Compare commits

...

37 Commits

Author SHA1 Message Date
手瓜一十雪
3de5438139 fix: poke report 2024-12-04 22:16:59 +08:00
手瓜一十雪
c4b5f34271 fix: 兜底 防止进入影响速度 2024-12-04 22:02:11 +08:00
手瓜一十雪
22d3ac33a2 Refactor: 更新群组通知处理逻辑,优化数据结构和异步处理 2024-12-04 21:59:53 +08:00
Mlikiowa
2e5dd6535a release: v4.2.22 2024-12-04 13:18:58 +00:00
手瓜一十雪
eac58a2a50 fix: 9.9.17-30366 2024-12-04 21:04:24 +08:00
手瓜一十雪
e939ec0e52 fix: #597 2024-12-04 20:37:08 +08:00
Mlikiowa
5b17a14a2a release: v4.2.21 2024-12-04 11:49:26 +00:00
手瓜一十雪
8fb8c888f5 refactor: 移除未使用的uidCache和uinCache逻辑 2024-12-04 19:48:59 +08:00
Mlikiowa
4a2884509e release: v4.2.20 2024-12-04 11:46:12 +00:00
手瓜一十雪
e295235a89 fix: #596 2024-12-04 19:45:46 +08:00
手瓜一十雪
ef515a38d0 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-12-04 18:50:55 +08:00
手瓜一十雪
02cff040e3 refactor: 移除未使用的createUidFromTinyId和getStatusByUid方法 2024-12-04 18:50:51 +08:00
Mlikiowa
bb0f65a52d release: v4.2.19 2024-12-04 10:42:32 +00:00
手瓜一十雪
d51d6a5cc1 refactor: getUidByUinV2/getUinByUidV2 2024-12-04 18:41:28 +08:00
手瓜一十雪
eb99379a79 fix: 性能优化 2024-12-04 18:29:33 +08:00
Mlikiowa
388eb57d0d release: v4.2.18 2024-12-04 03:40:59 +00:00
手瓜一十雪
0b8131392a chore: 移出调试 2024-12-04 11:40:36 +08:00
手瓜一十雪
229efbd006 Merge branch 'main' of https://github.com/NapNeko/NapCatQQ 2024-12-04 11:39:09 +08:00
手瓜一十雪
a482fa3a8d refactor: 优化调整文件处理 2024-12-04 11:38:59 +08:00
Mlikiowa
6cf047af39 release: v4.2.17 2024-12-04 02:57:21 +00:00
手瓜一十雪
41748c0b3f refactor: 数据清理与刷新 2024-12-04 10:55:56 +08:00
手瓜一十雪
1ce8be3c7e fix: 精简GroupApi实现 2024-12-04 10:47:21 +08:00
手瓜一十雪
32778acf57 refactor: NTQQGroupApi 2024-12-04 10:43:48 +08:00
Mlikiowa
a3c71473ae release: v4.2.16 2024-12-03 14:15:16 +00:00
手瓜一十雪
aceece7e90 fix: code 2024-12-03 22:12:57 +08:00
Mlikiowa
52efb4f9ef release: v4.2.15 2024-12-03 14:07:35 +00:00
手瓜一十雪
6b0d96fe8d fix: 移除复杂信息 2024-12-03 22:07:11 +08:00
Mlikiowa
ad052821b0 release: v4.2.14 2024-12-03 13:50:37 +00:00
手瓜一十雪
da7636e60c fix: #592 2024-12-03 21:50:12 +08:00
Mlikiowa
ef01dd0d77 release: v4.2.13 2024-12-03 13:43:37 +00:00
手瓜一十雪
03f7d4673f fix: code 2024-12-03 21:42:08 +08:00
手瓜一十雪
94e9c87978 fix: 优化处理 2024-12-03 21:42:08 +08:00
手瓜一十雪
501bbbe4df fix 2024-12-03 21:42:08 +08:00
手瓜一十雪
c9122a3fee fix: 临时的抽象方案 2024-12-03 21:42:08 +08:00
手瓜一十雪
8a289d014e fix: error 2024-12-03 21:42:08 +08:00
手瓜一十雪
ddadd38151 refactor: GroupAdminChange 2024-12-03 21:42:08 +08:00
手瓜一十雪
0b8d0e3cac feat: 迁移事件解析原理 2024-12-03 21:42:08 +08:00
33 changed files with 465 additions and 804 deletions

View File

@@ -4,7 +4,7 @@
"name": "NapCatQQ", "name": "NapCatQQ",
"slug": "NapCat.Framework", "slug": "NapCat.Framework",
"description": "高性能的 OneBot 11 协议实现", "description": "高性能的 OneBot 11 协议实现",
"version": "4.2.12", "version": "4.2.22",
"icon": "./logo.png", "icon": "./logo.png",
"authors": [ "authors": [
{ {

View File

@@ -2,7 +2,7 @@
"name": "napcat", "name": "napcat",
"private": true, "private": true,
"type": "module", "type": "module",
"version": "4.2.12", "version": "4.2.22",
"scripts": { "scripts": {
"build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:universal": "npm run build:webui && vite build --mode universal || exit 1",
"build:framework": "npm run build:webui && vite build --mode framework || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1",

View File

@@ -1,9 +1,7 @@
import fs from 'fs'; import fs from 'fs';
import { stat } from 'fs/promises'; import { stat } from 'fs/promises';
import crypto, { randomUUID } from 'crypto'; import crypto, { randomUUID } from 'crypto';
import util from 'util';
import path from 'node:path'; import path from 'node:path';
import * as fileType from 'file-type';
import { solveProblem } from '@/common/helper'; import { solveProblem } from '@/common/helper';
export interface HttpDownloadOptions { export interface HttpDownloadOptions {
@@ -15,7 +13,6 @@ type Uri2LocalRes = {
success: boolean, success: boolean,
errMsg: string, errMsg: string,
fileName: string, fileName: string,
ext: string,
path: string path: string
} }
@@ -73,27 +70,6 @@ async function checkFile(path: string): Promise<void> {
// 如果文件存在则无需做任何事情Promise 解决resolve自身 // 如果文件存在则无需做任何事情Promise 解决resolve自身
} }
export async function file2base64(path: string) {
const readFile = util.promisify(fs.readFile);
const result = {
err: '',
data: '',
};
try {
try {
await checkFileExist(path, 5000);
} catch (e: any) {
result.err = e.toString();
return result;
}
const data = await readFile(path);
result.data = data.toString('base64');
} catch (err: any) {
result.err = err.toString();
}
return result;
}
export function calculateFileMD5(filePath: string): Promise<string> { export function calculateFileMD5(filePath: string): Promise<string> {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// 创建一个流式读取器 // 创建一个流式读取器
@@ -160,20 +136,6 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi
return Buffer.from(buffer); return Buffer.from(buffer);
} }
export async function checkFileV2(filePath: string) {
try {
const ext: string | undefined = (await fileType.fileTypeFromFile(filePath))?.ext;
if (ext) {
fs.renameSync(filePath, filePath + `.${ext}`);
filePath += `.${ext}`;
return { success: true, ext: ext, path: filePath };
}
} catch (e) {
// log("获取文件类型失败", filePath,e.stack)
}
return { success: false, ext: '', path: filePath };
}
export enum FileUriType { export enum FileUriType {
Unknown = 0, Unknown = 0,
Local = 1, Local = 1,
@@ -213,63 +175,32 @@ export async function checkUriType(Uri: string) {
return { Uri: Uri, Type: FileUriType.Unknown }; return { Uri: Uri, Type: FileUriType.Unknown };
} }
export async function uri2local(dir: string, uri: string, filename: string | undefined = undefined): Promise<Uri2LocalRes> { export async function uriToLocalFile(dir: string, uri: string): Promise<Uri2LocalRes> {
const { Uri: HandledUri, Type: UriType } = await checkUriType(uri); const { Uri: HandledUri, Type: UriType } = await checkUriType(uri);
//解析失败 const filename = randomUUID();
const tempName = randomUUID(); const filePath = path.join(dir, filename);
if (!filename) filename = randomUUID();
//解析Http和Https协议 switch (UriType) {
if (UriType == FileUriType.Unknown) { case FileUriType.Local:
return { success: false, errMsg: `未知文件类型, uri= ${uri}`, fileName: '', ext: '', path: '' };
}
//解析File协议和本地文件
if (UriType == FileUriType.Local) {
const fileExt = path.extname(HandledUri); const fileExt = path.extname(HandledUri);
let filename = path.basename(HandledUri, fileExt); const localFileName = path.basename(HandledUri, fileExt) + fileExt;
filename += fileExt; const tempFilePath = path.join(dir, filename + fileExt);
//复制文件到临时文件并保持后缀 fs.copyFileSync(HandledUri, tempFilePath);
const filenameTemp = tempName + fileExt; return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath };
const filePath = path.join(dir, filenameTemp);
fs.copyFileSync(HandledUri, filePath);
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
}
//接下来都要有文件名 case FileUriType.Remote:
if (UriType == FileUriType.Remote) {
const pathInfo = path.parse(decodeURIComponent(new URL(HandledUri).pathname));
if (pathInfo.name) {
const pathlen = 200 - dir.length - pathInfo.name.length;
filename = pathlen > 0 ? pathInfo.name.substring(0, pathlen) : pathInfo.name.substring(pathInfo.name.length, pathInfo.name.length - 10);//过长截断
if (pathInfo.ext) {
filename += pathInfo.ext;
}
}
filename = filename.replace(/[/\\:*?"<>|]/g, '_');
const fileExt = path.extname(HandledUri).replace(/[/\\:*?"<>|]/g, '_').substring(0, 10);
const filePath = path.join(dir, tempName + fileExt);
const buffer = await httpDownload(HandledUri); const buffer = await httpDownload(HandledUri);
//没有文件就创建
fs.writeFileSync(filePath, buffer, { flag: 'wx' }); fs.writeFileSync(filePath, buffer, { flag: 'wx' });
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; return { success: true, errMsg: '', fileName: filename, path: filePath };
}
//解析Base64 case FileUriType.Base64:
if (UriType == FileUriType.Base64) {
const base64 = HandledUri.replace(/^base64:\/\//, ''); const base64 = HandledUri.replace(/^base64:\/\//, '');
const buffer = Buffer.from(base64, 'base64'); const base64Buffer = Buffer.from(base64, 'base64');
let filePath = path.join(dir, filename); fs.writeFileSync(filePath, base64Buffer, { flag: 'wx' });
let fileExt = ''; return { success: true, errMsg: '', fileName: filename, path: filePath };
fs.writeFileSync(filePath, buffer);
const { success, ext, path: fileTypePath } = await checkFileV2(filePath); default:
if (success) { return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' };
filePath = fileTypePath;
fileExt = ext;
filename = filename + '.' + ext;
} }
return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath };
}
return { success: false, errMsg: `未知文件类型, uri= ${uri}`, fileName: '', ext: '', path: '' };
} }

View File

@@ -1 +1 @@
export const napCatVersion = '4.2.12'; export const napCatVersion = '4.2.22';

View File

@@ -6,7 +6,6 @@ import {
Peer, Peer,
PicElement, PicElement,
PicSubType, PicSubType,
PicType,
RawMessage, RawMessage,
SendFileElement, SendFileElement,
SendPicElement, SendPicElement,
@@ -17,7 +16,7 @@ import path from 'path';
import fs from 'fs'; import fs from 'fs';
import fsPromises from 'fs/promises'; import fsPromises from 'fs/promises';
import { InstanceContext, NapCatCore, SearchResultItem } from '@/core'; import { InstanceContext, NapCatCore, SearchResultItem } from '@/core';
import * as fileType from 'file-type'; import { fileTypeFromFile } from 'file-type';
import imageSize from 'image-size'; import imageSize from 'image-size';
import { ISizeCalculationResult } from 'image-size/dist/types/interface'; import { ISizeCalculationResult } from 'image-size/dist/types/interface';
import { RkeyManager } from '@/core/helper/rkey'; import { RkeyManager } from '@/core/helper/rkey';
@@ -62,7 +61,7 @@ export class NTQQFileApi {
async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) { async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) {
const fileMd5 = await calculateFileMD5(filePath); const fileMd5 = await calculateFileMD5(filePath);
const extOrEmpty = (await fileType.fileTypeFromFile(filePath))?.ext; let extOrEmpty = await fileTypeFromFile(filePath).then(e => e?.ext ?? '').catch(e => '');
const ext = extOrEmpty ? `.${extOrEmpty}` : ''; const ext = extOrEmpty ? `.${extOrEmpty}` : '';
let fileName = `${path.basename(filePath)}`; let fileName = `${path.basename(filePath)}`;
if (fileName.indexOf('.') === -1) { if (fileName.indexOf('.') === -1) {
@@ -158,7 +157,7 @@ export class NTQQFileApi {
let fileExt = 'mp4'; let fileExt = 'mp4';
try { try {
const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext; const tempExt = (await fileTypeFromFile(filePath))?.ext;
if (tempExt) fileExt = tempExt; if (tempExt) fileExt = tempExt;
} catch (e) { } catch (e) {
this.context.logger.logError('获取文件类型失败', e); this.context.logger.logError('获取文件类型失败', e);

View File

@@ -1,6 +1,5 @@
import { import {
GeneralCallResult, GeneralCallResult,
Group,
GroupMember, GroupMember,
NTGroupMemberRole, NTGroupMemberRole,
NTGroupRequestOperateTypes, NTGroupRequestOperateTypes,
@@ -16,34 +15,22 @@ import { NTEventWrapper } from '@/common/event';
export class NTQQGroupApi { export class NTQQGroupApi {
context: InstanceContext; context: InstanceContext;
core: NapCatCore; core: NapCatCore;
groupCache: Map<string, Group> = new Map<string, Group>();
groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>(); groupMemberCache: Map<string, Map<string, GroupMember>> = new Map<string, Map<string, GroupMember>>();
groups: Group[] = [];
essenceLRU = new LimitedHashTable<number, string>(1000); essenceLRU = new LimitedHashTable<number, string>(1000);
session: any;
constructor(context: InstanceContext, core: NapCatCore) { constructor(context: InstanceContext, core: NapCatCore) {
this.context = context; this.context = context;
this.core = core; this.core = core;
} }
async initApi() { async initApi() {
this.initCache().then().catch(e => this.context.logger.logError(e)); this.initCache().then().catch(e => this.context.logger.logError(e));
} }
async initCache() {
this.groups = await this.getGroups();
for (const group of this.groups) {
this.groupCache.set(group.groupCode, group);
}
this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`);
// process.pid 调试点
}
async getCoreAndBaseInfo(uids: string[]) { async initCache() {
return await this.core.eventWrapper.callNoListenerEvent( for (const group of await this.getGroups(true)) {
'NodeIKernelProfileService/getCoreAndBaseInfo', this.refreshGroupMemberCache(group.groupCode).then().catch();
'nodeStore', }
uids,
);
} }
async fetchGroupEssenceList(groupCode: string) { async fetchGroupEssenceList(groupCode: string) {
@@ -54,20 +41,22 @@ export class NTQQGroupApi {
pageLimit: 300, pageLimit: 300,
}, pskey); }, pskey);
} }
async getGroupShutUpMemberList(groupCode: string) { async getGroupShutUpMemberList(groupCode: string) {
const data = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onShutUpMemberListChanged', (group_id) => group_id === groupCode, 1, 1000); const data = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onShutUpMemberListChanged', (group_id) => group_id === groupCode, 1, 1000);
this.context.session.getGroupService().getGroupShutUpMemberList(groupCode); this.context.session.getGroupService().getGroupShutUpMemberList(groupCode);
return (await data)[1]; return (await data)[1];
} }
async clearGroupNotifiesUnreadCount(uk: boolean) {
return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(uk); async clearGroupNotifiesUnreadCount(doubt: boolean) {
return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(doubt);
} }
async setGroupAvatar(gc: string, filePath: string) { async setGroupAvatar(groupCode: string, filePath: string) {
return this.context.session.getGroupService().setHeader(gc, filePath); return this.context.session.getGroupService().setHeader(groupCode, filePath);
} }
async getGroups(forced = false) { async getGroups(forced: boolean = false) {
const [, , groupList] = await this.core.eventWrapper.callNormalEventV2( const [, , groupList] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getGroupList', 'NodeIKernelGroupService/getGroupList',
'NodeIKernelGroupListener/onGroupListUpdate', 'NodeIKernelGroupListener/onGroupListUpdate',
@@ -76,9 +65,9 @@ export class NTQQGroupApi {
return groupList; return groupList;
} }
async getGroupExtFE0Info(groupCode: string[], forced = true) { async getGroupExtFE0Info(groupCodes: Array<string>, forced = true) {
return this.context.session.getGroupService().getGroupExt0xEF0Info( return this.context.session.getGroupService().getGroupExt0xEF0Info(
groupCode, groupCodes,
[], [],
{ {
bindGuildId: 1, bindGuildId: 1,
@@ -118,53 +107,42 @@ export class NTQQGroupApi {
); );
} }
async getGroup(groupCode: string, forced = false) {
let group = this.groupCache.get(groupCode.toString());
if (!group) {
try {
const groupList = await this.getGroups(forced);
if (groupList.length) {
groupList.forEach(g => {
this.groupCache.set(g.groupCode, g);
});
}
} catch (e) {
return undefined;
}
}
group = this.groupCache.get(groupCode.toString());
return group;
}
async getGroupMemberAll(groupCode: string, forced = false) { async getGroupMemberAll(groupCode: string, forced = false) {
return this.context.session.getGroupService().getAllMemberList(groupCode, forced); return this.context.session.getGroupService().getAllMemberList(groupCode, forced);
} }
async refreshGroupMemberCache(groupCode: string) {
try {
const members = await this.getGroupMemberAll(groupCode, true);
this.groupMemberCache.set(groupCode, members.result.infos);
} catch (e) {
this.context.logger.logError(`刷新群成员缓存失败, 群号: ${groupCode}, 错误: ${e}`);
}
return this.groupMemberCache;
}
async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) { async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) {
const groupCodeStr = groupCode.toString(); const groupCodeStr = groupCode.toString();
const memberUinOrUidStr = memberUinOrUid.toString(); const memberUinOrUidStr = memberUinOrUid.toString();
// 获取群成员缓存
let members = this.groupMemberCache.get(groupCodeStr); let members = this.groupMemberCache.get(groupCodeStr);
if (!members) { if (!members) {
try { members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr);
members = await this.getGroupMembers(groupCodeStr);
this.groupMemberCache.set(groupCodeStr, members);
} catch (e) {
return null;
}
}
function getMember() {
let member: GroupMember | undefined;
if (isNumeric(memberUinOrUidStr)) {
member = Array.from(members!.values()).find(member => member.uin === memberUinOrUidStr);
} else {
member = members!.get(memberUinOrUidStr);
}
return member;
} }
const getMember = () => {
if (isNumeric(memberUinOrUidStr)) {
return Array.from(members!.values()).find(member => member.uin === memberUinOrUidStr);
} else {
return members!.get(memberUinOrUidStr);
}
};
let member = getMember(); let member = getMember();
// 如果缓存中不存在该成员,尝试刷新缓存
if (!member) { if (!member) {
members = await this.getGroupMembers(groupCodeStr); members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr);
member = getMember(); member = getMember();
} }
return member; return member;
@@ -174,26 +152,26 @@ export class NTQQGroupApi {
return this.context.session.getGroupService().getGroupRecommendContactArkJson(groupCode); return this.context.session.getGroupService().getGroupRecommendContactArkJson(groupCode);
} }
async CreatGroupFileFolder(groupCode: string, folderName: string) { async creatGroupFileFolder(groupCode: string, folderName: string) {
return this.context.session.getRichMediaService().createGroupFolder(groupCode, folderName); return this.context.session.getRichMediaService().createGroupFolder(groupCode, folderName);
} }
async DelGroupFile(groupCode: string, files: string[]) { async delGroupFile(groupCode: string, files: Array<string>) {
return this.context.session.getRichMediaService().deleteGroupFile(groupCode, [102], files); return this.context.session.getRichMediaService().deleteGroupFile(groupCode, [102], files);
} }
async DelGroupFileFolder(groupCode: string, folderId: string) { async delGroupFileFolder(groupCode: string, folderId: string) {
return this.context.session.getRichMediaService().deleteGroupFolder(groupCode, folderId); return this.context.session.getRichMediaService().deleteGroupFolder(groupCode, folderId);
} }
async addGroupEssence(GroupCode: string, msgId: string) { async addGroupEssence(groupCode: string, msgId: string) {
const MsgData = await this.context.session.getMsgService().getMsgsIncludeSelf({ const MsgData = await this.context.session.getMsgService().getMsgsIncludeSelf({
chatType: 2, chatType: 2,
guildId: '', guildId: '',
peerUid: GroupCode, peerUid: groupCode,
}, msgId, 1, false); }, msgId, 1, false);
const param = { const param = {
groupCode: GroupCode, groupCode: groupCode,
msgRandom: parseInt(MsgData.msgList[0].msgRandom), msgRandom: parseInt(MsgData.msgList[0].msgRandom),
msgSeq: parseInt(MsgData.msgList[0].msgSeq), msgSeq: parseInt(MsgData.msgList[0].msgSeq),
}; };
@@ -204,9 +182,9 @@ export class NTQQGroupApi {
return this.context.session.getGroupService().kickMemberV2(param); return this.context.session.getGroupService().kickMemberV2(param);
} }
async deleteGroupBulletin(GroupCode: string, noticeId: string) { async deleteGroupBulletin(groupCode: string, noticeId: string) {
const psKey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!; const psKey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
return this.context.session.getGroupService().deleteGroupBulletin(GroupCode, psKey, noticeId); return this.context.session.getGroupService().deleteGroupBulletin(groupCode, psKey, noticeId);
} }
async quitGroupV2(GroupCode: string, needDeleteLocalMsg: boolean) { async quitGroupV2(GroupCode: string, needDeleteLocalMsg: boolean) {
@@ -217,65 +195,42 @@ export class NTQQGroupApi {
return this.context.session.getGroupService().quitGroupV2(param); return this.context.session.getGroupService().quitGroupV2(param);
} }
async removeGroupEssenceBySeq(GroupCode: string, msgRandom: string, msgSeq: string) { async removeGroupEssenceBySeq(groupCode: string, msgRandom: string, msgSeq: string) {
const param = { const param = {
groupCode: GroupCode, groupCode: groupCode,
msgRandom: parseInt(msgRandom), msgRandom: parseInt(msgRandom),
msgSeq: parseInt(msgSeq), msgSeq: parseInt(msgSeq),
}; };
return this.context.session.getGroupService().removeGroupEssence(param); return this.context.session.getGroupService().removeGroupEssence(param);
} }
async removeGroupEssence(GroupCode: string, msgId: string) { async removeGroupEssence(groupCode: string, msgId: string) {
const MsgData = await this.context.session.getMsgService().getMsgsIncludeSelf({ const MsgData = await this.context.session.getMsgService().getMsgsIncludeSelf({
chatType: 2, chatType: 2,
guildId: '', guildId: '',
peerUid: GroupCode, peerUid: groupCode,
}, msgId, 1, false); }, msgId, 1, false);
const param = { const param = {
groupCode: GroupCode, groupCode: groupCode,
msgRandom: parseInt(MsgData.msgList[0].msgRandom), msgRandom: parseInt(MsgData.msgList[0].msgRandom),
msgSeq: parseInt(MsgData.msgList[0].msgSeq), msgSeq: parseInt(MsgData.msgList[0].msgSeq),
}; };
return this.context.session.getGroupService().removeGroupEssence(param); return this.context.session.getGroupService().removeGroupEssence(param);
} }
async getSingleScreenNotifies(doubt: boolean, num: number) { async getSingleScreenNotifies(doubt: boolean, count: number) {
const [, , , notifies] = await this.core.eventWrapper.callNormalEventV2( const [, , , notifies] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getSingleScreenNotifies', 'NodeIKernelGroupService/getSingleScreenNotifies',
'NodeIKernelGroupListener/onGroupSingleScreenNotifies', 'NodeIKernelGroupListener/onGroupSingleScreenNotifies',
[ [
doubt, doubt,
'', '',
num, count,
], ],
); );
return notifies; return notifies;
} }
async getGroupMemberV2(GroupCode: string, uid: string, forced = false) {
const Listener = this.core.eventWrapper.registerListen(
'NodeIKernelGroupListener/onMemberInfoChange',
(params, _, members) => params === GroupCode && members.size > 0,
1,
forced ? 5000 : 250,
);
const retData = await (
this.core.eventWrapper
.createEventFunction('NodeIKernelGroupService/getMemberInfo')
)!(GroupCode, [uid], forced);
if (retData.result !== 0) {
throw new Error(`${retData.errMsg}`);
}
const result = await Listener as unknown;
let member: GroupMember | undefined;
if (Array.isArray(result) && result?.[2] instanceof Map) {
const members = result[2] as Map<string, GroupMember>;
member = members.get(uid);
}
return member;
}
async searchGroup(groupCode: string) { async searchGroup(groupCode: string) {
const [, ret] = await this.core.eventWrapper.callNormalEventV2( const [, ret] = await this.core.eventWrapper.callNormalEventV2(
'NodeIKernelSearchService/searchGroup', 'NodeIKernelSearchService/searchGroup',
@@ -294,127 +249,43 @@ export class NTQQGroupApi {
return ret.groupInfos.find(g => g.groupCode === groupCode); return ret.groupInfos.find(g => g.groupCode === groupCode);
} }
async getGroupMemberEx(GroupCode: string, uid: string, forced = false, retry = 2) { async getGroupMemberEx(groupCode: string, uid: string, forced: boolean = false, retry: number = 2) {
const data = await solveAsyncProblem((eventWrapper: NTEventWrapper, GroupCode: string, uid: string, forced = false) => { const data = await solveAsyncProblem((eventWrapper: NTEventWrapper, GroupCode: string, uid: string, forced = false) => {
return eventWrapper.callNormalEventV2( return eventWrapper.callNormalEventV2(
'NodeIKernelGroupService/getMemberInfo', 'NodeIKernelGroupService/getMemberInfo',
'NodeIKernelGroupListener/onMemberInfoChange', 'NodeIKernelGroupListener/onMemberInfoChange',
[GroupCode, [uid], forced], [groupCode, [uid], forced],
(ret) => ret.result === 0, (ret) => ret.result === 0,
(params, _, members) => params === GroupCode && members.size > 0 && members.has(uid), (params, _, members) => params === GroupCode && members.size > 0 && members.has(uid),
1, 1,
forced ? 2500 : 250 forced ? 2500 : 250
); );
}, this.core.eventWrapper, GroupCode, uid, forced); }, this.core.eventWrapper, groupCode, uid, forced);
if (data && data[3] instanceof Map && data[3].has(uid)) { if (data && data[3] instanceof Map && data[3].has(uid)) {
return data[3].get(uid); return data[3].get(uid);
} }
if (retry > 0) { if (retry > 0) {
const trydata = await this.getGroupMemberEx(GroupCode, uid, true, retry - 1) as GroupMember | undefined; const trydata = await this.getGroupMemberEx(groupCode, uid, true, retry - 1) as GroupMember | undefined;
if (trydata) return trydata; if (trydata) return trydata;
} }
return undefined; return undefined;
} }
async tryGetGroupMembersV2(groupQQ: string, modeListener = false, num = 30, timeout = 100): Promise<{ async getGroupFileCount(groupCodes: Array<string>) {
infos: Map<string, GroupMember>; return this.context.session.getRichMediaService().batchGetGroupFileCount(groupCodes);
finish: boolean;
hasNext: boolean | undefined;
}> {
const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow_1');
const once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', (params) => params.sceneId === sceneId, 0, timeout)
.catch(() => { });
const result = await this.context.session.getGroupService().getNextMemberList(sceneId, undefined, num);
if (result.errCode !== 0) {
throw new Error('获取群成员列表出错,' + result.errMsg);
}
let resMode2;
if (modeListener) {
const ret = (await once)?.[0];
if (ret) {
resMode2 = ret;
}
}
this.context.session.getGroupService().destroyMemberListScene(sceneId);
return {
infos: new Map([...(resMode2?.infos ?? []), ...result.result.infos]),
finish: result.result.finish,
hasNext: resMode2?.hasNext,
};
} }
async GetGroupMembersV3(groupQQ: string, num = 3000, timeout = 2500): Promise<{ async getArkJsonGroupShare(groupCode: string) {
infos: Map<string, GroupMember>;
finish: boolean;
hasNext: boolean | undefined;
listenerMode: boolean;
}> {
const sceneId = this.context.session.getGroupService().createMemberListScene(groupQQ, 'groupMemberList_MainWindow_1');
const once = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onMemberListChange', (params) => params.sceneId === sceneId, 0, timeout)
.catch(() => { });
const result = await this.context.session.getGroupService().getNextMemberList(sceneId, undefined, num);
if (result.errCode !== 0) {
throw new Error('获取群成员列表出错,' + result.errMsg);
}
let resMode2;
if (result.result.finish && result.result.infos.size === 0) {
const ret = (await once)?.[0];
if (ret) {
resMode2 = ret;
}
}
this.context.session.getGroupService().destroyMemberListScene(sceneId);
return {
infos: new Map([...(resMode2?.infos ?? []), ...result.result.infos]),
finish: result.result.finish,
hasNext: resMode2?.hasNext,
listenerMode: resMode2?.hasNext !== undefined
};
}
async getGroupMembersV2(groupQQ: string, num = 3000, no_cache: boolean = false): Promise<Map<string, GroupMember>> {
if (no_cache) {
return (await this.getGroupMemberAll(groupQQ, true)).result.infos;
}
let res = await this.GetGroupMembersV3(groupQQ, num);
let ret = res.infos;
if (res.infos.size === 0 && !res.listenerMode) {
res = await this.GetGroupMembersV3(groupQQ, num);
ret = res.infos;
}
if (res.infos.size === 0) {
ret = (await this.getGroupMemberAll(groupQQ)).result.infos;
}
return ret;
}
async getGroupMembers(groupQQ: string, num = 3000): Promise<Map<string, GroupMember>> {
const groupService = this.context.session.getGroupService();
const sceneId = groupService.createMemberListScene(groupQQ, 'groupMemberList_MainWindow');
const result = await groupService.getNextMemberList(sceneId, undefined, num);
if (result.errCode !== 0) {
throw new Error('获取群成员列表出错,' + result.errMsg);
}
this.context.logger.logDebug(`获取群(${groupQQ})成员列表结果:`, `members: ${result.result.infos.size}`);
return result.result.infos;
}
async getGroupFileCount(group_ids: Array<string>) {
return this.context.session.getRichMediaService().batchGetGroupFileCount(group_ids);
}
async getArkJsonGroupShare(GroupCode: string) {
const ret = await this.core.eventWrapper.callNoListenerEvent( const ret = await this.core.eventWrapper.callNoListenerEvent(
'NodeIKernelGroupService/getGroupRecommendContactArkJson', 'NodeIKernelGroupService/getGroupRecommendContactArkJson',
GroupCode, groupCode,
) as GeneralCallResult & { arkJson: string }; ) as GeneralCallResult & { arkJson: string };
return ret.arkJson; return ret.arkJson;
} }
//需要异常处理 async uploadGroupBulletinPic(groupCode: string, imageurl: string) {
async uploadGroupBulletinPic(GroupCode: string, imageurl: string) {
const _Pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!; const _Pskey = (await this.core.apis.UserApi.getPSkey(['qun.qq.com'])).domainPskeyMap.get('qun.qq.com')!;
return this.context.session.getGroupService().uploadGroupBulletinPic(GroupCode, _Pskey, imageurl); return this.context.session.getGroupService().uploadGroupBulletinPic(groupCode, _Pskey, imageurl);
} }
async handleGroupRequest(flag: string, operateType: NTGroupRequestOperateTypes, reason?: string) { async handleGroupRequest(flag: string, operateType: NTGroupRequestOperateTypes, reason?: string) {
@@ -436,36 +307,36 @@ export class NTQQGroupApi {
}); });
} }
async quitGroup(groupQQ: string) { async quitGroup(groupCode: string) {
return this.context.session.getGroupService().quitGroup(groupQQ); return this.context.session.getGroupService().quitGroup(groupCode);
} }
async kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') { async kickMember(groupCode: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') {
return this.context.session.getGroupService().kickMember(groupQQ, kickUids, refuseForever, kickReason); return this.context.session.getGroupService().kickMember(groupCode, kickUids, refuseForever, kickReason);
} }
async banMember(groupQQ: string, memList: Array<{ uid: string, timeStamp: number }>) { async banMember(groupCode: string, memList: Array<{ uid: string, timeStamp: number }>) {
// timeStamp为秒数, 0为解除禁言 // timeStamp为秒数, 0为解除禁言
return this.context.session.getGroupService().setMemberShutUp(groupQQ, memList); return this.context.session.getGroupService().setMemberShutUp(groupCode, memList);
} }
async banGroup(groupQQ: string, shutUp: boolean) { async banGroup(groupCode: string, shutUp: boolean) {
return this.context.session.getGroupService().setGroupShutUp(groupQQ, shutUp); return this.context.session.getGroupService().setGroupShutUp(groupCode, shutUp);
} }
async setMemberCard(groupQQ: string, memberUid: string, cardName: string) { async setMemberCard(groupCode: string, memberUid: string, cardName: string) {
return this.context.session.getGroupService().modifyMemberCardName(groupQQ, memberUid, cardName); return this.context.session.getGroupService().modifyMemberCardName(groupCode, memberUid, cardName);
} }
async setMemberRole(groupQQ: string, memberUid: string, role: NTGroupMemberRole) { async setMemberRole(groupCode: string, memberUid: string, role: NTGroupMemberRole) {
return this.context.session.getGroupService().modifyMemberRole(groupQQ, memberUid, role); return this.context.session.getGroupService().modifyMemberRole(groupCode, memberUid, role);
} }
async setGroupName(groupQQ: string, groupName: string) { async setGroupName(groupCode: string, groupName: string) {
return this.context.session.getGroupService().modifyGroupName(groupQQ, groupName, false); return this.context.session.getGroupService().modifyGroupName(groupCode, groupName, false);
} }
async publishGroupBulletin(groupQQ: string, content: string, picInfo: { async publishGroupBulletin(groupCode: string, content: string, picInfo: {
id: string, id: string,
width: number, width: number,
height: number height: number
@@ -479,11 +350,11 @@ export class NTQQGroupApi {
pinned: pinned, pinned: pinned,
confirmRequired: confirmRequired, confirmRequired: confirmRequired,
}; };
return this.context.session.getGroupService().publishGroupBulletin(groupQQ, psKey!, data); return this.context.session.getGroupService().publishGroupBulletin(groupCode, psKey!, data);
} }
async getGroupRemainAtTimes(GroupCode: string) { async getGroupRemainAtTimes(groupCode: string) {
return this.context.session.getGroupService().getGroupRemainAtTimes(GroupCode); return this.context.session.getGroupService().getGroupRemainAtTimes(groupCode);
} }
async getMemberExtInfo(groupCode: string, uin: string) { async getMemberExtInfo(groupCode: string, uin: string) {

View File

@@ -2,6 +2,8 @@ import { ModifyProfileParams, User, UserDetailSource } from '@/core/types';
import { RequestUtil } from '@/common/request'; import { RequestUtil } from '@/common/request';
import { InstanceContext, NapCatCore, ProfileBizType } from '..'; import { InstanceContext, NapCatCore, ProfileBizType } from '..';
import { solveAsyncProblem } from '@/common/helper'; import { solveAsyncProblem } from '@/common/helper';
import { promisify } from 'node:util';
import { LRUCache } from '@/common/lru-cache';
export class NTQQUserApi { export class NTQQUserApi {
context: InstanceContext; context: InstanceContext;
@@ -11,13 +13,15 @@ export class NTQQUserApi {
this.context = context; this.context = context;
this.core = core; this.core = core;
} }
//self_tind格式
async createUidFromTinyId(tinyId: string) { async getCoreAndBaseInfo(uids: string[]) {
return this.context.session.getMsgService().createUidFromTinyId(this.core.selfInfo.uin, tinyId); return await this.core.eventWrapper.callNoListenerEvent(
} 'NodeIKernelProfileService/getCoreAndBaseInfo',
async getStatusByUid(uid: string) { 'nodeStore',
return this.context.session.getProfileService().getStatus(uid); uids,
);
} }
// 默认获取自己的 type = 2 获取别人 type = 1 // 默认获取自己的 type = 2 获取别人 type = 1
async getProfileLike(uid: string, start: number, count: number, type: number = 2) { async getProfileLike(uid: string, start: number, count: number, type: number = 2) {
return this.context.session.getProfileLikeService().getBuddyProfileLike({ return this.context.session.getProfileLikeService().getBuddyProfileLike({
@@ -161,35 +165,51 @@ export class NTQQUserApi {
if (!skey) { if (!skey) {
throw new Error('SKey is Empty'); throw new Error('SKey is Empty');
} }
return skey; return skey;
} }
//后期改成流水线处理
async getUidByUinV2(Uin: string) { async getUidByUinV2(Uin: string) {
let uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin); if (!Uin) {
if (uid) return uid; return '';
uid = (await this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin); }
if (uid) return uid; const services = [
uid = (await this.context.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin); () => this.context.session.getUixConvertService().getUid([Uin]).then((data) => data.uidInfo.get(Uin)).catch(() => undefined),
if (uid) return uid; () => promisify<string, string[], Map<string, string>>
const unverifiedUid = (await this.getUserDetailInfoByUin(Uin)).detail.uid;//从QQ Native 特殊转换 (this.context.session.getProfileService().getUidByUin)('FriendsServiceImpl', [Uin]).then((data) => data.get(Uin)).catch(() => undefined),
if (unverifiedUid.indexOf('*') == -1) uid = unverifiedUid; () => this.context.session.getGroupService().getUidByUins([Uin]).then((data) => data.uids.get(Uin)).catch(() => undefined),
//if (uid) return uid; () => this.getUserDetailInfoByUin(Uin).then((data) => data.detail.uid).catch(() => undefined),
return uid; ];
let uid: string | undefined = undefined;
for (const service of services) {
uid = await service();
if (uid && uid.indexOf('*') == -1 && uid !== '') {
break;
}
}
return uid ?? '';
} }
//后期改成流水线处理
async getUinByUidV2(Uid: string) { async getUinByUidV2(Uid: string) {
let uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid); if (!Uid) {
if (uin && uin !== '0') return uin; return '0';
uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid); }
if (uin && uin !== '0') return uin; const services = [
uin = (await this.context.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid); () => this.context.session.getUixConvertService().getUin([Uid]).then((data) => data.uinInfo.get(Uid)).catch(() => undefined),
if (uin && uin !== '0') return uin; () => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined),
uin = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getKey(Uid); () => promisify<string, string[], Map<string, string>>
if (uin && uin !== '0') return uin; (this.context.session.getProfileService().getUinByUid)('FriendsServiceImpl', [Uid]).then((data) => data.get(Uid)).catch(() => undefined),
uin = (await this.getUserDetailInfo(Uid)).uin; //从QQ Native 转换 () => this.core.apis.FriendApi.getBuddyIdMap(true).then((data) => data.getKey(Uid)).catch(() => undefined),
return uin; () => this.getUserDetailInfo(Uid).then((data) => data.uin).catch(() => undefined),
];
let uin: string | undefined = undefined;
for (const service of services) {
uin = await service();
if (uin && uin !== '0' && uin !== '') {
break;
}
}
return uin ?? '0';
} }
async getRecentContactListSnapShot(count: number) { async getRecentContactListSnapShot(count: number) {

View File

@@ -98,5 +98,9 @@
"6.9.61-29927": { "6.9.61-29927": {
"appid": 537255836, "appid": 537255836,
"qua": "V1_MAC_NQ_6.9.61_29927_GW_B" "qua": "V1_MAC_NQ_6.9.61_29927_GW_B"
},
"9.9.17-30366": {
"appid": 537258389,
"qua": "V1_WIN_NQ_9.9.17_30366_GW_B"
} }
} }

View File

@@ -102,5 +102,9 @@
"6.9.61-29927-arm64": { "6.9.61-29927-arm64": {
"send": "4038740", "send": "4038740",
"recv": "403AF58" "recv": "403AF58"
},
"9.9.17-30366-x64": {
"send": "39AB0B0",
"recv": "39AF4E4"
} }
} }

View File

@@ -1,7 +1,7 @@
import * as fileType from 'file-type'; import { fileTypeFromFile } from 'file-type';
import { PicType } from '../types'; import { PicType } from '../types';
export async function getFileTypeForSendType(picPath: string): Promise<PicType> { export async function getFileTypeForSendType(picPath: string): Promise<PicType> {
const fileTypeResult = (await fileType.fileTypeFromFile(picPath))?.ext ?? 'jpg'; const fileTypeResult = (await fileTypeFromFile(picPath))?.ext ?? 'jpg';
const picTypeMap: { [key: string]: PicType } = { const picTypeMap: { [key: string]: PicType } = {
//'webp': PicType.NEWPIC_WEBP, //'webp': PicType.NEWPIC_WEBP,
'gif': PicType.NEWPIC_GIF, 'gif': PicType.NEWPIC_GIF,

View File

@@ -24,10 +24,10 @@ import path from 'node:path';
import fs from 'node:fs'; import fs from 'node:fs';
import { hostname, systemName, systemVersion } from '@/common/system'; import { hostname, systemName, systemVersion } from '@/common/system';
import { NTEventWrapper } from '@/common/event'; import { NTEventWrapper } from '@/common/event';
import { DataSource, GroupMember, KickedOffLineInfo, SelfInfo, SelfStatusInfo } from '@/core/types'; import { GroupMember, KickedOffLineInfo, SelfInfo, SelfStatusInfo } from '@/core/types';
import { NapCatConfigLoader } from '@/core/helper/config'; import { NapCatConfigLoader } from '@/core/helper/config';
import os from 'node:os'; import os from 'node:os';
import { NodeIKernelGroupListener, NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners'; import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners';
import { proxiedListenerOf } from '@/common/proxy-handler'; import { proxiedListenerOf } from '@/common/proxy-handler';
import { NTQQPacketApi } from './apis/packet'; import { NTQQPacketApi } from './apis/packet';
export * from './wrapper'; export * from './wrapper';
@@ -163,7 +163,6 @@ export class NapCatCore {
msgListener.onAddSendMsg = (msg) => { msgListener.onAddSendMsg = (msg) => {
this.context.logger.logMessage(msg, this.selfInfo); this.context.logger.logMessage(msg, this.selfInfo);
}; };
//await sleep(2500);
this.context.session.getMsgService().addKernelMsgListener( this.context.session.getMsgService().addKernelMsgListener(
proxiedListenerOf(msgListener, this.context.logger), proxiedListenerOf(msgListener, this.context.logger),
); );
@@ -185,92 +184,6 @@ export class NapCatCore {
this.context.session.getProfileService().addKernelProfileListener( this.context.session.getProfileService().addKernelProfileListener(
proxiedListenerOf(profileListener, this.context.logger), proxiedListenerOf(profileListener, this.context.logger),
); );
// 群相关
const groupListener = new NodeIKernelGroupListener();
groupListener.onGroupListUpdate = (updateType, groupList) => {
// console.log("onGroupListUpdate", updateType, groupList)
groupList.map(g => {
const existGroup = this.apis.GroupApi.groupCache.get(g.groupCode);
//群成员数量变化 应该刷新缓存
if (existGroup && g.memberCount === existGroup.memberCount) {
Object.assign(existGroup, g);
} else {
this.apis.GroupApi.groupCache.set(g.groupCode, g);
// 获取群成员
}
const sceneId = this.context.session.getGroupService().createMemberListScene(g.groupCode, 'groupMemberList_MainWindow');
this.context.session.getGroupService().getNextMemberList(sceneId, undefined, 3000).then( /* r => {
// console.log(`get group ${g.groupCode} members`, r);
// r.result.infos.forEach(member => {
// });
// groupMembers.set(g.groupCode, r.result.infos);
} */);
this.context.session.getGroupService().destroyMemberListScene(sceneId);
});
};
groupListener.onMemberListChange = (arg) => {
// TODO: 应该加一个内部自己维护的成员变动callback用于判断成员变化通知
const groupCode = arg.sceneId.split('_')[0];
if (this.apis.GroupApi.groupMemberCache.has(groupCode)) {
const existMembers = this.apis.GroupApi.groupMemberCache.get(groupCode)!;
arg.infos.forEach((member, uid) => {
//console.log('onMemberListChange', member);
const existMember = existMembers.get(uid);
if (existMember) {
Object.assign(existMember, member);
} else {
existMembers.set(uid, member);
}
//移除成员
if (member.isDelete) {
existMembers.delete(uid);
}
});
} else {
this.apis.GroupApi.groupMemberCache.set(groupCode, arg.infos);
}
};
groupListener.onMemberInfoChange = (groupCode, dataSource, members) => {
if (dataSource === DataSource.LOCAL && members.get(this.selfInfo.uid)?.isDelete) {
// 自身退群或者被踢退群 5s用于Api操作 之后不再出现
setTimeout(() => {
this.apis.GroupApi.groupCache.delete(groupCode);
}, 5000);
}
const existMembers = this.apis.GroupApi.groupMemberCache.get(groupCode);
if (existMembers) {
members.forEach((member, uid) => {
const existMember = existMembers.get(uid);
if (existMember) {
// 检查管理变动
member.isChangeRole = this.checkAdminEvent(groupCode, member, existMember);
// 更新成员信息
Object.assign(existMember, member);
} else {
existMembers.set(uid, member);
}
//移除成员
if (member.isDelete) {
existMembers.delete(uid);
}
});
} else {
this.apis.GroupApi.groupMemberCache.set(groupCode, members);
}
};
this.context.session.getGroupService().addKernelGroupListener(
proxiedListenerOf(groupListener, this.context.logger),
);
}
checkAdminEvent(groupCode: string, memberNew: GroupMember, memberOld: GroupMember | undefined): boolean {
if (memberNew.role !== memberOld?.role) {
this.context.logger.logDebug(`${groupCode} ${memberNew.nick} 角色变更为 ${memberNew.role === 3 ? '管理员' : '群员'}`);
return true;
}
return false;
} }
} }

View File

@@ -0,0 +1,18 @@
import { ProtoField, ScalarType } from "@napneko/nap-proto-core";
export const GroupAdminExtra = {
adminUid: ProtoField(1, ScalarType.STRING),
isPromote: ProtoField(2, ScalarType.BOOL),
}
export const GroupAdminBody = {
extraDisable: ProtoField(1, () => GroupAdminExtra),
extraEnable: ProtoField(2, () => GroupAdminExtra),
}
export const GroupAdmin = {
groupUin: ProtoField(1, ScalarType.UINT32),
flag: ProtoField(2, ScalarType.UINT32),
isPromote: ProtoField(3, ScalarType.BOOL),
body: ProtoField(4, () => GroupAdminBody),
}

View File

@@ -187,13 +187,13 @@ export interface NodeIKernelGroupService {
destroyGroup(groupCode: string): void; destroyGroup(groupCode: string): void;
getSingleScreenNotifies(doubted: boolean, start_seq: string, num: number): Promise<GeneralCallResult>; getSingleScreenNotifies(doubt: boolean, startSeq: string, count: number): Promise<GeneralCallResult>;
clearGroupNotifies(groupCode: string): void; clearGroupNotifies(groupCode: string): void;
getGroupNotifiesUnreadCount(unknown: boolean): Promise<GeneralCallResult>; getGroupNotifiesUnreadCount(doubt: boolean): Promise<GeneralCallResult>;
clearGroupNotifiesUnreadCount(unknown: boolean): void; clearGroupNotifiesUnreadCount(doubt: boolean): void;
operateSysNotify( operateSysNotify(
doubt: boolean, doubt: boolean,

View File

@@ -9,9 +9,9 @@ export interface NodeIKernelProfileService {
getRelationFlag(callfrom: string, uids: string[]): Promise<Map<string, any>>; getRelationFlag(callfrom: string, uids: string[]): Promise<Map<string, any>>;
getUidByUin(callfrom: string, uin: Array<string>): Promise<Map<string, string>>; getUidByUin(callfrom: string, uin: Array<string>): Map<string, string>;
getUinByUid(callfrom: string, uid: Array<string>): Promise<Map<string, string>>; getUinByUid(callfrom: string, uid: Array<string>): Map<string, string>;
getCoreAndBaseInfo(callfrom: string, uids: string[]): Promise<Map<string, SimpleInfo>>; getCoreAndBaseInfo(callfrom: string, uids: string[]): Promise<Map<string, SimpleInfo>>;

View File

@@ -29,6 +29,7 @@ export interface TextElement {
} }
export interface FaceElement { export interface FaceElement {
pokeType?: number;
faceIndex: number; faceIndex: number;
faceType: FaceType; faceType: FaceType;
faceText?: string; faceText?: string;

View File

@@ -2,31 +2,43 @@ import { GroupNotifyMsgStatus } from '@/core';
import { OneBotAction } from '@/onebot/action/OneBotAction'; import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
interface OB11GroupRequestNotify { interface Notify {
group_id: number, request_id: string;
user_id: number, invitor_uin: number;
flag: string invitor_nick?: string;
group_id?: number;
group_name?: string;
checked: boolean;
requester_nick?: string;
actor: number;
} }
export default class GetGroupAddRequest extends OneBotAction<null, OB11GroupRequestNotify[] | null> { export default class GetGroupAddRequest extends OneBotAction<null, Notify[] | null> {
actionName = ActionName.GetGroupIgnoreAddRequest; actionName = ActionName.GetGroupIgnoreAddRequest;
async _handle(payload: null): Promise<OB11GroupRequestNotify[] | null> { async _handle(payload: null): Promise<Notify[] | null> {
const ignoredNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(true, 10); const NTQQUserApi = this.core.apis.UserApi;
const retData: any = { const NTQQGroupApi = this.core.apis.GroupApi;
join_requests: await Promise.all( const ignoredNotifies = await NTQQGroupApi.getSingleScreenNotifies(true, 10);
ignoredNotifies const retData: Notify[] = [];
const notifyPromises = ignoredNotifies
.filter(notify => notify.type === 7) .filter(notify => notify.type === 7)
.map(async SSNotify => ({ .map(async SSNotify => {
request_id: SSNotify.seq, const invitorUin = SSNotify.user1?.uid ? +await NTQQUserApi.getUinByUidV2(SSNotify.user1.uid) : 0;
requester_uin: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1?.uid), const actorUin = SSNotify.user2?.uid ? +await NTQQUserApi.getUinByUidV2(SSNotify.user2.uid) : 0;
retData.push({
request_id: `${SSNotify.group.groupCode}|${SSNotify.seq}|${SSNotify.type}`,
invitor_uin: invitorUin,
requester_nick: SSNotify.user1?.nickName, requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode, group_id: +SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName, group_name: SSNotify.group?.groupName,
checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE,
actor: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, actor: actorUin,
}))), });
}; });
await Promise.all(notifyPromises);
return retData; return retData;
} }

View File

@@ -1,6 +1,6 @@
import { OneBotAction } from '@/onebot/action/OneBotAction'; import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { checkFileExist, uri2local } from '@/common/file'; import { checkFileExist, uriToLocalFile } from '@/common/file';
import fs from 'fs'; import fs from 'fs';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
@@ -15,7 +15,7 @@ export class OCRImage extends OneBotAction<Payload, any> {
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.image)); const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.image));
if (!success) { if (!success) {
throw new Error(`OCR ${payload.image}失败,image字段可能格式不正确`); throw new Error(`OCR ${payload.image}失败,image字段可能格式不正确`);
} }

View File

@@ -1,7 +1,7 @@
import { OneBotAction } from '@/onebot/action/OneBotAction'; import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
import { checkFileExist, uri2local } from '@/common/file'; import { checkFileExist, uriToLocalFile } from '@/common/file';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
@@ -14,7 +14,7 @@ export default class SetAvatar extends OneBotAction<Payload, null> {
actionName = ActionName.SetQQAvatar; actionName = ActionName.SetQQAvatar;
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<null> { async _handle(payload: Payload): Promise<null> {
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file)); const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file));
if (!success) { if (!success) {
throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`); throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
} }

View File

@@ -13,6 +13,6 @@ export class CreateGroupFileFolder extends OneBotAction<Payload, any> {
actionName = ActionName.GoCQHTTP_CreateGroupFileFolder; actionName = ActionName.GoCQHTTP_CreateGroupFileFolder;
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
return (await this.core.apis.GroupApi.CreatGroupFileFolder(payload.group_id.toString(), payload.folder_name)).resultWithGroupItem; return (await this.core.apis.GroupApi.creatGroupFileFolder(payload.group_id.toString(), payload.folder_name)).resultWithGroupItem;
} }
} }

View File

@@ -17,6 +17,6 @@ export class DeleteGroupFile extends OneBotAction<Payload, any> {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const data = FileNapCatOneBotUUID.decodeModelId(payload.file_id); const data = FileNapCatOneBotUUID.decodeModelId(payload.file_id);
if (!data) throw new Error('Invalid file_id'); if (!data) throw new Error('Invalid file_id');
return await this.core.apis.GroupApi.DelGroupFile(payload.group_id.toString(), [data.fileId]); return await this.core.apis.GroupApi.delGroupFile(payload.group_id.toString(), [data.fileId]);
} }
} }

View File

@@ -14,7 +14,7 @@ export class DeleteGroupFileFolder extends OneBotAction<Payload, any> {
actionName = ActionName.GoCQHTTP_DeleteGroupFileFolder; actionName = ActionName.GoCQHTTP_DeleteGroupFileFolder;
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload) { async _handle(payload: Payload) {
return (await this.core.apis.GroupApi.DelGroupFileFolder( return (await this.core.apis.GroupApi.delGroupFileFolder(
payload.group_id.toString(), payload.folder ?? payload.folder_id ?? '')).groupFileCommonResult; payload.group_id.toString(), payload.folder ?? payload.folder_id ?? '')).groupFileCommonResult;
} }
} }

View File

@@ -1,4 +1,4 @@
import { checkFileExist, uri2local } from '@/common/file'; import { checkFileExist, uriToLocalFile } from '@/common/file';
import { OneBotAction } from '@/onebot/action/OneBotAction'; import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { unlink } from 'node:fs/promises'; import { unlink } from 'node:fs/promises';
@@ -28,7 +28,7 @@ export class SendGroupNotice extends OneBotAction<Payload, null> {
const { const {
path, path,
success, success,
} = (await uri2local(this.core.NapCatTempPath, payload.image)); } = (await uriToLocalFile(this.core.NapCatTempPath, payload.image));
if (!success) { if (!success) {
throw new Error(`群公告${payload.image}设置失败,image字段可能格式不正确`); throw new Error(`群公告${payload.image}设置失败,image字段可能格式不正确`);
} }

View File

@@ -1,6 +1,6 @@
import { OneBotAction } from '@/onebot/action/OneBotAction'; import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { checkFileExistV2, uri2local } from '@/common/file'; import { checkFileExistV2, uriToLocalFile } from '@/common/file';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
import fs from 'node:fs/promises'; import fs from 'node:fs/promises';
const SchemaData = Type.Object({ const SchemaData = Type.Object({
@@ -15,7 +15,7 @@ export default class SetGroupPortrait extends OneBotAction<Payload, any> {
payloadSchema = SchemaData; payloadSchema = SchemaData;
async _handle(payload: Payload): Promise<any> { async _handle(payload: Payload): Promise<any> {
const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file)); const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file));
if (!success) { if (!success) {
throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`); throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
} }

View File

@@ -2,7 +2,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { ChatType, Peer } from '@/core/types'; import { ChatType, Peer } from '@/core/types';
import fs from 'fs'; import fs from 'fs';
import { uri2local } from '@/common/file'; import { uriToLocalFile } from '@/common/file';
import { SendMessageContext } from '@/onebot/api'; import { SendMessageContext } from '@/onebot/api';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
@@ -25,7 +25,7 @@ export default class GoCQHTTPUploadGroupFile extends OneBotAction<Payload, null>
if (fs.existsSync(file)) { if (fs.existsSync(file)) {
file = `file://${file}`; file = `file://${file}`;
} }
const downloadResult = await uri2local(this.core.NapCatTempPath, file); const downloadResult = await uriToLocalFile(this.core.NapCatTempPath, file);
const peer: Peer = { const peer: Peer = {
chatType: ChatType.KCHATTYPEGROUP, chatType: ChatType.KCHATTYPEGROUP,
peerUid: payload.group_id.toString(), peerUid: payload.group_id.toString(),

View File

@@ -2,7 +2,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { ChatType, Peer, SendFileElement } from '@/core/types'; import { ChatType, Peer, SendFileElement } from '@/core/types';
import fs from 'fs'; import fs from 'fs';
import { uri2local } from '@/common/file'; import { uriToLocalFile } from '@/common/file';
import { SendMessageContext } from '@/onebot/api'; import { SendMessageContext } from '@/onebot/api';
import { ContextMode, createContext } from '@/onebot/action/msg/SendMsg'; import { ContextMode, createContext } from '@/onebot/action/msg/SendMsg';
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
@@ -36,7 +36,7 @@ export default class GoCQHTTPUploadPrivateFile extends OneBotAction<Payload, nul
if (fs.existsSync(file)) { if (fs.existsSync(file)) {
file = `file://${file}`; file = `file://${file}`;
} }
const downloadResult = await uri2local(this.core.NapCatTempPath, file); const downloadResult = await uriToLocalFile(this.core.NapCatTempPath, file);
if (!downloadResult.success) { if (!downloadResult.success) {
throw new Error(downloadResult.errMsg); throw new Error(downloadResult.errMsg);
} }

View File

@@ -1,26 +1,55 @@
import { GroupNotifyMsgStatus } from '@/core'; import { GroupNotifyMsgStatus } from '@/core';
import { OneBotAction } from '@/onebot/action/OneBotAction'; import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
export class GetGroupIgnoredNotifies extends OneBotAction<void, any> {
actionName = ActionName.GetGroupIgnoredNotifies;
async _handle(payload: void) { interface Notify {
const ignoredNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(true, 10); request_id: string;
const retData: any = { invitor_uin: number;
join_requests: await Promise.all( invitor_nick?: string;
ignoredNotifies group_id?: number;
.filter(notify => notify.type === 7) group_name?: string;
.map(async SSNotify => ({ checked: boolean;
request_id: SSNotify.seq, requester_nick?: string;
requester_uin: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1?.uid), actor: number;
requester_nick: SSNotify.user1?.nickName, }
group_id: SSNotify.group?.groupCode, interface RetData {
InvitedRequest: Notify[];
join_requests: Notify[];
}
export class GetGroupIgnoredNotifies extends OneBotAction<void, RetData> {
actionName = ActionName.GetGroupSystemMsg;
async _handle(): Promise<RetData> {
const NTQQUserApi = this.core.apis.UserApi;
const NTQQGroupApi = this.core.apis.GroupApi;
const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false, 50);
const retData: RetData = { InvitedRequest: [], join_requests: [] };
const notifyPromises = SingleScreenNotifies.map(async (SSNotify) => {
const invitorUin = SSNotify.user1?.uid ? +await NTQQUserApi.getUinByUidV2(SSNotify.user1.uid) : 0;
const actorUin = SSNotify.user2?.uid ? +await NTQQUserApi.getUinByUidV2(SSNotify.user2.uid) : 0;
const commonData = {
request_id: `${SSNotify.group.groupCode}|${SSNotify.seq}|${SSNotify.type}`,
invitor_uin: invitorUin,
invitor_nick: SSNotify.user1?.nickName,
group_id: +SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName, group_name: SSNotify.group?.groupName,
checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE,
actor: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, actor: actorUin,
}))), requester_nick: SSNotify.user1?.nickName,
}; };
if (SSNotify.type === 1) {
retData.InvitedRequest.push(commonData);
} else if (SSNotify.type === 7) {
retData.join_requests.push(commonData);
}
});
await Promise.all(notifyPromises);
return retData; return retData;
} }
} }

View File

@@ -19,11 +19,14 @@ export class GetGroupMemberList extends OneBotAction<Payload, OB11GroupMember[]>
const groupIdStr = payload.group_id.toString(); const groupIdStr = payload.group_id.toString();
const noCache = payload.no_cache ? this.stringToBoolean(payload.no_cache) : false; const noCache = payload.no_cache ? this.stringToBoolean(payload.no_cache) : false;
const memberCache = this.core.apis.GroupApi.groupMemberCache; const memberCache = this.core.apis.GroupApi.groupMemberCache;
let groupMembers; let groupMembers = memberCache.get(groupIdStr);
try { if (noCache || !groupMembers) {
groupMembers = await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr, 3000, noCache); this.core.apis.GroupApi.refreshGroupMemberCache(groupIdStr).then().catch();
} catch (error) { //下次刷新
groupMembers = memberCache.get(groupIdStr) ?? await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr); groupMembers = memberCache.get(groupIdStr);
if (!groupMembers) {
throw new Error(`Failed to get group member list for group ${groupIdStr}`);
}
} }
const memberPromises = Array.from(groupMembers.values()).map(item => const memberPromises = Array.from(groupMembers.values()).map(item =>
OB11Construct.groupMember(groupIdStr, item) OB11Construct.groupMember(groupIdStr, item)

View File

@@ -1,6 +1,6 @@
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus"; import { GetPacketStatusDepends } from "@/onebot/action/packet/GetPacketStatus";
import { uri2local } from "@/common/file"; import { uriToLocalFile } from "@/common/file";
import { ChatType, Peer } from "@/core"; import { ChatType, Peer } from "@/core";
import { AIVoiceChatType } from "@/core/packet/entities/aiChat"; import { AIVoiceChatType } from "@/core/packet/entities/aiChat";
import { Static, Type } from '@sinclair/typebox'; import { Static, Type } from '@sinclair/typebox';
@@ -23,7 +23,7 @@ export class SendGroupAiRecord extends GetPacketStatusDepends<Payload, {
async _handle(payload: Payload) { async _handle(payload: Payload) {
const rawRsp = await this.core.apis.PacketApi.pkt.operation.GetAiVoice(+payload.group_id, payload.character, payload.text, AIVoiceChatType.Sound); const rawRsp = await this.core.apis.PacketApi.pkt.operation.GetAiVoice(+payload.group_id, payload.character, payload.text, AIVoiceChatType.Sound);
const url = await this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+payload.group_id, rawRsp.msgInfoBody[0].index); const url = await this.core.apis.PacketApi.pkt.operation.GetGroupPttUrl(+payload.group_id, rawRsp.msgInfoBody[0].index);
const { path, errMsg, success } = (await uri2local(this.core.NapCatTempPath, url)); const { path, errMsg, success } = (await uriToLocalFile(this.core.NapCatTempPath, url));
if (!success) { if (!success) {
throw new Error(errMsg); throw new Error(errMsg);
} }

View File

@@ -1,38 +1,63 @@
import { GroupNotifyMsgStatus } from '@/core'; import { GroupNotifyMsgStatus } from '@/core';
import { OneBotAction } from '@/onebot/action/OneBotAction'; import { OneBotAction } from '@/onebot/action/OneBotAction';
import { ActionName } from '@/onebot/action/router'; import { ActionName } from '@/onebot/action/router';
export class GetGroupSystemMsg extends OneBotAction<void, any> {
interface Notify {
request_id: string;
invitor_uin: number;
invitor_nick?: string;
group_id?: number;
group_name?: string;
checked: boolean;
actor: number;
}
interface JoinRequest extends Notify {
requester_nick?: string;
}
interface RetData {
InvitedRequest: Notify[];
join_requests: JoinRequest[];
}
export class GetGroupSystemMsg extends OneBotAction<void, RetData> {
actionName = ActionName.GetGroupSystemMsg; actionName = ActionName.GetGroupSystemMsg;
async _handle() { async _handle(): Promise<RetData> {
const NTQQUserApi = this.core.apis.UserApi; const NTQQUserApi = this.core.apis.UserApi;
const NTQQGroupApi = this.core.apis.GroupApi; const NTQQGroupApi = this.core.apis.GroupApi;
// 默认10条 该api未完整实现 包括响应数据规范化 类型规范化 const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false, 50);
const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false,10); const retData: RetData = { InvitedRequest: [], join_requests: [] };
const retData: any = { InvitedRequest: [], join_requests: [] };
for (const SSNotify of SingleScreenNotifies) { const notifyPromises = SingleScreenNotifies.map(async (SSNotify) => {
if (SSNotify.type == 1) { const invitorUin = SSNotify.user1?.uid ? +await NTQQUserApi.getUinByUidV2(SSNotify.user1.uid) : 0;
const actorUin = SSNotify.user2?.uid ? +await NTQQUserApi.getUinByUidV2(SSNotify.user2.uid) : 0;
if (SSNotify.type === 1) {
retData.InvitedRequest.push({ retData.InvitedRequest.push({
request_id: SSNotify.seq, request_id: `${SSNotify.group.groupCode}|${SSNotify.seq}|${SSNotify.type}`,
invitor_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid), invitor_uin: invitorUin,
invitor_nick: SSNotify.user1?.nickName, invitor_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode, group_id: +SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName, group_name: SSNotify.group?.groupName,
checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true, checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, actor: actorUin,
}); });
} else if (SSNotify.type == 7) { } else if (SSNotify.type === 7) {
retData.join_requests.push({ retData.join_requests.push({
request_id: SSNotify.seq, request_id: `${SSNotify.group.groupCode}|${SSNotify.seq}|${SSNotify.type}`,
requester_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid), invitor_uin: invitorUin,
requester_nick: SSNotify.user1?.nickName, requester_nick: SSNotify.user1?.nickName,
group_id: SSNotify.group?.groupCode, group_id: +SSNotify.group?.groupCode,
group_name: SSNotify.group?.groupName, group_name: SSNotify.group?.groupName,
checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true, checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE,
actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, actor: actorUin,
}); });
} }
} });
await Promise.all(notifyPromises);
return retData; return retData;
} }

View File

@@ -7,15 +7,10 @@ import {
MessageElement, MessageElement,
NapCatCore, NapCatCore,
NTGrayTipElementSubTypeV2, NTGrayTipElementSubTypeV2,
NTMsgType,
RawMessage, RawMessage,
TipGroupElement,
TipGroupElementType,
} from '@/core'; } from '@/core';
import { NapCatOneBot11Adapter } from '@/onebot'; import { NapCatOneBot11Adapter } from '@/onebot';
import { OB11GroupBanEvent } from '@/onebot/event/notice/OB11GroupBanEvent'; import { OB11GroupBanEvent } from '@/onebot/event/notice/OB11GroupBanEvent';
import { OB11GroupIncreaseEvent } from '@/onebot/event/notice/OB11GroupIncreaseEvent';
import { OB11GroupDecreaseEvent } from '@/onebot/event/notice/OB11GroupDecreaseEvent';
import fastXmlParser from 'fast-xml-parser'; import fastXmlParser from 'fast-xml-parser';
import { OB11GroupMsgEmojiLikeEvent } from '@/onebot/event/notice/OB11MsgEmojiLikeEvent'; import { OB11GroupMsgEmojiLikeEvent } from '@/onebot/event/notice/OB11MsgEmojiLikeEvent';
import { MessageUnique } from '@/common/message-unique'; import { MessageUnique } from '@/common/message-unique';
@@ -66,67 +61,6 @@ export class OneBotGroupApi {
return undefined; return undefined;
} }
// async parseGroupIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
// this.core.context.logger.logDebug('收到新人被邀请进群消息', grayTipElement);
// const xmlElement = grayTipElement.xmlElement;
// if (xmlElement?.content) {
// const regex = /jp="(\d+)"/g;
// const matches = [];
// let match = null;
// while ((match = regex.exec(xmlElement.content)) !== null) {
// matches.push(match[1]);
// }
// if (matches.length === 2) {
// const [inviter, invitee] = matches;
// return new OB11GroupIncreaseEvent(
// this.core,
// parseInt(GroupCode),
// parseInt(invitee),
// parseInt(inviter),
// 'invite',
// );
// }
// }
// return undefined;
// }
// async parseGroupMemberIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) {
// const groupElement = grayTipElement?.groupElement;
// if (!groupElement) return undefined;
// const member = await this.core.apis.UserApi.getUserDetailInfo(groupElement.memberUid);
// const memberUin = member?.uin;
// const adminMember = await this.core.apis.GroupApi.getGroupMember(GroupCode, groupElement.adminUid);
// if (memberUin) {
// const operatorUin = adminMember?.uin ?? memberUin;
// return new OB11GroupIncreaseEvent(
// this.core,
// parseInt(GroupCode),
// parseInt(memberUin),
// parseInt(operatorUin),
// );
// } else {
// return undefined;
// }
// }
// async parseGroupKickEvent(GroupCode: string, grayTipElement: GrayTipElement) {
// const groupElement = grayTipElement?.groupElement;
// if (!groupElement) return undefined;
// const adminUin = (await this.core.apis.GroupApi.getGroupMember(GroupCode, groupElement.adminUid))?.uin ?? (await this.core.apis.UserApi.getUidByUinV2(groupElement.adminUid));
// if (adminUin) {
// return new OB11GroupDecreaseEvent(
// this.core,
// parseInt(GroupCode),
// parseInt(this.core.selfInfo.uin),
// parseInt(adminUin),
// 'kick_me',
// );
// }
// return undefined;
// }
async parseGroupEmojiLikeEventByGrayTip( async parseGroupEmojiLikeEventByGrayTip(
groupCode: string, groupCode: string,
grayTipElement: GrayTipElement grayTipElement: GrayTipElement
@@ -187,31 +121,6 @@ export class OneBotGroupApi {
return undefined; return undefined;
} }
// async parseGroupElement(msg: RawMessage, groupElement: TipGroupElement, elementWrapper: GrayTipElement) {
// if (groupElement.type == TipGroupElementType.KMEMBERADD) {
// const MemberIncreaseEvent = await this.obContext.apis.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, elementWrapper);
// if (MemberIncreaseEvent) return MemberIncreaseEvent;
// } else if (groupElement.type === TipGroupElementType.KSHUTUP) {
// const BanEvent = await this.obContext.apis.GroupApi.parseGroupBanEvent(msg.peerUid, elementWrapper);
// if (BanEvent) return BanEvent;
// } else if (groupElement.type == TipGroupElementType.KQUITTE) {
// this.core.apis.GroupApi.quitGroup(msg.peerUid).then();
// try {
// const KickEvent = await this.obContext.apis.GroupApi.parseGroupKickEvent(msg.peerUid, elementWrapper);
// if (KickEvent) return KickEvent;
// } catch (e) {
// return new OB11GroupDecreaseEvent(
// this.core,
// parseInt(msg.peerUid),
// parseInt(this.core.selfInfo.uin),
// 0,
// 'leave',
// );
// }
// }
// return undefined;
// }
async parsePaiYiPai(msg: RawMessage, jsonStr: string) { async parsePaiYiPai(msg: RawMessage, jsonStr: string) {
const json = JSON.parse(jsonStr); const json = JSON.parse(jsonStr);

View File

@@ -23,7 +23,7 @@ import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataTyp
import { OB11Construct } from '@/onebot/helper/data'; import { OB11Construct } from '@/onebot/helper/data';
import { EventType } from '@/onebot/event/OneBotEvent'; import { EventType } from '@/onebot/event/OneBotEvent';
import { encodeCQCode } from '@/onebot/helper/cqcode'; import { encodeCQCode } from '@/onebot/helper/cqcode';
import { uri2local } from '@/common/file'; import { uriToLocalFile } from '@/common/file';
import { RequestUtil } from '@/common/request'; import { RequestUtil } from '@/common/request';
import fsPromise, { constants } from 'node:fs/promises'; import fsPromise, { constants } from 'node:fs/promises';
import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent'; import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent';
@@ -32,6 +32,8 @@ import { GroupChange, PushMsgBody } from "@/core/packet/transformer/proto";
import { NapProtoMsg } from '@napneko/nap-proto-core'; import { NapProtoMsg } from '@napneko/nap-proto-core';
import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent'; import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent';
import { OB11GroupDecreaseEvent, GroupDecreaseSubType } from '../event/notice/OB11GroupDecreaseEvent'; import { OB11GroupDecreaseEvent, GroupDecreaseSubType } from '../event/notice/OB11GroupDecreaseEvent';
import { GroupAdmin } from '@/core/packet/transformer/proto/message/groupAdmin';
import { OB11GroupAdminNoticeEvent } from '../event/notice/OB11GroupAdminNoticeEvent';
type RawToOb11Converters = { type RawToOb11Converters = {
[Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: ( [Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: (
@@ -151,6 +153,17 @@ export class OneBotMsgApi {
faceElement: async element => { faceElement: async element => {
const faceIndex = element.faceIndex; const faceIndex = element.faceIndex;
if (element.faceType == FaceType.Poke) {
return {
type: OB11MessageDataType.poke,
data: {
type: element?.pokeType?.toString() ?? '0',
id: faceIndex.toString(),
}
}
}
if (faceIndex === FaceIndex.DICE) { if (faceIndex === FaceIndex.DICE) {
return { return {
type: OB11MessageDataType.dice, type: OB11MessageDataType.dice,
@@ -512,7 +525,7 @@ export class OneBotMsgApi {
let thumb = sendMsg.data.thumb; let thumb = sendMsg.data.thumb;
if (thumb) { if (thumb) {
const uri2LocalRes = await uri2local(this.core.NapCatTempPath, thumb); const uri2LocalRes = await uriToLocalFile(this.core.NapCatTempPath, thumb);
if (uri2LocalRes.success) thumb = uri2LocalRes.path; if (uri2LocalRes.success) thumb = uri2LocalRes.path;
} }
return await this.core.apis.FileApi.createValidSendVideoElement(context, path, fileName, thumb); return await this.core.apis.FileApi.createValidSendVideoElement(context, path, fileName, thumb);
@@ -930,7 +943,7 @@ export class OneBotMsgApi {
{ data: inputdata }: OB11MessageFileBase, { data: inputdata }: OB11MessageFileBase,
{ deleteAfterSentFiles }: SendMessageContext, { deleteAfterSentFiles }: SendMessageContext,
) { ) {
const realUri = inputdata.url || inputdata.file || inputdata.path || ''; const realUri = inputdata.url ?? inputdata.file ?? inputdata.path ?? '';
if (realUri.length === 0) { if (realUri.length === 0) {
this.core.context.logger.logError('文件消息缺少参数', inputdata); this.core.context.logger.logError('文件消息缺少参数', inputdata);
throw Error('文件消息缺少参数'); throw Error('文件消息缺少参数');
@@ -940,7 +953,7 @@ export class OneBotMsgApi {
fileName, fileName,
errMsg, errMsg,
success, success,
} = (await uri2local(this.core.NapCatTempPath, realUri)); } = (await uriToLocalFile(this.core.NapCatTempPath, realUri));
if (!success) { if (!success) {
this.core.context.logger.logError('文件下载失败', errMsg); this.core.context.logger.logError('文件下载失败', errMsg);
@@ -965,11 +978,10 @@ export class OneBotMsgApi {
} }
async parseSysMessage(msg: number[]) { async parseSysMessage(msg: number[]) {
// Todo Refactor
const SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg)); const SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg));
if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) { if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) {
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
console.log(JSON.stringify(groupChange)); this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch();
return new OB11GroupIncreaseEvent( return new OB11GroupIncreaseEvent(
this.core, this.core,
groupChange.groupUin, groupChange.groupUin,
@@ -979,6 +991,14 @@ export class OneBotMsgApi {
); );
} else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) { } else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) {
const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent);
if (groupChange.memberUid === this.core.selfInfo.uid) {
setTimeout(() => {
this.core.apis.GroupApi.groupMemberCache.delete(groupChange.groupUin.toString());
}, 5000);
// 自己被踢了 5S后回收
} else {
this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch();
}
return new OB11GroupDecreaseEvent( return new OB11GroupDecreaseEvent(
this.core, this.core,
groupChange.groupUin, groupChange.groupUin,
@@ -986,31 +1006,26 @@ export class OneBotMsgApi {
groupChange.operatorUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.operatorUid) : 0, groupChange.operatorUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.operatorUid) : 0,
this.groupChangDecreseType2String(groupChange.decreaseType), this.groupChangDecreseType2String(groupChange.decreaseType),
); );
} else if (SysMessage.contentHead.type == 44 && SysMessage.body?.msgContent) {
const groupAmin = new NapProtoMsg(GroupAdmin).decode(SysMessage.body.msgContent);
this.core.apis.GroupApi.refreshGroupMemberCache(groupAmin.groupUin.toString()).then().catch();
let enabled = false;
let uid = '';
if (groupAmin.body.extraEnable != null) {
uid = groupAmin.body.extraEnable.adminUid;
enabled = true;
} else if (groupAmin.body.extraDisable != null) {
uid = groupAmin.body.extraDisable.adminUid;
enabled = false;
}
return new OB11GroupAdminNoticeEvent(
this.core,
groupAmin.groupUin,
+await this.core.apis.UserApi.getUinByUidV2(uid),
enabled ? 'set' : 'unset'
);
} else if (SysMessage.contentHead.type == 528 && SysMessage.contentHead.subType == 39 && SysMessage.body?.msgContent) { } else if (SysMessage.contentHead.type == 528 && SysMessage.contentHead.subType == 39 && SysMessage.body?.msgContent) {
return await this.obContext.apis.UserApi.parseLikeEvent(SysMessage.body?.msgContent); return await this.obContext.apis.UserApi.parseLikeEvent(SysMessage.body?.msgContent);
} }
/*
if (msgType === 732 && subType === 16 && subSubType === 16) {
const greyTip = GreyTipWrapper.fromBinary(Uint8Array.from(sysMsg.bodyWrapper!.wrappedBody.slice(7)));
if (greyTip.subTypeId === 36) {
const emojiLikeToOthers = EmojiLikeToOthersWrapper1
.fromBinary(greyTip.rest)
.wrapper!
.body!;
if (emojiLikeToOthers.attributes?.operation !== 1) { // Un-like
return;
}
const eventOrEmpty = await this.apis.GroupApi.createGroupEmojiLikeEvent(
greyTip.groupCode.toString(),
await this.core.apis.UserApi.getUinByUidV2(emojiLikeToOthers.attributes!.senderUid),
emojiLikeToOthers.msgSpec!.msgSeq.toString(),
emojiLikeToOthers.attributes!.emojiId,
);
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
eventOrEmpty && await this.networkManager.emitEvent(eventOrEmpty);
}
}
*/
} }
} }

View File

@@ -1,8 +1,6 @@
import { import {
BuddyReqType, BuddyReqType,
ChatType, ChatType,
DataSource,
NTGroupMemberRole,
GroupNotifyMsgStatus, GroupNotifyMsgStatus,
GroupNotifyMsgType, GroupNotifyMsgType,
InstanceContext, InstanceContext,
@@ -41,8 +39,6 @@ import { OB11InputStatusEvent } from '@/onebot/event/notice/OB11InputStatusEvent
import { MessageUnique } from '@/common/message-unique'; import { MessageUnique } from '@/common/message-unique';
import { proxiedListenerOf } from '@/common/proxy-handler'; import { proxiedListenerOf } from '@/common/proxy-handler';
import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest'; import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest';
import { OB11GroupAdminNoticeEvent } from '@/onebot/event/notice/OB11GroupAdminNoticeEvent';
// import { GroupDecreaseSubType, OB11GroupDecreaseEvent } from '@/onebot/event/notice/OB11GroupDecreaseEvent';
import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest'; import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest';
import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent'; import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent';
import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent'; import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent';
@@ -355,7 +351,6 @@ export class NapCatOneBot11Adapter {
const groupListener = new NodeIKernelGroupListener(); const groupListener = new NodeIKernelGroupListener();
groupListener.onGroupNotifiesUpdated = async (_, notifies) => { groupListener.onGroupNotifiesUpdated = async (_, notifies) => {
//console.log('ob11 onGroupNotifiesUpdated', notifies[0]);
await this.core.apis.GroupApi.clearGroupNotifiesUnreadCount(false); await this.core.apis.GroupApi.clearGroupNotifiesUnreadCount(false);
if ( if (
![ ![
@@ -373,77 +368,6 @@ export class NapCatOneBot11Adapter {
const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type; const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type;
this.context.logger.logDebug('收到群通知', notify); this.context.logger.logDebug('收到群通知', notify);
if (
[
GroupNotifyMsgType.SET_ADMIN,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN,
].includes(notify.type)
) {
const member1 = await this.core.apis.GroupApi.getGroupMember(
notify.group.groupCode,
notify.user1.uid
);
this.context.logger.logDebug('有管理员变动通知');
// refreshGroupMembers(notify.group.groupCode).then();
this.context.logger.logDebug('开始获取变动的管理员');
if (member1) {
this.context.logger.logDebug('变动管理员获取成功');
const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent(
this.core,
parseInt(notify.group.groupCode),
parseInt(member1.uin),
[
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_CANCELED,
GroupNotifyMsgType.CANCEL_ADMIN_NOTIFY_ADMIN,
].includes(notify.type)
? 'unset'
: 'set'
);
this.networkManager
.emitEvent(groupAdminNoticeEvent)
.catch((e) =>
this.context.logger.logError('处理群管理员变动失败', e)
);
} else {
this.context.logger.logDebug(
'获取群通知的成员信息失败',
notify,
this.core.apis.GroupApi.getGroup(notify.group.groupCode)
);
}
} else
// if (
// notify.type == GroupNotifyMsgType.MEMBER_LEAVE_NOTIFY_ADMIN ||
// notify.type == GroupNotifyMsgType.KICK_MEMBER_NOTIFY_ADMIN
// ) {
// this.context.logger.logDebug('有成员退出通知', notify);
// const member1Uin = await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid);
// let operatorId = member1Uin;
// let subType: GroupDecreaseSubType = 'leave';
// if (notify.user2.uid) {
// // 是被踢的
// const member2Uin = await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid);
// if (member2Uin) {
// operatorId = member2Uin;
// }
// subType = 'kick';
// }
// const groupDecreaseEvent = new OB11GroupDecreaseEvent(
// this.core,
// parseInt(notify.group.groupCode),
// parseInt(member1Uin),
// parseInt(operatorId),
// subType
// );
// this.networkManager
// .emitEvent(groupDecreaseEvent)
// .catch((e) =>
// this.context.logger.logError('处理群成员退出失败', e)
// );
// // notify.status == 1 表示未处理 2表示处理完成
// } else
if ( if (
[GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS].includes(notify.type) && [GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS].includes(notify.type) &&
notify.status == GroupNotifyMsgStatus.KUNHANDLE notify.status == GroupNotifyMsgStatus.KUNHANDLE
@@ -481,8 +405,8 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug(`收到邀请我加群通知:${notify}`); this.context.logger.logDebug(`收到邀请我加群通知:${notify}`);
const groupInviteEvent = new OB11GroupRequestEvent( const groupInviteEvent = new OB11GroupRequestEvent(
this.core, this.core,
parseInt(notify.group.groupCode), +notify.group.groupCode,
parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid)), +await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid),
'invite', 'invite',
notify.postscript, notify.postscript,
flag flag
@@ -499,8 +423,8 @@ export class NapCatOneBot11Adapter {
this.context.logger.logDebug(`收到群员邀请加群通知:${notify}`); this.context.logger.logDebug(`收到群员邀请加群通知:${notify}`);
const groupInviteEvent = new OB11GroupRequestEvent( const groupInviteEvent = new OB11GroupRequestEvent(
this.core, this.core,
parseInt(notify.group.groupCode), +notify.group.groupCode,
parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid)), +await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid),
'add', 'add',
notify.postscript, notify.postscript,
flag flag
@@ -514,33 +438,6 @@ export class NapCatOneBot11Adapter {
} }
} }
}; };
groupListener.onMemberInfoChange = async (groupCode, dataSource, members) => {
//this.context.logger.logDebug('收到群成员信息变动通知', groupCode, changeType);
if (dataSource === DataSource.LOCAL) {
const existMembers = this.core.apis.GroupApi.groupMemberCache.get(groupCode);
if (!existMembers) return;
members.forEach((member) => {
const existMember = existMembers.get(member.uid);
if (!existMember?.isChangeRole) return;
this.context.logger.logDebug('变动管理员获取成功');
const groupAdminNoticeEvent = new OB11GroupAdminNoticeEvent(
this.core,
parseInt(groupCode),
parseInt(member.uin),
member.role === NTGroupMemberRole.KADMIN ? 'set' : 'unset'
);
this.networkManager
.emitEvent(groupAdminNoticeEvent)
.catch((e) =>
this.context.logger.logError('处理群管理员变动失败', e)
);
existMember.isChangeRole = false;
this.context.logger.logDebug('群管理员变动处理完毕');
});
}
};
this.context.session this.context.session
.getGroupService() .getGroupService()
.addKernelGroupListener(proxiedListenerOf(groupListener, this.context.logger)); .addKernelGroupListener(proxiedListenerOf(groupListener, this.context.logger));
@@ -674,6 +571,8 @@ export class NapCatOneBot11Adapter {
} }
private async emitFriendRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) { private async emitFriendRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) {
const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
if (!operatorUid) return undefined;
return new OB11FriendRecallNoticeEvent( return new OB11FriendRecallNoticeEvent(
this.core, this.core,
+message.senderUin, +message.senderUin,
@@ -684,7 +583,7 @@ export class NapCatOneBot11Adapter {
private async emitGroupRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) { private async emitGroupRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) {
const operatorUid = element.grayTipElement?.revokeElement.operatorUid; const operatorUid = element.grayTipElement?.revokeElement.operatorUid;
if (!operatorUid) return undefined; if (!operatorUid) return undefined;
const operatorId = message.senderUin ?? await this.core.apis.UserApi.getUinByUidV2(operatorUid); const operatorId = await this.core.apis.UserApi.getUinByUidV2(operatorUid);
return new OB11GroupRecallNoticeEvent( return new OB11GroupRecallNoticeEvent(
this.core, this.core,
+message.peerUin, +message.peerUin,

View File

@@ -71,6 +71,14 @@ export enum OB11MessageDataType {
location = 'location' location = 'location'
} }
export interface OB11MessagePoke {
type: OB11MessageDataType.poke;
data: {
type: string;
id: string;
};
}
// 商城表情消息接口定义 // 商城表情消息接口定义
export interface OB11MessageMFace { export interface OB11MessageMFace {
type: OB11MessageDataType.mface; type: OB11MessageDataType.mface;
@@ -247,7 +255,7 @@ export type OB11MessageData =
OB11MessageAt | OB11MessageReply | OB11MessageAt | OB11MessageReply |
OB11MessageImage | OB11MessageRecord | OB11MessageFile | OB11MessageVideo | OB11MessageImage | OB11MessageRecord | OB11MessageFile | OB11MessageVideo |
OB11MessageNode | OB11MessageIdMusic | OB11MessageCustomMusic | OB11MessageJson | OB11MessageNode | OB11MessageIdMusic | OB11MessageCustomMusic | OB11MessageJson |
OB11MessageDice | OB11MessageRPS | OB11MessageMarkdown | OB11MessageForward | OB11MessageContext; OB11MessageDice | OB11MessageRPS | OB11MessageMarkdown | OB11MessageForward | OB11MessageContext | OB11MessagePoke;
// 发送消息接口定义 // 发送消息接口定义
export interface OB11PostSendMsg { export interface OB11PostSendMsg {