From af052242fa74ca6acbb2f2048789bc2a0c6dce85 Mon Sep 17 00:00:00 2001 From: bietiaop <1527109126@qq.com> Date: Sun, 1 Dec 2024 15:16:16 +0800 Subject: [PATCH 01/57] =?UTF-8?q?feat:=E5=B8=A6=E7=AD=89=E7=BA=A7=E7=9A=84?= =?UTF-8?q?=E5=AE=9E=E6=97=B6=E6=97=A5=E5=BF=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/log.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/common/log.ts b/src/common/log.ts index 7b34f46a..0965f4ea 100644 --- a/src/common/log.ts +++ b/src/common/log.ts @@ -187,7 +187,7 @@ export class LogWrapper { // eslint-disable-next-line no-control-regex this.logger.log(level, message.replace(/\x1B[@-_][0-?]*[ -/]*[@-~]/g, '')); } - logSubscription.notify(message); + logSubscription.notify(JSON.stringify({ level, message })); } log(...args: any[]) { From 5376e16c9fdaae2f6f320b92f03ac7f57f884274 Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Sun, 1 Dec 2024 11:12:10 +0000 Subject: [PATCH 02/57] release: v4.2.10 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 9b04b238..374483a7 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.9", + "version": "4.2.10", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index 09873c0d..d2ece823 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.9", + "version": "4.2.10", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 944ed73c..4fc9deb7 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.9'; +export const napCatVersion = '4.2.10'; From 3b5d2c8f6f69082401b993b3e906d430436a5bde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Mon, 2 Dec 2024 11:16:12 +0800 Subject: [PATCH 03/57] =?UTF-8?q?style:=20=E7=AE=80=E5=8C=96=E5=86=99?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../action/extends/GetCollectionList.ts | 2 +- src/onebot/action/group/SetGroupBan.ts | 2 +- src/onebot/action/msg/ForwardSingleMsg.ts | 2 +- src/onebot/action/msg/SetMsgEmojiLike.ts | 2 +- src/onebot/api/msg.ts | 19 +++++++++---------- 5 files changed, 13 insertions(+), 14 deletions(-) diff --git a/src/onebot/action/extends/GetCollectionList.ts b/src/onebot/action/extends/GetCollectionList.ts index f1a31c16..a80484dd 100644 --- a/src/onebot/action/extends/GetCollectionList.ts +++ b/src/onebot/action/extends/GetCollectionList.ts @@ -14,6 +14,6 @@ export class GetCollectionList extends OneBotAction { payloadSchema = SchemaData; async _handle(payload: Payload) { - return await this.core.apis.CollectionApi.getAllCollection(parseInt(payload.category.toString()), +payload.count); + return await this.core.apis.CollectionApi.getAllCollection(+payload.category, +payload.count); } } diff --git a/src/onebot/action/group/SetGroupBan.ts b/src/onebot/action/group/SetGroupBan.ts index 88687582..1a02dcc7 100644 --- a/src/onebot/action/group/SetGroupBan.ts +++ b/src/onebot/action/group/SetGroupBan.ts @@ -18,7 +18,7 @@ export default class SetGroupBan extends OneBotAction { const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString()); if (!uid) throw new Error('uid error'); await this.core.apis.GroupApi.banMember(payload.group_id.toString(), - [{ uid: uid, timeStamp: parseInt(payload.duration.toString()) }]); + [{ uid: uid, timeStamp: +payload.duration}]); return null; } } diff --git a/src/onebot/action/msg/ForwardSingleMsg.ts b/src/onebot/action/msg/ForwardSingleMsg.ts index f37bf4c9..61beb99d 100644 --- a/src/onebot/action/msg/ForwardSingleMsg.ts +++ b/src/onebot/action/msg/ForwardSingleMsg.ts @@ -25,7 +25,7 @@ class ForwardSingleMsg extends OneBotAction { } async _handle(payload: Payload): Promise { - const msg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString())); + const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id); if (!msg) { throw new Error(`无法找到消息${payload.message_id}`); } diff --git a/src/onebot/action/msg/SetMsgEmojiLike.ts b/src/onebot/action/msg/SetMsgEmojiLike.ts index 1211a62b..0ac44b1a 100644 --- a/src/onebot/action/msg/SetMsgEmojiLike.ts +++ b/src/onebot/action/msg/SetMsgEmojiLike.ts @@ -16,7 +16,7 @@ export class SetMsgEmojiLike extends OneBotAction { payloadSchema = SchemaData; async _handle(payload: Payload) { - const msg = MessageUnique.getMsgIdAndPeerByShortId(parseInt(payload.message_id.toString())); + const msg = MessageUnique.getMsgIdAndPeerByShortId(+payload.message_id); if (!msg) { throw new Error('msg not found'); } diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index eba61da8..cac41fdc 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -452,7 +452,7 @@ export class OneBotMsgApi { }, [OB11MessageDataType.face]: async ({ data: { id } }) => { - let parsedFaceId = parseInt(id); + let parsedFaceId = +id; // 从face_config.json中获取表情名称 const sysFaces = faceConfig.sysface; const face: any = sysFaces.find((systemFace) => systemFace.QSid === parsedFaceId.toString()); @@ -460,7 +460,6 @@ export class OneBotMsgApi { this.core.context.logger.logError('不支持的ID', id); return undefined; } - parsedFaceId = parseInt(parsedFaceId.toString()); let faceType = 1; if (parsedFaceId >= 222) { faceType = 2; @@ -958,14 +957,14 @@ export class OneBotMsgApi { } groupChangDecreseType2String(type: number): GroupDecreaseSubType { switch (type) { - case 130: - return 'leave'; - case 131: - return 'kick'; - case 3: - return 'kick_me'; - default: - return 'kick'; + case 130: + return 'leave'; + case 131: + return 'kick'; + case 3: + return 'kick_me'; + default: + return 'kick'; } } From 45b1f369ac6e96eb5f9af4c2e38edba39fb2013c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Mon, 2 Dec 2024 11:44:37 +0800 Subject: [PATCH 04/57] =?UTF-8?q?style:=20=E5=BC=82=E6=AD=A5=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/log.ts | 29 ++++++++----------- src/common/request.ts | 21 -------------- src/onebot/action/extends/SetQQAvatar.ts | 8 ++--- .../action/go-cqhttp/SendGroupNotice.ts | 5 ++-- .../action/go-cqhttp/SetGroupPortrait.ts | 11 ++++--- src/onebot/api/msg.ts | 18 +++++------- src/webui/src/api/Log.ts | 4 +-- src/webui/src/helper/config.ts | 22 +++++++------- 8 files changed, 42 insertions(+), 76 deletions(-) diff --git a/src/common/log.ts b/src/common/log.ts index 0965f4ea..870f0c66 100644 --- a/src/common/log.ts +++ b/src/common/log.ts @@ -1,7 +1,7 @@ import winston, { format, transports } from 'winston'; import { truncateString } from '@/common/helper'; import path from 'node:path'; -import fs from 'node:fs'; +import fs from 'node:fs/promises'; import { NTMsgAtType, ChatType, ElementType, MessageElement, RawMessage, SelfInfo } from '@/core'; import EventEmitter from 'node:events'; export enum LogLevel { @@ -97,26 +97,20 @@ export class LogWrapper { cleanOldLogs(logDir: string) { const oneWeekAgo = Date.now() - 7 * 24 * 60 * 60 * 1000; - fs.readdir(logDir, (err, files) => { - if (err) { - this.logger.error('Failed to read log directory', err); - return; - } + fs.readdir(logDir).then((files) => { files.forEach((file) => { const filePath = path.join(logDir, file); this.deleteOldLogFile(filePath, oneWeekAgo); }); + }).catch((err) => { + this.logger.error('Failed to read log directory', err); }); } private deleteOldLogFile(filePath: string, oneWeekAgo: number) { - fs.stat(filePath, (err, stats) => { - if (err) { - this.logger.error('Failed to get file stats', err); - return; - } + fs.stat(filePath).then((stats) => { if (stats.mtime.getTime() < oneWeekAgo) { - fs.unlink(filePath, (err) => { + fs.unlink(filePath).catch((err) => { if (err) { if (err.code === 'ENOENT') { this.logger.warn(`File already deleted: ${filePath}`); @@ -128,6 +122,8 @@ export class LogWrapper { } }); } + }).catch((err) => { + this.logger.error('Failed to get file stats', err); }); } @@ -316,9 +312,8 @@ function textElementToText(textElement: any): string { function replyElementToText(replyElement: any, msg: RawMessage, recursiveLevel: number): string { const recordMsgOrNull = msg.records.find((record) => replyElement.sourceMsgIdInRecords === record.msgId); - return `[回复消息 ${ - recordMsgOrNull && recordMsgOrNull.peerUin != '284840486' && recordMsgOrNull.peerUin != '1094950020' - ? rawMessageToText(recordMsgOrNull, recursiveLevel + 1) - : `未找到消息记录 (MsgId = ${replyElement.sourceMsgIdInRecords})` - }]`; + return `[回复消息 ${recordMsgOrNull && recordMsgOrNull.peerUin != '284840486' && recordMsgOrNull.peerUin != '1094950020' + ? rawMessageToText(recordMsgOrNull, recursiveLevel + 1) + : `未找到消息记录 (MsgId = ${replyElement.sourceMsgIdInRecords})` + }]`; } diff --git a/src/common/request.ts b/src/common/request.ts index aecc5b1b..11523842 100644 --- a/src/common/request.ts +++ b/src/common/request.ts @@ -1,6 +1,5 @@ import https from 'node:https'; import http from 'node:http'; -import { readFileSync } from 'node:fs'; export class RequestUtil { // 适用于获取服务器下发cookies时获取,仅GET @@ -112,24 +111,4 @@ export class RequestUtil { static async HttpGetText(url: string, method: string = 'GET', data?: any, headers: { [key: string]: string } = {}) { return this.HttpGetJson(url, method, data, headers, false, false); } - - static async createFormData(boundary: string, filePath: string): Promise { - let type = 'image/png'; - if (filePath.endsWith('.jpg')) { - type = 'image/jpeg'; - } - const formDataParts = [ - `------${boundary}\r\n`, - `Content-Disposition: form-data; name="share_image"; filename="${filePath}"\r\n`, - 'Content-Type: ' + type + '\r\n\r\n', - ]; - - const fileContent = readFileSync(filePath); - const footer = `\r\n------${boundary}--`; - return Buffer.concat([ - Buffer.from(formDataParts.join(''), 'utf8'), - fileContent, - Buffer.from(footer, 'utf8'), - ]); - } } diff --git a/src/onebot/action/extends/SetQQAvatar.ts b/src/onebot/action/extends/SetQQAvatar.ts index 3f96a91c..f8003fec 100644 --- a/src/onebot/action/extends/SetQQAvatar.ts +++ b/src/onebot/action/extends/SetQQAvatar.ts @@ -1,6 +1,6 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; -import * as fs from 'node:fs'; +import fs from 'node:fs/promises'; import { checkFileExist, uri2local } from '@/common/file'; import { Static, Type } from '@sinclair/typebox'; @@ -21,9 +21,7 @@ export default class SetAvatar extends OneBotAction { if (path) { await checkFileExist(path, 5000);// 避免崩溃 const ret = await this.core.apis.UserApi.setQQAvatar(path); - fs.unlink(path, () => { - }); - + fs.unlink(path).catch(() => { }); if (!ret) { throw new Error(`头像${payload.file}设置失败,api无返回`); } @@ -34,7 +32,7 @@ export default class SetAvatar extends OneBotAction { throw new Error(`头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`); } } else { - fs.unlink(path, () => { }); + fs.unlink(path).catch(() => { }); throw new Error(`头像${payload.file}设置失败,无法获取头像,文件可能不存在`); } return null; diff --git a/src/onebot/action/go-cqhttp/SendGroupNotice.ts b/src/onebot/action/go-cqhttp/SendGroupNotice.ts index b36146a8..998d711f 100644 --- a/src/onebot/action/go-cqhttp/SendGroupNotice.ts +++ b/src/onebot/action/go-cqhttp/SendGroupNotice.ts @@ -1,7 +1,7 @@ import { checkFileExist, uri2local } from '@/common/file'; import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; -import { unlink } from 'node:fs'; +import { unlink } from 'node:fs/promises'; import { Static, Type } from '@sinclair/typebox'; const SchemaData = Type.Object({ @@ -41,8 +41,7 @@ export class SendGroupNotice extends OneBotAction { throw new Error(`群公告${payload.image}设置失败,图片上传失败`); } - unlink(path, () => { - }); + unlink(path).catch(() => { }); UploadImage = ImageUploadResult.picInfo; } diff --git a/src/onebot/action/go-cqhttp/SetGroupPortrait.ts b/src/onebot/action/go-cqhttp/SetGroupPortrait.ts index 9845aad7..b9354dd5 100644 --- a/src/onebot/action/go-cqhttp/SetGroupPortrait.ts +++ b/src/onebot/action/go-cqhttp/SetGroupPortrait.ts @@ -1,9 +1,8 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; -import { ActionName, BaseCheckResult } from '@/onebot/action/router'; -import * as fs from 'node:fs'; +import { ActionName } from '@/onebot/action/router'; import { checkFileExistV2, uri2local } from '@/common/file'; import { Static, Type } from '@sinclair/typebox'; - +import fs from 'node:fs/promises'; const SchemaData = Type.Object({ file: Type.String(), group_id: Type.Union([Type.Number(), Type.String()]) @@ -14,7 +13,7 @@ type Payload = Static; export default class SetGroupPortrait extends OneBotAction { actionName = ActionName.SetGroupPortrait; payloadSchema = SchemaData; - + async _handle(payload: Payload): Promise { const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file)); if (!success) { @@ -23,7 +22,7 @@ export default class SetGroupPortrait extends OneBotAction { if (path) { await checkFileExistV2(path, 5000); // 文件不存在QQ会崩溃,需要提前判断 const ret = await this.core.apis.GroupApi.setGroupAvatar(payload.group_id.toString(), path); - fs.unlink(path, () => { }); + fs.unlink(path).catch(() => { }); if (!ret) { throw new Error(`头像${payload.file}设置失败,api无返回`); } @@ -34,7 +33,7 @@ export default class SetGroupPortrait extends OneBotAction { } return ret; } else { - fs.unlink(path, () => { }); + fs.unlink(path).catch(() => { }); throw new Error(`头像${payload.file}设置失败,无法获取头像,文件可能不存在`); } } diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index cac41fdc..bf8f790c 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -15,7 +15,6 @@ import { RawMessage, SendMessageElement, SendTextElement, - BaseEmojiType, FaceType, GrayTipElement, } from '@/core'; @@ -26,12 +25,9 @@ import { EventType } from '@/onebot/event/OneBotEvent'; import { encodeCQCode } from '@/onebot/helper/cqcode'; import { uri2local } from '@/common/file'; import { RequestUtil } from '@/common/request'; -import fs from 'node:fs'; -import fsPromise from 'node:fs/promises'; +import fsPromise, { constants } from 'node:fs/promises'; import { OB11FriendAddNoticeEvent } from '@/onebot/event/notice/OB11FriendAddNoticeEvent'; -// import { decodeSysMessage } from '@/core/packet/proto/old/ProfileLike'; import { ForwardMsgBuilder } from "@/common/forward-msg-builder"; -import { decodeSysMessage } from "@/core/helper/adaptDecoder"; import { GroupChange, PushMsgBody } from "@/core/packet/transformer/proto"; import { NapProtoMsg } from '@napneko/nap-proto-core'; import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent'; @@ -887,16 +883,16 @@ export class OneBotMsgApi { try { for (const fileElement of sendElements) { if (fileElement.elementType === ElementType.PTT) { - totalSize += fs.statSync(fileElement.pttElement.filePath).size; + totalSize += (await fsPromise.stat(fileElement.pttElement.filePath)).size; } if (fileElement.elementType === ElementType.FILE) { - totalSize += fs.statSync(fileElement.fileElement.filePath).size; + totalSize += (await fsPromise.stat(fileElement.fileElement.filePath)).size; } if (fileElement.elementType === ElementType.VIDEO) { - totalSize += fs.statSync(fileElement.videoElement.filePath).size; + totalSize += (await fsPromise.stat(fileElement.videoElement.filePath)).size; } if (fileElement.elementType === ElementType.PIC) { - totalSize += fs.statSync(fileElement.picElement.sourcePath).size; + totalSize += (await fsPromise.stat(fileElement.picElement.sourcePath)).size; } } //且 PredictTime ((totalSize / 1024 / 512) * 1000)不等于Nan @@ -916,9 +912,9 @@ export class OneBotMsgApi { }, returnMsg.msgId); setTimeout(() => { - deleteAfterSentFiles.forEach(file => { + deleteAfterSentFiles.forEach(async file => { try { - if (fs.existsSync(file)) { + if (await fsPromise.access(file, constants.W_OK).then(() => true).catch(() => false)) { fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError('发送消息删除文件失败', e)); } } catch (error) { diff --git a/src/webui/src/api/Log.ts b/src/webui/src/api/Log.ts index 1684183d..5429e0b6 100644 --- a/src/webui/src/api/Log.ts +++ b/src/webui/src/api/Log.ts @@ -9,13 +9,13 @@ export const LogHandler: RequestHandler = async (req, res) => { if (filename.includes('..')) { return sendError(res, 'ID不合法'); } - const logContent = WebUiConfigWrapper.GetLogContent(filename); + const logContent = await WebUiConfigWrapper.GetLogContent(filename); return sendSuccess(res, logContent); }; // 日志列表 export const LogListHandler: RequestHandler = async (_, res) => { - const logList = WebUiConfigWrapper.GetLogsList(); + const logList = await WebUiConfigWrapper.GetLogsList(); return sendSuccess(res, logList); }; // 实时日志(SSE) diff --git a/src/webui/src/helper/config.ts b/src/webui/src/helper/config.ts index 9c1d7413..6f74e93e 100644 --- a/src/webui/src/helper/config.ts +++ b/src/webui/src/helper/config.ts @@ -1,5 +1,5 @@ import { webUiPathWrapper } from '@/webui'; -import { existsSync, readFileSync, writeFileSync, readdirSync } from 'node:fs'; +import fs, { constants } from 'node:fs/promises'; import * as net from 'node:net'; import { resolve } from 'node:path'; @@ -90,18 +90,18 @@ export class WebUiConfigWrapper { try { const configPath = resolve(webUiPathWrapper.configPath, './webui.json'); - if (!existsSync(configPath)) { - writeFileSync(configPath, JSON.stringify(defaultconfig, null, 4)); + if (!await fs.access(configPath, constants.R_OK | constants.W_OK).then(() => true).catch(() => false)) { + await fs.writeFile(configPath, JSON.stringify(defaultconfig, null, 4)); } - const fileContent = readFileSync(configPath, 'utf-8'); + const fileContent = await fs.readFile(configPath, 'utf-8'); // 更新配置字段后新增字段可能会缺失,同步一下 const parsedConfig = this.applyDefaults(JSON.parse(fileContent) as Partial, defaultconfig); if (!parsedConfig.prefix.startsWith('/')) parsedConfig.prefix = '/' + parsedConfig.prefix; if (parsedConfig.prefix.endsWith('/')) parsedConfig.prefix = parsedConfig.prefix.slice(0, -1); // 配置已经被操作过了,还是回写一下吧,不然新配置不会出现在配置文件里 - writeFileSync(configPath, JSON.stringify(parsedConfig, null, 4)); + await fs.writeFile(configPath, JSON.stringify(parsedConfig, null, 4)); // 不希望回写的配置放后面 // 查询主机地址是否可用 @@ -137,19 +137,19 @@ export class WebUiConfigWrapper { return resolve(webUiPathWrapper.logsPath); } // 获取日志列表 - public static GetLogsList(): string[] { - if (existsSync(webUiPathWrapper.logsPath)) { - return readdirSync(webUiPathWrapper.logsPath) + public static async GetLogsList(): Promise { + if (await fs.access(webUiPathWrapper.logsPath, constants.F_OK).then(() => true).catch(() => false)) { + return (await fs.readdir(webUiPathWrapper.logsPath)) .filter((file) => file.endsWith('.log')) .map((file) => file.replace('.log', '')); } return []; } // 获取指定日志文件内容 - public static GetLogContent(filename: string): string { + public static async GetLogContent(filename: string): Promise { const logPath = resolve(webUiPathWrapper.logsPath, `${filename}.log`); - if (existsSync(logPath)) { - return readFileSync(logPath, 'utf-8'); + if (await fs.access(logPath, constants.R_OK).then(() => true).catch(() => false)) { + return await fs.readFile(logPath, 'utf-8'); } return ''; } From 2abc7e541de7f45251517693d18ac3d5685b7423 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Mon, 2 Dec 2024 21:23:13 +0800 Subject: [PATCH 05/57] =?UTF-8?q?fix:=20=E7=A9=BA=E6=A0=BC=E7=9B=AE?= =?UTF-8?q?=E5=BD=95=E5=90=AF=E5=8A=A8=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- launcher/NapCatWinBootMain.exe | Bin 29696 -> 31232 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/launcher/NapCatWinBootMain.exe b/launcher/NapCatWinBootMain.exe index 813f342766861ad90ee61f1517c2182e8c81d704..9501691cff77d5244e000b11e6e43af0e14250a0 100644 GIT binary patch literal 31232 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 M690Ps*Gl030XV-{xc~qF literal 29696 zcmeHwdwf*Ywf~;UOcL^7f@Ux%)FBKtmWLss7)T&9B!g#kf)Nrx0VhK;A(6Z~j|3Dn zI8YppWBFON-doyIMdjY!Ua|Ts0bdD05+F)ID^R@>tgR=;UZPbVTAkl_?Q>3M5)!5N z-rpbn+#WnSGY z7q2;S&rbyPelJERyn}b+7eBWD2ZxW?-{SCL`|BJ&W>Wr3BMsDS7c3?-S)&Dgt?*uhva8D<+}CWRlN*>1=S zkhzSp@f3(C9{u*jhm3Wt#33{{)`~5H60=DVwX%W_hmBrfp>VjG9D@c@bH@l` zyhGIN0~2uRw1&GQyu!xmPzrYw>#H=Vk+UoyGg_mGT-2z+&awn3!yd4Lw|p@d0jn_g zfUbD&n=|YpMm+&PVQM1jV+#R1*CO zL43(f5mp>Zs5qUKXeD7{#OX4^Mjs)`=`y07?z_Ma<8*-o#oXUklE*D?7lI{9EE(){ znS%IxIGrs!Y!%NJ246BaLn?cq3t{Cz<$~FHtNaKFk+@>V@S-@M+O)87CXkmn-?c!h z&Q~NNI4N=ED4c0qVGaU|8dol&#%M)2j_^*SNY(<20h3kX~p?P25NP;QsB z#7HBP0F4Eg#S6hIj7|+{LU66gx&#bO0(v;V?z59=5Y0Yu!cpUT)lT{i1mBZ%!npwl4ab~Gjldyz+1bKm&AV*v&60;CiS^DhdaTQ$PQ zi!>+lBuKMS6+EoZK;bA(TJ;^q`p#kqbBOJZ;4DG>oe;b;#V)=f1n*6gev2{HdQkTr za9d%9)7O}D1cZAF#eD&7G+FO#m!9Fx8=HKp_B#k>OvsF5_?lIEh~>_1lqr{?x<=8b93{xjMdXU{#@m?E^u48!*c zp(NHfeQyFt&?ai{K$gQW{}X#?p+?BrjgUu(XrOEYX+RJ^7lKYS{Dctm zju5<&tD`AR5Z{oV!vqnMWRnii@*1=pIw^VQH(?FecU>s7%37S(%0* zq|lsV53Dz*#Wm`RM6((5R)`|K2wM?1DkO+Y;stTEBljb}-XVV3H^Cvkfv#y6e66Yp&f@u&k8KtUL#-jLD}JIT<21D5CkUiuncu1k6Wx!kAa zwFANzUqI0ML7iu27Ks-IrkXw)5F_Jz^-PE|blp$_B-Ra$=u?qFKOaawz7JV9oJBtv zyl(iEw?0yG!uQqj{UvpLzZnCr&lb(Tm;@O-zT1FO2i)FlKH&EF6ZwP?VR~)f^)$Ht zHkwHyO7K<0?+^{b#+OhC?cl?^FE5(#edG+O?F3M2ZTG0PDeDNROl=O0?Rm&n))7ys z>xd{4Wnix(D#?G<@rz!aN?%*Df6E6{fTaxzZ)EY6iO9^)zA3?M8q) z85yF4&@D7P8MZt_wvHa69yvc~hQ_!%NK!qtgeLgQySf=wwjV#2pm%w*P} zIa>&P71ua}){ddnS{~Uqe!++RG>2%#oMiuX_9>m1zaV+x+NNoCOkjQv))?-T1(zji zctgBfrjBve6`0H%fquKBU=lr_Pv#wlhrQ@AKhj@J4)F&m5j8?XMPjdg3L^X`LhgRU z{WmF^EDGg~MU!A|(&_(@LO&R)(1LTM&eD4n zCYxMh3wh_mgjSLRW0bnpE=ocOR+-X^l?&pr5L(i27!zl0F+pFBVBU0Uc5walcxf_NIG;bHgo3!z36W*8Vk-tc4Vflr9ARKIji)YWBKEDwhoH2gOS@@gN8 z(noI?*k?G5Bwwf=;t5P<515v>k5FziXw=BkrWS0CE~w1COr=Ydfw(vOIPJ<&c^#L{>nl=m9i$%NebXcubSu@vrXFX1~*X zx0(5_l&-&$C>qkdFsOwn;)nuKcm=p3l}j@z)GCCH4-;=}r1<8`xfonv$s>H;Ac$C1 z90?a=bfbd72^;PNu`KP)N~@5=d!X~qmF?1uQD}my8_X~EW8Z5X_BqIm!9*dn81f78 zhA~>{N)vI?KZr~rh+2l|RVh1n*s>>RJrmMb4Jj)GTq%Sv<9JL=LV6od!nkdi&+sHP2-w5vd&@{$2wfbPy2yPz=QISbPZo3bm0fS^5$f8M>;@z_KLSKw!kH+%A zy|6Ox$cQVndg9YS>aUk2<2~Od<3}JED&xFMW&AOrqMGqM-oHgg)vmXtQS=IYVQ8Ex zh@*t?{((?8+`#f38dz+YSP6l0xJ^d^U$`EO!8>*EuyG@_)76`Zs6u^~*3@8%sbvBt zReV1zoC4B}_O9OL*i0Xw8Du$HAr7(|b`5zMncpDV*(1`1^xvNjK0%ETXp8SXjdhVC zk3F6Q8V7;G#eBPNv_oW(y*focRX?YsH%Ids3R;jDHlD$X5ngmaF=F)Re3+cj6*z_eH_D>5LgXw6ORI>F=^!|81V!5QCp8vTMa_so-{+D zcnjapxd!!R(DVuC1g7lc*@wbL9WjJ#2+_Qe6nZUKtwrQ3U5c}WP>p%7u<_#_wCKeu z-Yl9N23MY-!KUbRA;x=2!~!Y%IFh@8+9R#UYa!|i6@f^q!`t4^u^#KNUCA&B3LF3R zI;uH25o>HAWPFg+7_{9W-PgyLUf9Rt`Q0>htO3puntSpBWp!fS6gF-rxkI)+w(LWE zXrq}_$aaFZ4l&YMfQ80#(Dy3LnXCgVA{~}TfF{(9%KziLvju zm5v2P$kv1wpoz3Qyq94N#C(uw#pD&de&grpV^Y^g;jpqRXyodYMZ73CFiWQV6;VC` zO1{}}Li!1kwC_T3^r%c3FH^P=??CoO6U8|TP^yh(_VaXS?%h$6fP z_}+Lv-L&li!HXb(+}nvfsQ+pS$WuWsmcr~1VFl+a3vS`*H^RnS$u?rqp6uh$3nClo z59+}>TxZjA&HZdLQM`fmP}n$8p@wJ5?uzN7Bb202K2Ma~8ZZ@CaQ1PBm|Vjd6zw5@ zGlh-c$7|pS=}3GtY`jX&d^K#GtdJ2_Gl0ES!G?`CB}GfDGiJu-YSD5!fg$0;#U6HeIIj5mC3|>+ILcb- z5=$KlJH-?-X!Y*I7%~^8p;#@@q{$@RK`W+^@#u}<#u4MNWQ@vxOwP~8p|G5P5epx+ zy-Mscp_#PML+frA23l7EP08Che_QLmORdYttW5}9hkJEAJt#o&@aID?MQf-fUbrvD zICU&h$6CIU8-)c%DLL3TD)L&Y>aBRbAi-!GNA$shluFG0PzQ~;2DuJCHinX)gYKkj zK#ywW@h+5H1fP}SRNTPagkmzH^{FBe@dsN?VnG^~xsKe{rl}NA<8BVk)CfT@GQOlJ zn~NRUTZ>{^rTekW;X?`fZF^rDjV2b1CU)_-17|AG^bzS*iqO!8pxQQ5*&BoZV-7#C z_kxIf3K4f3wMx_{F9OKMR=VvfzJ5dWJtYK-UKN6+9gd*ws4e@?g~8fAeCXpl_ZT4z zV{CA!+Bn<>d!Q4iG_5$LnK~b*G@+vN()`|Vc$-Xi0wf>eKCA4-hKw&$a6#mj#t%@% zID%_ZaAQ^N$0n-#yb#!6h;tZja-X-qIg^devqTD4(deS3V%6}H? z(pr=(H4J5U{xaB|$8x(%`X`wk{KIb`b}0FVRD*3uh8-Rduf`)pg#Ev@3`u-Q*+;va1xzh-mT z@X%~>T5;u%?BZ2&_j?uuuj1}ddg+3ksakxZ5h{9Cdiet~N2v5!yq%RgFo7Z;!YmSt zk{Prj5sRLUGWfV*{Kv3`COOitVz~3XDsN<;#9T!Lb zeir@??H-Z7^CxH-W1SFO`J=uxN63Z&?@frbMaqy!|Nfs)gP@1kNxfq7EG$eV8!Tj1 zKFatU<{&$VjL(2V`W~|QYw1~O>mQH~8Nd8bjtU$1gA`3hb9*j^!?u_|*#jN%g}EOZ z9z23569~s_7>7-OV%Rloyw`*<@)h(MHcmoP31`^NsXdMRLp~`qT1lo*(NU?Gl!@rI zB@ov6lkK4;WgPu~|3U)To<(r|#Snv_Ri}lJ|ETp<9>ZpD_z-IG9~B1T9Zoq6TV8|! z_T1A=qaDEw9k$R-7`}?0nhMP?Ivtlv>2vC(Jnyw9i}?`D6QxQidQc| zMKt51PK+5}CT4sVyLgYlXMOmz1{;ZpTED>(Z=3Wkigtx3AHq5~Jo!cb{57qK)w#*3 zy(!=#Vfl^;Mj@s!*aaCah{n!a2!Y)UI^Wjsqc?ftF?k6UkCexVgiiEKGOam$SRWse zeu*@(`A{x_)4{MWR+@q!+a*naP+h(GlqMcF5!(>Iw+8-z4b4N=b4=$DkNK{&ikA4I z@?zcjla82ULV2fS(lL8^`^{o%3MMNlqJTMMLHxNwO^m^pX!tE)pr_vv)9J{@WaQBD z-k+f@k@z?CY~C*3jz#aMI5ZQmdyNA&@eMl`rlA6zRD(b#+H~g1b0_(TX}hM?dj4c# zOl#rh0-bLR&Yy~C)8}m*4@eIup^@^$Z@43LnuZOzDk}}wZh~kimzzA!jwyVK(AJhx zdA==mTJSuedxG!GK>O_+S*W3gD-5%yA?k@}DU&CtA6 z`UTQ-N`Q?a{_X|6v|%nttZ9g ztD)Y&MU7$8Bp@~)^55K{j{yoVG@!kUT?{nnWdO;{o9jzx2_vufluYnbnP5E;e1+9a zhyFD|{%W%?-tt>)|Dmp}N2G1gr4$2k>BuaQ!}!jj^ge2V9)q~FT4aOgzo04kD#eC>2WdWyp|=_h~=+P!#FjF&PHUtrFQs1X&ES~i{Y z>!ppL$Ugp@M*1$K!NhzVC{D-}x%h0+#6VLH8ai0woBKXY&v9<%Y?tk^95QW_lf_7) z|Nagxu?^6GQrLlmovwtf$x6K*_?}|BrJ#amOmqvPZgM8+4IQ&jkJB$-YIbYCV0bQh z`%Q52|ICsNyj#n25_eF)8Mzv%0!|?Xz<*P#UzgR|mnt>NFQc*#No$bo`)D_| z@@VT&@%84p4wS^S$-f#VZn{f)oqL%t16%cGm~|F%r5|zW(8EH>^XUCPS=#BSA-mF0 zufH8Z3}2swF_Y2jL4X)H8h~C2;`J(kgZeWIAS4$6i>|%U55{*0#dUziLR(AG9XLms zt+&61PP|qqPhO7%Tx>Rhn8O=q)3@AyXGBi$TPh|qX+q2~!yaoa z7Ff;LOE@e9Q_c3=W4<);<|IdMnA0I;tGfzCIGx|nUkZ9Qg$-l(^{~f1I$zI2uiXA_w*-o9D&=!?vFC={6nrCrMvJL^$<% z0JXuemoiQS=Dvr9Anu&<$4T>`a0>gFB_($PEnNo$)g>4TaWubQu0`>a7ZFs2^Cz!q zJ%2LH=fdq>`=LPf{GkVXXV|Z#^M_w!Pm%U*`S}C~R1j7Cr5@BNnf9>2iO2^0tP0NMwwL!T0(f}FkBQ2^ac&_Pjg ze3JY6^=Q>$YSq6WlTH(J;0@1_@ai>GytE5_>EhemKr5JPuIH5zQfznw7FD74{4}8#}GpR_ZCD~k{AW^Vo93%|W zP!Kqf!}|t5BeTFlVdIafk>c|gz%)C|CyFIFXB4sP^#jU5TW8aD6}Awe&?-81FLTiB z%0c_X2MO6gFBynk^43)riiJA@VeR_oP@jc9q*UT^TB#s@3|(eI8-*nFi&;;cp7u{u zkjicM`-Fg-j+u&5!bbYGMi6b7QrP;##$9+5TZI{v-YKU;MQQT;c6zs^p;*7r)?*T~ zk_}B`y*6mS%}SNfmhbs)ynafr2zD#mvs>G{_@EBKTbPA0 zK&bIK0R-X638=BFYqth_!|+4ku(hi|iho#mL(lPM2}=b^Xu%T!DZ6#HemiO~Jkm<^ zk1FLh6AVoD1Aq^WU8O?k-^>>J?q=i1t$K=Gis$Vc!#z(Y9(n!xy)mGd4xk3U@7!-~ z>DB=;1`pCi*!VJt1@UJR$b`_NCkZbefpx>ipHdF2nx|Sd)WdT*QN*$kG9*z3*_LY; z2@`dkMV$5KPF|M-+6!)`YDf@{w6;7>wQ}Fz`~b;$H>4Frxdi5oW!OF~#s-lEu!P(l z_Qu3gJP8*7sU=FzAooq(iWF~q0aAR({x3&i#&)<`#^N$r5n?m>Zd{OhKzy)oi5g!Ox z{s{*nd@ji6GS~r>n$e$yAm+BtcgBSueu*x-;~UDR$e(edVJX?I*@p~I6pC%O>~{*q zpYib#8m1ctq^aK4c9QXl#kyh9|5WKe@?W=MU=4w!~x5jSi%>g|Z4)k11Zc6TnRzMm{Di=5k9 zcWd?`%ppL%j8)4v0XC8^=-z}A6R^nuqW4B(6WW?X=@)5F0N#)iVfpAbWDLjkMy&gGTMd?a8 zy^GR!AU#k6z68Jrwpdr4}s+PZOlPOZZ$m8Cbq#XYDfn*XJw<_EEBJ-VyGP;o zo`Qc%fisnSRq6!_-3vqTEeid91*-Htx$>WVt1zWde$N~NRr;|7ga1tYvm2H7l)-qF zeyWoHfl8-lTqbN@Ci7i?oeVQ)$gs3-OrR-Nk(P1NS)!tqu7^ z;lX8$=C9Qu7(1a&otjvzQ$cwvC>rH_S9C_xqNO@ zkqf`|Q0%EMcYD2DfR!lZ@~m!L9wo_qNM6q+pjOx7p9^(X4DfGsm;35H5qmW&_F_Z) zEbMgH$v}mG)_@7KR%lwD3>|QM6dlWb{pD&y)LH_?ke5UGg zNEIg)`6LDQEAWCUcS)pu>ThLWy2X;PH1ol*zMFgZptHGD(~p}iGTE^ z(hzP(Gj8M~rj2;f3b-XI|0L2Q$v{kqC&^4#v>a4V^c5|~g=WSw?m#ZxBV5GO5|zIU z>EZN1yj0%N^>hAFyj2=PnKr^s;D^3G;zjhWQTg3bX(~iK)GNAP;;UY1NNAh2%}MZE z3(^k248UK{#wH@}#ktr`%VR7K`vT)}H|9ew+*{@|_8{)JanCPg>`~mb<$N`EL7ceh z3zgd@rXXpGfhu{dAGXz%voMkYM^AiT)iaEdH z!8lI0Y!I%5)0F|o^~>8{)L<0C!tcQc<^BR;Zb$fMvI>uY?qq)>`+ z6zdfH-FM$zv5H3j<~tP~dVJ+(yOr1dz#!ZmyzXt$xDuJ}@j*D5ZU=B_depO4{#~4w za`>l4eK)T~mHan?Isp~iW)ZH-%~)y#2QOl*2RQo8ABAov;daB*$j=lUxfX9QF;;)G zhhpe1^&M)VK4pr$^A&~#xHRnRO<(2Z@9OUqslVsLcHqZ?s0{sXj~TN(TxC1p-(24u z$K@vcH(eU+WyWS{Gc^f1mM}AxC1k}mXmlE;yC3m|;Cw(sEHhNZGsDa{X2?oxklz#V zK8hI@B{qyx=;jnK_8IhBb`cM4Asdq#%TiG;73EU1QW_{f)rnXz0S_~7ZPEvmL9X+$ zyC60jr^rHddC0D^;c*MmjZOhdOTIMjdx>oP%n59~<_eb5Ke{KWJE1kcA+9VI{jm_* zUJG)F&N4P4@eAAuqyK6{m}o&o4KwLkmPOB2{%lTqKh_cdvNmC(HvW%VttEje`Y0YZ zHlB^0nasv6x}ra&XLM^)Lt+`p^%hv>oWbKPZdL~8Oj!S2bg#^ugqSm)Ae7ok$7+Ak6I zg3Uaz*NkK~$v25jsz_&(Aj2fh)hw-lY)@+U6|E_X9$UfiFv?~eLgGB`(FyNDZI-MR zcy8m$o2dO~(&R+#aRS|3xeco*{Y&lGpjJCx_i6kG+7}Z3Z%k52f_d~42`8B&L6eri z(kgJ5jACgs<5^l(YX22IDT+_Qzu<%9W8+fcHxCc8FxN$i3%YRO;m7?$+zDJ4#hD4k zP)S48y|NW_u^W&FjnEkKRb0|JRu-$*>Sn}fm$ADN(5~Tpb2o})?8QU&%@i;!8&rHHWe9WA3mX^aObw@K?sT^$l)MQ2&8TO_nfsVg)50iD$ngt)HdD_GS0pxoHRm4< zDNS*jE_I%hhIG#)jvDmHgQM_^2C^Yn4Bkz^3%F^l8SdKAEMueV$rw^k3P`$#q5By6 z8G5PmY#T-%()+FvWT81FUD0xE8X^bftNi;%;6JpjG)}75LE7hn4Z15;3~cVhxCNZ4 zDuS!=!|sb0j?W#!(IQ_-<7rUkNsU)5C7QTmG$uVy1)Au?M-gqVqb;es;j6$ z+*(ZUwz|e@PkmjjyAD~j&*@#t*dwf$M`@VK$Bx5!;v?{k+_;RSK^FSJfJZxh;q|J9OlQ)c=Zc>t5^mgmAV%z*eDtoOy z-(Oem@~^J)IS2Nsy(*ai9y2ug`SYtQEVTbvQ}k=79~ic zDGRK{HtP}+uDZ6tG9$}^LV5c9CbY87l5Z~et!r>woJ&=<^R3RMT&@x!b4Kmt`T;%q$DF>$RBt)%Eoimg;)1bCt_GAU7&lNOG@^$*-(&t@aK> zK#gYexQxE4Ve*|fgJAAR`Btm*X6MZip&b&qebB`)Eb^^O6eV$mu=jNN-Uh#~9BqZL z)#XlX?YmvItNfLg!cu4n{^0a^;M`tIp{y!ZVOFca^>s$(Ksx=KIXG`7Z{R|+o{=SS zE*&C@YPN_hHvx4A`@_hxTm}y=jdLj%=QVJ0`@BAM)q#e=qTjBRWrA}dBuUWc`>Ow@ zqVu`Gs!FVv?J#0}vh}v;@;wz~E4A2Vh&Z^OZ#TDGlM7y3T~qV7Ra8j+JTubgWdFD< z84I~@9U8$3mtjoB4x=x%!?zY5tRdABBNvl3Bo1ebkqar2bZ}h}Ym8h_uFXA+Jw`4w zP?>WnOfqXYi(FP=GD;yL`>o!}wF9o8D(G9|#WK_#i+Y9-xd45ypRDErOYnfTx1 z%Ex*0e~-s4WTo=qt#p~~gPnMUziT8rT|OUZ`dCap<`qLH0a;5j2gXJ#dqJCDUJtk8 zeJ6X#<@)Rlixr<9 zjGvdUb=Q_RV6HfqQnmGsZu~4iQW)>Bzo*a-%*dSO^+cO~3BJOOXjQ)sdnwL>wQDhx zt#7hLe{SF;^aA{^3zSK-XD03K1MBoQxXY_6G1sW#({9Y4uK~W?@9|*D9hnQ_7x{<5 zLLPkZi(l7YQn09WVTrxSHqh2+jB|$E{@Kh~UDxQUsjhG~xJVc%!dX{eT~}ET;Bou> z9-It4$(qW&n62WZilJ3JRm$9JtD%uf=3VFYx$(tPIrCPpu5)2djCNp3T7!=ws@OPI z%jcii!C+3WyUK}ik}7epb>n*gV0=zjLxYp1n`{(m5p{ZD4VSM9Hpad`c-7ZcR%@qZTzW2Zdfcn4p&-l?`D(_=`w%tX ziOCaG$W^rpUpB~k<875m<=#eoNlVbClM4ZlA{ol-;hZGX%t3mnuj5R8u2r;R%ZsXC z$*IJ$f`6{?9Z2bc_PG?$b0sZ;@RbQEfPY-UTn*LJYQ58%s_UkKavJ{68ougU_q3WB z(`HPQJJ1jcI0sgE(F8+zRJmOZ-;_^w|E7F&*6R9bf*}&fI*TS?pX(O+Y6@Hp@MU*_ z(mTE)oTI*ejlW@mq7Pf0k5-5}J#v)AWN96mj`ioC^rdxjXRH|L!wa|@GqzK|*zFrJ zJBwDr1#;I_7rDD)>|P~Z?^%G=F61iqxV`AztU-61-|bmf?DkaBTC}bl5<}~556ixk zlf8o77=@Kr%RsV(7}UC$cgqqO%<6@(Zc4GOG4_Ejv)BQ@6lj6H0}>UwSNT`3c6(Gw z9wp92^%edaB|yqLJv!aZ+y_(@r@Vnfbx=EEixQmPEQMNmFOGz(zjnxWW&}^fe~@3 zvfg4L5&y2Q6l~o0j=_F34h263T#-uWS2A2O76+4MjMV@>gZm{Mo9+Vq;Z@ib0?h+} z52ax*3F&Qs{&7gJ!k#$beOJpg&4AD0HUs|xpnW_b(hk5Izk@SWq;ml8z83#a8>H6* z#!o~((gYvFP5d7R?8Z&!;{->g%jqOQI%8-B4M94WAR2-dN}A3HT9q`Nk6dAvX;J}O zaQ_wb1nKDTaquBX$ArX(j?Lb|O>L*cVEQSb50IwAw_GJnCwuqcCK@_yrNhsu&?iCN zb#j^@{Sw&$q5*sb_b#Mc0i78*V}PE^0B@dxGian8fPckJZ6`Q>D)a_=f_X}s;6dCO zpy>qs-ZWX}sep5FlROr{+mv((;On^G0=^sYreWD0d$VC_;#0FfqNIy_47R=-6ey!uTNrRfaypK()~J~l{SfVc{~)43*{RZ^NDeMhtc{0W}L zO>O_j^}k628q7Iqe$Sc*w_5(|L4<}&@c6@PCV;qX2mXpsJGd0Roqh4uy!^GbHKsmy|iS(w48Jk_ATowT=+EGJvV)w+nb(0FF7I4<@LI2 zSJkXDVcJz)iPhVZK}^85&O0|9>!CT`@+x<&%R8;My4+Lmt*`V=!(4oh%Ue6WaYp*Q z0Y-N0r2DFU>r6C+&rQcvGCM2XREI^@-1PFg>2JGwZqOm984Enx!vStsTx7o?D|5!|8rC;OjdJ5zS1?abVnv$J96+MUfickJA?bN9}covk}N zcV5`Zo{E2J*VDV7?s-~zT6m`HnW|^{pNZd{9Xq;rT-agy N<==x6{`boG{{Uzp&{qHe From 672ae8decfdf698d0ef198bdc1ac1132151a95d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Mon, 2 Dec 2024 22:08:08 +0800 Subject: [PATCH 06/57] =?UTF-8?q?fix:=20Once=E5=A4=84=E7=90=86=E7=A9=BA?= =?UTF-8?q?=E6=A0=BC=E7=9B=AE=E5=BD=95=20=E4=B8=AD=E6=96=87=E7=9B=AE?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- external/LiteLoaderWrapper.zip | Bin 86771 -> 87700 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/external/LiteLoaderWrapper.zip b/external/LiteLoaderWrapper.zip index bae1ad15a5558665b688f1b7576a725172929d09..3dde2561c362d21c3002a0624561930ed02ed931 100644 GIT binary patch delta 21638 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 yb{&{I>}3dP!50YswH<&93fBR#$R=G7>eKW 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 Date: Mon, 2 Dec 2024 14:08:36 +0000 Subject: [PATCH 07/57] release: v4.2.11 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 374483a7..aa35518d 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.10", + "version": "4.2.11", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index d2ece823..354ff82f 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.10", + "version": "4.2.11", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 4fc9deb7..44fb919d 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.10'; +export const napCatVersion = '4.2.11'; From f429db61af95483a153780ccec649d46793d6d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 17:16:58 +0800 Subject: [PATCH 08/57] fix: #594 --- src/core/apis/user.ts | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index 2e4a96cd..cbb62699 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -181,13 +181,13 @@ export class NTQQUserApi { //后期改成流水线处理 async getUinByUidV2(Uid: string) { let uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid); - if (uin) return uin; + if (uin && uin !== '0') return uin; uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid); - if (uin) return uin; + if (uin && uin !== '0') return uin; uin = (await this.context.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid); - if (uin) return uin; + if (uin && uin !== '0') return uin; uin = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getKey(Uid); - if (uin) return uin; + if (uin && uin !== '0') return uin; uin = (await this.getUserDetailInfo(Uid)).uin; //从QQ Native 转换 return uin; } From 491a79ec9640aade86c26cdacf71004e3a46c7d4 Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Tue, 3 Dec 2024 09:22:43 +0000 Subject: [PATCH 09/57] release: v4.2.12 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index aa35518d..25b02629 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.11", + "version": "4.2.12", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index 354ff82f..5a5afd73 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.11", + "version": "4.2.12", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 44fb919d..1fe967bf 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.11'; +export const napCatVersion = '4.2.12'; From eeb27d38bcf697a9dd0b7e7bfdd51a84055a73e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 17:33:13 +0800 Subject: [PATCH 10/57] =?UTF-8?q?fix:=20=E6=B8=85=E7=90=86=E6=97=A7?= =?UTF-8?q?=E7=9A=84=E8=84=9A=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- external/LiteLoaderWrapper.zip | Bin 87700 -> 68555 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/external/LiteLoaderWrapper.zip b/external/LiteLoaderWrapper.zip index 3dde2561c362d21c3002a0624561930ed02ed931..ef57db0fd3efbd1669029dfc605d23ccbf510984 100644 GIT binary patch delta 1939 zcma)7ZERCj7(Va4W!}?#Mc--NNe8u*7q0M%6)LfYt4u?Z$eUa*#$Z#ld)1BAq z%}W>#t^a!1I#v}5olmlOE$7^aE9N;ZOJC^f%6=IsZgo6l+Acj!XWT#^#XadG*xQYd z4i+bMPt0`YR8{cSV3%9Xyp8pC52ToM)O{nZr;+`N%*mHtTXOVEoRy%*dy3QphjLlT zpM3ii$W#OCq{Y*Dn~rM2*JTiRub2mU!* z1ofK?t(f9hZQg(T#GQ3oT&;U3)i$-*}_yX=R^uYhYBlyH z@Zg@gN8CO*w=}slsONIMqc-<5lEm~;r$DKiyin)_Qd+C-k?JPS?A)!9ec%AroKmh^h)&L-9NhUac3) zys9IW8^`V4eZ&xe`4U3-!JbFSx&Wk+lBJL(A21QldB^gMQ6EvLG>4aSQB1cPz-U&0wZ70l?cfKxo3(LVHpYG%X{AQw2P|lT8igl|% zZ#|GBP)7yKmGZWerV8*$`?r&m72uUN^^xRCsF74VNJAwQn5dJO0@N`D)b_K|t?S!X z{lD^O$7H|X|BeexysTUot^NiZYe=&Tth@tzTwsuFgn>9>R^wwWv*PFmVBPwO@!z=r z%45_MWeH@E)o##Bv-eNr%p;wZkjBME+*Itx1LS8n%;!_;JyfFfASw62N@?I@a>xTu z7^jvyC&us}M-bj$WSTUQUq#o#b(CQlIJsCW9DGm(dO37VpgYP8u@>aLClGeo=S^xT zUh3lJ3DV((6>{xKgpYXAafUNnXql_2mg)V zYNYlCr%*JWT&w|ytp7y-mpAg&eN5i;n*bJfa3GDGT>@Fs?!U+nOQ1kABaWR*E4}Tr K5Nf-_M*jd}_n86! delta 21126 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 From 0b8d0e3cac8f0f6cb20e0a968296454601515cea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 19:28:51 +0800 Subject: [PATCH 11/57] =?UTF-8?q?feat:=20=E8=BF=81=E7=A7=BB=E4=BA=8B?= =?UTF-8?q?=E4=BB=B6=E8=A7=A3=E6=9E=90=E5=8E=9F=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/group.ts | 215 +++++++++--------- src/core/index.ts | 91 +------- src/onebot/action/group/GetGroupMemberList.ts | 9 +- src/onebot/api/group.ts | 91 -------- src/onebot/api/msg.ts | 3 +- src/onebot/index.ts | 143 +++++------- 6 files changed, 175 insertions(+), 377 deletions(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index 074155e9..31efa701 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -139,14 +139,21 @@ export class NTQQGroupApi { async getGroupMemberAll(groupCode: string, forced = false) { 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(`刷新群成员缓存失败, ${e}`); + } + } async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) { const groupCodeStr = groupCode.toString(); const memberUinOrUidStr = memberUinOrUid.toString(); let members = this.groupMemberCache.get(groupCodeStr); if (!members) { try { - members = await this.getGroupMembers(groupCodeStr); + members = (await this.getGroupMemberAll(groupCodeStr)).result.infos; this.groupMemberCache.set(groupCodeStr, members); } catch (e) { return null; @@ -164,7 +171,7 @@ export class NTQQGroupApi { let member = getMember(); if (!member) { - members = await this.getGroupMembers(groupCodeStr); + members = members = (await this.getGroupMemberAll(groupCodeStr)).result.infos; member = getMember(); } return member; @@ -253,28 +260,28 @@ export class NTQQGroupApi { 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; - member = members.get(uid); - } - return member; - } + // 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; + // member = members.get(uid); + // } + // return member; + // } async searchGroup(groupCode: string) { const [, ret] = await this.core.eventWrapper.callNormalEventV2( @@ -316,88 +323,88 @@ export class NTQQGroupApi { return undefined; } - async tryGetGroupMembersV2(groupQQ: string, modeListener = false, num = 30, timeout = 100): Promise<{ - infos: Map; - 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 tryGetGroupMembersV2(groupQQ: string, modeListener = false, num = 30, timeout = 100): Promise<{ + // infos: Map; + // 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<{ - infos: Map; - 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 GetGroupMembersV3(groupQQ: string, num = 3000, timeout = 2500): Promise<{ + // infos: Map; + // 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> { - 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 getGroupMembersV2(groupQQ: string, num = 3000, no_cache: boolean = false): Promise> { + // 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> { - 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 getGroupMembers(groupQQ: string, num = 3000): Promise> { + // 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) { return this.context.session.getRichMediaService().batchGetGroupFileCount(group_ids); diff --git a/src/core/index.ts b/src/core/index.ts index 3b318054..3cdf6158 100644 --- a/src/core/index.ts +++ b/src/core/index.ts @@ -24,10 +24,10 @@ import path from 'node:path'; import fs from 'node:fs'; import { hostname, systemName, systemVersion } from '@/common/system'; 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 os from 'node:os'; -import { NodeIKernelGroupListener, NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners'; +import { NodeIKernelMsgListener, NodeIKernelProfileListener } from '@/core/listeners'; import { proxiedListenerOf } from '@/common/proxy-handler'; import { NTQQPacketApi } from './apis/packet'; export * from './wrapper'; @@ -163,7 +163,6 @@ export class NapCatCore { msgListener.onAddSendMsg = (msg) => { this.context.logger.logMessage(msg, this.selfInfo); }; - //await sleep(2500); this.context.session.getMsgService().addKernelMsgListener( proxiedListenerOf(msgListener, this.context.logger), ); @@ -185,92 +184,6 @@ export class NapCatCore { this.context.session.getProfileService().addKernelProfileListener( 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; } } diff --git a/src/onebot/action/group/GetGroupMemberList.ts b/src/onebot/action/group/GetGroupMemberList.ts index 451ae6e1..0f26d325 100644 --- a/src/onebot/action/group/GetGroupMemberList.ts +++ b/src/onebot/action/group/GetGroupMemberList.ts @@ -19,11 +19,10 @@ export class GetGroupMemberList extends OneBotAction const groupIdStr = payload.group_id.toString(); const noCache = payload.no_cache ? this.stringToBoolean(payload.no_cache) : false; const memberCache = this.core.apis.GroupApi.groupMemberCache; - let groupMembers; - try { - groupMembers = await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr, 3000, noCache); - } catch (error) { - groupMembers = memberCache.get(groupIdStr) ?? await this.core.apis.GroupApi.getGroupMembersV2(groupIdStr); + let groupMembers = memberCache.get(groupIdStr); + if (noCache || !groupMembers) { + groupMembers = (await this.core.apis.GroupApi.getGroupMemberAll(groupIdStr)).result.infos; + memberCache.set(groupIdStr, groupMembers); } const memberPromises = Array.from(groupMembers.values()).map(item => OB11Construct.groupMember(groupIdStr, item) diff --git a/src/onebot/api/group.ts b/src/onebot/api/group.ts index c760dfd9..0a57086d 100644 --- a/src/onebot/api/group.ts +++ b/src/onebot/api/group.ts @@ -7,15 +7,10 @@ import { MessageElement, NapCatCore, NTGrayTipElementSubTypeV2, - NTMsgType, RawMessage, - TipGroupElement, - TipGroupElementType, } from '@/core'; import { NapCatOneBot11Adapter } from '@/onebot'; 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 { OB11GroupMsgEmojiLikeEvent } from '@/onebot/event/notice/OB11MsgEmojiLikeEvent'; import { MessageUnique } from '@/common/message-unique'; @@ -66,67 +61,6 @@ export class OneBotGroupApi { 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( groupCode: string, grayTipElement: GrayTipElement @@ -187,31 +121,6 @@ export class OneBotGroupApi { 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) { const json = JSON.parse(jsonStr); diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index bf8f790c..1ff5ae8d 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -969,7 +969,7 @@ export class OneBotMsgApi { const SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg)); if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); - console.log(JSON.stringify(groupChange)); + await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()); return new OB11GroupIncreaseEvent( this.core, groupChange.groupUin, @@ -979,6 +979,7 @@ export class OneBotMsgApi { ); } else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); + await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()); return new OB11GroupDecreaseEvent( this.core, groupChange.groupUin, diff --git a/src/onebot/index.ts b/src/onebot/index.ts index 9d858d74..1eed43da 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -413,104 +413,73 @@ export class NapCatOneBot11Adapter { 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 ( - [GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS].includes(notify.type) && - notify.status == GroupNotifyMsgStatus.KUNHANDLE - ) { - this.context.logger.logDebug('有加群请求'); - try { - let requestUin = await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid); - if (isNaN(parseInt(requestUin))) { - requestUin = (await this.core.apis.UserApi.getUserDetailInfo(notify.user1.uid)).uin; - } - const groupRequestEvent = new OB11GroupRequestEvent( - this.core, - parseInt(notify.group.groupCode), - parseInt(requestUin), - 'add', - notify.postscript, - flag - ); - this.networkManager - .emitEvent(groupRequestEvent) - .catch((e) => - this.context.logger.logError('处理加群请求失败', e) - ); - } catch (e) { - this.context.logger.logError( - '获取加群人QQ号失败 Uid:', - notify.user1.uid, - e - ); + } else if ( + [GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS].includes(notify.type) && + notify.status == GroupNotifyMsgStatus.KUNHANDLE + ) { + this.context.logger.logDebug('有加群请求'); + try { + let requestUin = await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid); + if (isNaN(parseInt(requestUin))) { + requestUin = (await this.core.apis.UserApi.getUserDetailInfo(notify.user1.uid)).uin; } - } else if ( - notify.type == GroupNotifyMsgType.INVITED_BY_MEMBER && - notify.status == GroupNotifyMsgStatus.KUNHANDLE - ) { - this.context.logger.logDebug(`收到邀请我加群通知:${notify}`); - const groupInviteEvent = new OB11GroupRequestEvent( + const groupRequestEvent = new OB11GroupRequestEvent( this.core, parseInt(notify.group.groupCode), - parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid)), - 'invite', - notify.postscript, - flag - ); - this.networkManager - .emitEvent(groupInviteEvent) - .catch((e) => - this.context.logger.logError('处理邀请本人加群失败', e) - ); - } else if ( - notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS && - notify.status == GroupNotifyMsgStatus.KUNHANDLE - ) { - this.context.logger.logDebug(`收到群员邀请加群通知:${notify}`); - const groupInviteEvent = new OB11GroupRequestEvent( - this.core, - parseInt(notify.group.groupCode), - parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid)), + parseInt(requestUin), 'add', notify.postscript, flag ); this.networkManager - .emitEvent(groupInviteEvent) + .emitEvent(groupRequestEvent) .catch((e) => - this.context.logger.logError('处理邀请本人加群失败', e) + this.context.logger.logError('处理加群请求失败', e) ); + } catch (e) { + this.context.logger.logError( + '获取加群人QQ号失败 Uid:', + notify.user1.uid, + e + ); } + } else if ( + notify.type == GroupNotifyMsgType.INVITED_BY_MEMBER && + notify.status == GroupNotifyMsgStatus.KUNHANDLE + ) { + this.context.logger.logDebug(`收到邀请我加群通知:${notify}`); + const groupInviteEvent = new OB11GroupRequestEvent( + this.core, + parseInt(notify.group.groupCode), + parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid)), + 'invite', + notify.postscript, + flag + ); + this.networkManager + .emitEvent(groupInviteEvent) + .catch((e) => + this.context.logger.logError('处理邀请本人加群失败', e) + ); + } else if ( + notify.type == GroupNotifyMsgType.INVITED_NEED_ADMINI_STRATOR_PASS && + notify.status == GroupNotifyMsgStatus.KUNHANDLE + ) { + this.context.logger.logDebug(`收到群员邀请加群通知:${notify}`); + const groupInviteEvent = new OB11GroupRequestEvent( + this.core, + parseInt(notify.group.groupCode), + parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid)), + 'add', + notify.postscript, + flag + ); + this.networkManager + .emitEvent(groupInviteEvent) + .catch((e) => + this.context.logger.logError('处理邀请本人加群失败', e) + ); + } } } }; From ddadd381519d6eeb8de54a6e4dedc97a41b9b781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 19:44:38 +0800 Subject: [PATCH 12/57] refactor: GroupAdminChange --- src/core/apis/group.ts | 106 ------------------ .../transformer/proto/message/groupAdmin.ts | 18 +++ src/onebot/api/msg.ts | 20 +++- src/onebot/index.ts | 71 ------------ 4 files changed, 37 insertions(+), 178 deletions(-) create mode 100644 src/core/packet/transformer/proto/message/groupAdmin.ts diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index 31efa701..51868c65 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -260,29 +260,6 @@ export class NTQQGroupApi { 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; - // member = members.get(uid); - // } - // return member; - // } - async searchGroup(groupCode: string) { const [, ret] = await this.core.eventWrapper.callNormalEventV2( 'NodeIKernelSearchService/searchGroup', @@ -323,89 +300,6 @@ export class NTQQGroupApi { return undefined; } - // async tryGetGroupMembersV2(groupQQ: string, modeListener = false, num = 30, timeout = 100): Promise<{ - // infos: Map; - // 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<{ - // infos: Map; - // 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> { - // 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> { - // 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) { return this.context.session.getRichMediaService().batchGetGroupFileCount(group_ids); } diff --git a/src/core/packet/transformer/proto/message/groupAdmin.ts b/src/core/packet/transformer/proto/message/groupAdmin.ts new file mode 100644 index 00000000..0aa130b9 --- /dev/null +++ b/src/core/packet/transformer/proto/message/groupAdmin.ts @@ -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), +} \ No newline at end of file diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index 1ff5ae8d..c9ddd427 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -32,6 +32,8 @@ import { GroupChange, PushMsgBody } from "@/core/packet/transformer/proto"; import { NapProtoMsg } from '@napneko/nap-proto-core'; import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent'; import { OB11GroupDecreaseEvent, GroupDecreaseSubType } from '../event/notice/OB11GroupDecreaseEvent'; +import { GroupAdmin } from '@/core/packet/transformer/proto/message/groupAdmin'; +import { OB11GroupAdminNoticeEvent } from '../event/notice/OB11GroupAdminNoticeEvent'; type RawToOb11Converters = { [Key in keyof MessageElement as Key extends `${string}Element` ? Key : never]: ( @@ -965,8 +967,8 @@ export class OneBotMsgApi { } async parseSysMessage(msg: number[]) { - // Todo Refactor const SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg)); + console.log(Buffer.from(msg).toString('hex')); if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()); @@ -987,6 +989,22 @@ export class OneBotMsgApi { groupChange.operatorUid ? +await this.core.apis.UserApi.getUinByUidV2(groupChange.operatorUid) : 0, this.groupChangDecreseType2String(groupChange.decreaseType), ); + } else if (SysMessage.contentHead.type == 44 && SysMessage.body?.msgContent) { + const groupAmin = new NapProtoMsg(GroupAdmin).decode(SysMessage.body.msgContent); + await this.core.apis.GroupApi.refreshGroupMemberCache(groupAmin.groupUin.toString()); + let enabled = false; + let uid = groupAmin.body.extraEnable.adminUid; + if (groupAmin.body.extraEnable != null) { + enabled = true; + } else if (groupAmin.body.extraDisable != null) { + 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) { return await this.obContext.apis.UserApi.parseLikeEvent(SysMessage.body?.msgContent); } diff --git a/src/onebot/index.ts b/src/onebot/index.ts index 1eed43da..010c4177 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -1,8 +1,6 @@ import { BuddyReqType, ChatType, - DataSource, - NTGroupMemberRole, GroupNotifyMsgStatus, GroupNotifyMsgType, InstanceContext, @@ -41,8 +39,6 @@ import { OB11InputStatusEvent } from '@/onebot/event/notice/OB11InputStatusEvent import { MessageUnique } from '@/common/message-unique'; import { proxiedListenerOf } from '@/common/proxy-handler'; 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 { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent'; import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent'; @@ -373,47 +369,7 @@ export class NapCatOneBot11Adapter { const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type; 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 ( [GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS].includes(notify.type) && notify.status == GroupNotifyMsgStatus.KUNHANDLE ) { @@ -483,33 +439,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 .getGroupService() .addKernelGroupListener(proxiedListenerOf(groupListener, this.context.logger)); From 8a289d014e50584334c8982dc98eaceea0eb38b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 19:50:47 +0800 Subject: [PATCH 13/57] fix: error --- src/onebot/api/msg.ts | 27 +++------------------------ src/onebot/index.ts | 1 - 2 files changed, 3 insertions(+), 25 deletions(-) diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index c9ddd427..ace71a72 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -993,10 +993,12 @@ export class OneBotMsgApi { const groupAmin = new NapProtoMsg(GroupAdmin).decode(SysMessage.body.msgContent); await this.core.apis.GroupApi.refreshGroupMemberCache(groupAmin.groupUin.toString()); let enabled = false; - let uid = groupAmin.body.extraEnable.adminUid; + 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( @@ -1008,28 +1010,5 @@ export class OneBotMsgApi { } else if (SysMessage.contentHead.type == 528 && SysMessage.contentHead.subType == 39 && 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); - } - } - */ } } diff --git a/src/onebot/index.ts b/src/onebot/index.ts index 010c4177..85303d87 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -351,7 +351,6 @@ export class NapCatOneBot11Adapter { const groupListener = new NodeIKernelGroupListener(); groupListener.onGroupNotifiesUpdated = async (_, notifies) => { - //console.log('ob11 onGroupNotifiesUpdated', notifies[0]); await this.core.apis.GroupApi.clearGroupNotifiesUnreadCount(false); if ( ![ From c9122a3fee2b470ff1c00c5f73838f744b55e60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 20:55:24 +0800 Subject: [PATCH 14/57] =?UTF-8?q?fix:=20=E4=B8=B4=E6=97=B6=E7=9A=84?= =?UTF-8?q?=E6=8A=BD=E8=B1=A1=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/group.ts | 22 +++++++++++++------ src/core/apis/user.ts | 2 +- src/onebot/action/group/GetGroupMemberList.ts | 7 ++++-- src/onebot/api/msg.ts | 7 +++--- 4 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index 51868c65..b4a331ad 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -33,6 +33,7 @@ export class NTQQGroupApi { this.groups = await this.getGroups(); for (const group of this.groups) { this.groupCache.set(group.groupCode, group); + this.refreshGroupMemberCache(group.groupCode).then().catch(e => this.context.logger.logError(e)); } this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`); // process.pid 调试点 @@ -54,11 +55,13 @@ export class NTQQGroupApi { pageLimit: 300, }, pskey); } + async getGroupShutUpMemberList(groupCode: string) { const data = this.core.eventWrapper.registerListen('NodeIKernelGroupListener/onShutUpMemberListChanged', (group_id) => group_id === groupCode, 1, 1000); this.context.session.getGroupService().getGroupShutUpMemberList(groupCode); return (await data)[1]; } + async clearGroupNotifiesUnreadCount(uk: boolean) { return this.context.session.getGroupService().clearGroupNotifiesUnreadCount(uk); } @@ -139,26 +142,31 @@ export class NTQQGroupApi { async getGroupMemberAll(groupCode: string, forced = false) { return this.context.session.getGroupService().getAllMemberList(groupCode, forced); } + async refreshGroupMemberCache(groupCode: string) { try { const members = await this.getGroupMemberAll(groupCode, true); + let data = (await Promise.allSettled(members.result.ids.map(e => this.core.apis.UserApi.getUserDetailInfo(e.uid)))).filter(e => e.status === 'fulfilled').map(e => e.value); + data.forEach(e => { + const existingMember = members.result.infos.get(e.uid); + if (existingMember) { + members.result.infos.set(e.uid, { ...existingMember, ...e }); + } + }); this.groupMemberCache.set(groupCode, members.result.infos); } catch (e) { this.context.logger.logError(`刷新群成员缓存失败, ${e}`); } } + async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) { const groupCodeStr = groupCode.toString(); const memberUinOrUidStr = memberUinOrUid.toString(); let members = this.groupMemberCache.get(groupCodeStr); if (!members) { - try { - members = (await this.getGroupMemberAll(groupCodeStr)).result.infos; - this.groupMemberCache.set(groupCodeStr, members); - } catch (e) { - return null; - } + this.refreshGroupMemberCache(groupCodeStr); } + function getMember() { let member: GroupMember | undefined; if (isNumeric(memberUinOrUidStr)) { @@ -171,7 +179,7 @@ export class NTQQGroupApi { let member = getMember(); if (!member) { - members = members = (await this.getGroupMemberAll(groupCodeStr)).result.infos; + members = (await this.getGroupMemberAll(groupCodeStr)).result.infos; member = getMember(); } return member; diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index cbb62699..90c99c36 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -90,7 +90,7 @@ export class NTQQUserApi { }; return RetUser; } - + async getUserDetailInfo(uid: string): Promise { let retUser = await solveAsyncProblem(async (uid) => this.fetchUserDetailInfo(uid, UserDetailSource.KDB), uid); if (retUser && retUser.uin !== '0') { diff --git a/src/onebot/action/group/GetGroupMemberList.ts b/src/onebot/action/group/GetGroupMemberList.ts index 0f26d325..0bb3fda4 100644 --- a/src/onebot/action/group/GetGroupMemberList.ts +++ b/src/onebot/action/group/GetGroupMemberList.ts @@ -21,8 +21,11 @@ export class GetGroupMemberList extends OneBotAction const memberCache = this.core.apis.GroupApi.groupMemberCache; let groupMembers = memberCache.get(groupIdStr); if (noCache || !groupMembers) { - groupMembers = (await this.core.apis.GroupApi.getGroupMemberAll(groupIdStr)).result.infos; - memberCache.set(groupIdStr, groupMembers); + await this.core.apis.GroupApi.refreshGroupMemberCache(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 => OB11Construct.groupMember(groupIdStr, item) diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index ace71a72..c1b28f7b 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -968,10 +968,9 @@ export class OneBotMsgApi { async parseSysMessage(msg: number[]) { const SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg)); - console.log(Buffer.from(msg).toString('hex')); if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); - await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()); + await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch(); return new OB11GroupIncreaseEvent( this.core, groupChange.groupUin, @@ -981,7 +980,7 @@ export class OneBotMsgApi { ); } else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); - await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()); + this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch(); return new OB11GroupDecreaseEvent( this.core, groupChange.groupUin, @@ -991,7 +990,7 @@ export class OneBotMsgApi { ); } else if (SysMessage.contentHead.type == 44 && SysMessage.body?.msgContent) { const groupAmin = new NapProtoMsg(GroupAdmin).decode(SysMessage.body.msgContent); - await this.core.apis.GroupApi.refreshGroupMemberCache(groupAmin.groupUin.toString()); + await this.core.apis.GroupApi.refreshGroupMemberCache(groupAmin.groupUin.toString()).then().catch(); let enabled = false; let uid = ''; if (groupAmin.body.extraEnable != null) { From 501bbbe4df531fec59b3630a82d3029f726074c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 21:14:18 +0800 Subject: [PATCH 15/57] fix --- src/core/apis/group.ts | 52 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index b4a331ad..04b43e12 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -79,6 +79,35 @@ export class NTQQGroupApi { return groupList; } + async GetGroupMembersV3(groupQQ: string, num = 3000, timeout = 2500): Promise<{ + infos: Map; + 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 getGroupExtFE0Info(groupCode: string[], forced = true) { return this.context.session.getGroupService().getGroupExt0xEF0Info( groupCode, @@ -146,13 +175,22 @@ export class NTQQGroupApi { async refreshGroupMemberCache(groupCode: string) { try { const members = await this.getGroupMemberAll(groupCode, true); - let data = (await Promise.allSettled(members.result.ids.map(e => this.core.apis.UserApi.getUserDetailInfo(e.uid)))).filter(e => e.status === 'fulfilled').map(e => e.value); - data.forEach(e => { - const existingMember = members.result.infos.get(e.uid); - if (existingMember) { - members.result.infos.set(e.uid, { ...existingMember, ...e }); - } - }); + let groupData = (await this.GetGroupMembersV3(groupCode)).infos; + if (groupData.size === 0 || groupData.size !== members.result.infos.size) { + let data = (await Promise.allSettled(members.result.ids.map(e => this.core.apis.UserApi.getUserDetailInfo(e.uid)))).filter(e => e.status === 'fulfilled').map(e => e.value); + data.forEach(e => { + const existingMember = members.result.infos.get(e.uid); + if (existingMember) { + members.result.infos.set(e.uid, { ...existingMember, ...e }); + } + }); + } else { + groupData.forEach((v, k) => { + if (members.result.infos.has(k)) { + members.result.infos.set(k, { ...members.result.infos.get(k), ...v }); + } + }); + } this.groupMemberCache.set(groupCode, members.result.infos); } catch (e) { this.context.logger.logError(`刷新群成员缓存失败, ${e}`); From 94e9c87978f39fd508e671b0c2d768d07faa85e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 21:36:55 +0800 Subject: [PATCH 16/57] =?UTF-8?q?fix:=20=E4=BC=98=E5=8C=96=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/group.ts | 23 +++++++++++++---------- src/onebot/api/msg.ts | 4 ++-- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index 04b43e12..0653afdd 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -175,8 +175,13 @@ export class NTQQGroupApi { async refreshGroupMemberCache(groupCode: string) { try { const members = await this.getGroupMemberAll(groupCode, true); - let groupData = (await this.GetGroupMembersV3(groupCode)).infos; - if (groupData.size === 0 || groupData.size !== members.result.infos.size) { + // 首先填入基础信息 + const existingMembers = this.groupMemberCache.get(groupCode) ?? new Map(); + members.result.infos.forEach((value, key) => { + existingMembers.set(value.uid, { ...value, ...existingMembers.get(value.uid) }); + }); + // 后台补全复杂信息 + let event = (async () => { let data = (await Promise.allSettled(members.result.ids.map(e => this.core.apis.UserApi.getUserDetailInfo(e.uid)))).filter(e => e.status === 'fulfilled').map(e => e.value); data.forEach(e => { const existingMember = members.result.infos.get(e.uid); @@ -184,14 +189,12 @@ export class NTQQGroupApi { members.result.infos.set(e.uid, { ...existingMember, ...e }); } }); - } else { - groupData.forEach((v, k) => { - if (members.result.infos.has(k)) { - members.result.infos.set(k, { ...members.result.infos.get(k), ...v }); - } - }); + this.groupMemberCache.set(groupCode, members.result.infos); + })().then().catch(e => this.context.logger.logError(e)); + // 处理首次空缺 + if (!this.groupMemberCache.get(groupCode)) { + await event; } - this.groupMemberCache.set(groupCode, members.result.infos); } catch (e) { this.context.logger.logError(`刷新群成员缓存失败, ${e}`); } @@ -202,7 +205,7 @@ export class NTQQGroupApi { const memberUinOrUidStr = memberUinOrUid.toString(); let members = this.groupMemberCache.get(groupCodeStr); if (!members) { - this.refreshGroupMemberCache(groupCodeStr); + await this.refreshGroupMemberCache(groupCodeStr); } function getMember() { diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index c1b28f7b..a9decfea 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -970,7 +970,7 @@ export class OneBotMsgApi { const SysMessage = new NapProtoMsg(PushMsgBody).decode(Uint8Array.from(msg)); if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); - await this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch(); + this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch(); return new OB11GroupIncreaseEvent( this.core, groupChange.groupUin, @@ -990,7 +990,7 @@ export class OneBotMsgApi { ); } else if (SysMessage.contentHead.type == 44 && SysMessage.body?.msgContent) { const groupAmin = new NapProtoMsg(GroupAdmin).decode(SysMessage.body.msgContent); - await this.core.apis.GroupApi.refreshGroupMemberCache(groupAmin.groupUin.toString()).then().catch(); + this.core.apis.GroupApi.refreshGroupMemberCache(groupAmin.groupUin.toString()).then().catch(); let enabled = false; let uid = ''; if (groupAmin.body.extraEnable != null) { From 03f7d4673f18240bc3733f6eb33e420063e544e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 21:41:53 +0800 Subject: [PATCH 17/57] fix: code --- src/core/apis/group.ts | 35 +++++------------------------------ 1 file changed, 5 insertions(+), 30 deletions(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index 0653afdd..8a5eb9a1 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -79,35 +79,6 @@ export class NTQQGroupApi { return groupList; } - async GetGroupMembersV3(groupQQ: string, num = 3000, timeout = 2500): Promise<{ - infos: Map; - 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 getGroupExtFE0Info(groupCode: string[], forced = true) { return this.context.session.getGroupService().getGroupExt0xEF0Info( groupCode, @@ -203,6 +174,8 @@ export class NTQQGroupApi { async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) { const groupCodeStr = groupCode.toString(); const memberUinOrUidStr = memberUinOrUid.toString(); + + // 检查群缓存 let members = this.groupMemberCache.get(groupCodeStr); if (!members) { await this.refreshGroupMemberCache(groupCodeStr); @@ -218,9 +191,11 @@ export class NTQQGroupApi { return member; } + let member = getMember(); + // 不存在群友缓存 尝试刷新 if (!member) { - members = (await this.getGroupMemberAll(groupCodeStr)).result.infos; + await this.refreshGroupMemberCache(groupCode.toString()); member = getMember(); } return member; From ef01dd0d777246966b463e71ad7afb19da70e7ca Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Tue, 3 Dec 2024 13:43:37 +0000 Subject: [PATCH 18/57] release: v4.2.13 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 25b02629..cc47ff21 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.12", + "version": "4.2.13", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index 5a5afd73..7285cf21 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.12", + "version": "4.2.13", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 1fe967bf..d38f6892 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.12'; +export const napCatVersion = '4.2.13'; From da7636e60c376db1d962457bca4bb5670a302960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 21:50:12 +0800 Subject: [PATCH 19/57] fix: #592 --- src/onebot/action/extends/GetGroupAddRequest.ts | 2 +- src/onebot/action/group/GetGroupIgnoredNotifies.ts | 2 +- src/onebot/action/system/GetSystemMsg.ts | 6 +++--- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/onebot/action/extends/GetGroupAddRequest.ts b/src/onebot/action/extends/GetGroupAddRequest.ts index c886ed2f..8bdd65d8 100644 --- a/src/onebot/action/extends/GetGroupAddRequest.ts +++ b/src/onebot/action/extends/GetGroupAddRequest.ts @@ -18,7 +18,7 @@ export default class GetGroupAddRequest extends OneBotAction notify.type === 7) .map(async SSNotify => ({ - request_id: SSNotify.seq, + request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type, requester_uin: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1?.uid), requester_nick: SSNotify.user1?.nickName, group_id: SSNotify.group?.groupCode, diff --git a/src/onebot/action/group/GetGroupIgnoredNotifies.ts b/src/onebot/action/group/GetGroupIgnoredNotifies.ts index 0faa1826..a639ed1e 100644 --- a/src/onebot/action/group/GetGroupIgnoredNotifies.ts +++ b/src/onebot/action/group/GetGroupIgnoredNotifies.ts @@ -11,7 +11,7 @@ export class GetGroupIgnoredNotifies extends OneBotAction { ignoredNotifies .filter(notify => notify.type === 7) .map(async SSNotify => ({ - request_id: SSNotify.seq, + request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type, requester_uin: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1?.uid), requester_nick: SSNotify.user1?.nickName, group_id: SSNotify.group?.groupCode, diff --git a/src/onebot/action/system/GetSystemMsg.ts b/src/onebot/action/system/GetSystemMsg.ts index 435d17bb..f1a904c5 100644 --- a/src/onebot/action/system/GetSystemMsg.ts +++ b/src/onebot/action/system/GetSystemMsg.ts @@ -8,12 +8,12 @@ export class GetGroupSystemMsg extends OneBotAction { const NTQQUserApi = this.core.apis.UserApi; const NTQQGroupApi = this.core.apis.GroupApi; // 默认10条 该api未完整实现 包括响应数据规范化 类型规范化 - const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false,10); + const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false, 10); const retData: any = { InvitedRequest: [], join_requests: [] }; for (const SSNotify of SingleScreenNotifies) { if (SSNotify.type == 1) { 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_nick: SSNotify.user1?.nickName, group_id: SSNotify.group?.groupCode, @@ -23,7 +23,7 @@ export class GetGroupSystemMsg extends OneBotAction { }); } else if (SSNotify.type == 7) { 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), requester_nick: SSNotify.user1?.nickName, group_id: SSNotify.group?.groupCode, From ad052821b0f2419e07b767d98b6a550043e3b191 Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Tue, 3 Dec 2024 13:50:37 +0000 Subject: [PATCH 20/57] release: v4.2.14 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index cc47ff21..57748137 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.13", + "version": "4.2.14", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index 7285cf21..a0f5446d 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.13", + "version": "4.2.14", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index d38f6892..776bf78b 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.13'; +export const napCatVersion = '4.2.14'; From 6b0d96fe8d297a7c090ba45d577c048ccc530aaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 22:07:11 +0800 Subject: [PATCH 21/57] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E5=A4=8D?= =?UTF-8?q?=E6=9D=82=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/group.ts | 42 ++++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index 8a5eb9a1..b0dd6dae 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -146,31 +146,33 @@ export class NTQQGroupApi { async refreshGroupMemberCache(groupCode: string) { try { const members = await this.getGroupMemberAll(groupCode, true); - // 首先填入基础信息 - const existingMembers = this.groupMemberCache.get(groupCode) ?? new Map(); - members.result.infos.forEach((value, key) => { + let existingMembers = this.groupMemberCache.get(groupCode); + if (!existingMembers) { + existingMembers = new Map(); + this.groupMemberCache.set(groupCode, existingMembers); + } + members.result.infos.forEach((value) => { existingMembers.set(value.uid, { ...value, ...existingMembers.get(value.uid) }); }); - // 后台补全复杂信息 - let event = (async () => { - let data = (await Promise.allSettled(members.result.ids.map(e => this.core.apis.UserApi.getUserDetailInfo(e.uid)))).filter(e => e.status === 'fulfilled').map(e => e.value); - data.forEach(e => { - const existingMember = members.result.infos.get(e.uid); - if (existingMember) { - members.result.infos.set(e.uid, { ...existingMember, ...e }); - } - }); - this.groupMemberCache.set(groupCode, members.result.infos); - })().then().catch(e => this.context.logger.logError(e)); - // 处理首次空缺 - if (!this.groupMemberCache.get(groupCode)) { - await event; - } } catch (e) { - this.context.logger.logError(`刷新群成员缓存失败, ${e}`); + this.context.logger.logError(`刷新群成员缓存失败, 群号: ${groupCode}, 错误: ${e}`); } } - + // 后台补全复杂信息 + // let event = (async () => { + // let data = (await Promise.allSettled(members.result.ids.map(e => this.core.apis.UserApi.getUserDetailInfo(e.uid)))).filter(e => e.status === 'fulfilled').map(e => e.value); + // data.forEach(e => { + // const existingMember = members.result.infos.get(e.uid); + // if (existingMember) { + // members.result.infos.set(e.uid, { ...existingMember, ...e }); + // } + // }); + // this.groupMemberCache.set(groupCode, members.result.infos); + // })().then().catch(e => this.context.logger.logError(e)); + // 处理首次空缺 + // if (!this.groupMemberCache.get(groupCode)) { + // await event; + // } async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) { const groupCodeStr = groupCode.toString(); const memberUinOrUidStr = memberUinOrUid.toString(); From 52efb4f9ef673d358681968518b0b2eff68ac74e Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Tue, 3 Dec 2024 14:07:35 +0000 Subject: [PATCH 22/57] release: v4.2.15 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 57748137..32b0fbde 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.14", + "version": "4.2.15", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index a0f5446d..cb2d57e0 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.14", + "version": "4.2.15", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 776bf78b..7e6fde4b 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.14'; +export const napCatVersion = '4.2.15'; From aceece7e90ecc6dca50ea8dbf4ab75a6ad7e4cdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Tue, 3 Dec 2024 22:12:57 +0800 Subject: [PATCH 23/57] fix: code --- src/core/apis/group.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index b0dd6dae..ca03366f 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -157,6 +157,7 @@ export class NTQQGroupApi { } catch (e) { this.context.logger.logError(`刷新群成员缓存失败, 群号: ${groupCode}, 错误: ${e}`); } + return this.groupMemberCache; } // 后台补全复杂信息 // let event = (async () => { @@ -180,7 +181,7 @@ export class NTQQGroupApi { // 检查群缓存 let members = this.groupMemberCache.get(groupCodeStr); if (!members) { - await this.refreshGroupMemberCache(groupCodeStr); + members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr); } function getMember() { From a3c71473aeadcd5c804bca333a4a4067c9cecdf7 Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Tue, 3 Dec 2024 14:15:16 +0000 Subject: [PATCH 24/57] release: v4.2.16 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 32b0fbde..a8b4b262 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.15", + "version": "4.2.16", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index cb2d57e0..cc8c4c22 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.15", + "version": "4.2.16", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 7e6fde4b..a256536f 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.15'; +export const napCatVersion = '4.2.16'; From 32778acf57986cc066246f048151441abde88b6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 10:43:48 +0800 Subject: [PATCH 25/57] refactor: NTQQGroupApi --- src/core/apis/group.ts | 163 ++++++++----------- src/core/apis/user.ts | 9 + src/core/services/NodeIKernelGroupService.ts | 2 +- src/onebot/api/msg.ts | 2 + 4 files changed, 84 insertions(+), 92 deletions(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index ca03366f..41fa1057 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -18,33 +18,23 @@ export class NTQQGroupApi { core: NapCatCore; groupCache: Map = new Map(); groupMemberCache: Map> = new Map>(); - groups: Group[] = []; essenceLRU = new LimitedHashTable(1000); - session: any; constructor(context: InstanceContext, core: NapCatCore) { this.context = context; this.core = core; } + async initApi() { 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.refreshGroupMemberCache(group.groupCode).then().catch(e => this.context.logger.logError(e)); - } - this.context.logger.logDebug(`加载${this.groups.length}个群组缓存完成`); - // process.pid 调试点 - } - async getCoreAndBaseInfo(uids: string[]) { - return await this.core.eventWrapper.callNoListenerEvent( - 'NodeIKernelProfileService/getCoreAndBaseInfo', - 'nodeStore', - uids, - ); + async initCache() { + await this.core.apis.GroupApi.refreshGroups(); + for (const group of this.groupCache.keys()) { + await this.refreshGroupMemberCache(group); + } + this.context.logger.logDebug(`加载${this.groupCache.size}个群组缓存完成`); } async fetchGroupEssenceList(groupCode: string) { @@ -62,15 +52,15 @@ export class NTQQGroupApi { 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) { - return this.context.session.getGroupService().setHeader(gc, filePath); + async setGroupAvatar(groupCode: string, filePath: string) { + return this.context.session.getGroupService().setHeader(groupCode, filePath); } - async getGroups(forced = false) { + async getGroups(forced: boolean = false) { const [, , groupList] = await this.core.eventWrapper.callNormalEventV2( 'NodeIKernelGroupService/getGroupList', 'NodeIKernelGroupListener/onGroupListUpdate', @@ -79,9 +69,9 @@ export class NTQQGroupApi { return groupList; } - async getGroupExtFE0Info(groupCode: string[], forced = true) { + async getGroupExtFE0Info(groupCodes: Array, forced = true) { return this.context.session.getGroupService().getGroupExt0xEF0Info( - groupCode, + groupCodes, [], { bindGuildId: 1, @@ -143,6 +133,15 @@ export class NTQQGroupApi { return this.context.session.getGroupService().getAllMemberList(groupCode, forced); } + async refreshGroups() { + let groups = await this.getGroups(true); + let tempGroupCache = new Map(); + for (const group of groups) { + tempGroupCache.set(group.groupCode, group); + } + this.groupCache = tempGroupCache; + } + async refreshGroupMemberCache(groupCode: string) { try { const members = await this.getGroupMemberAll(groupCode, true); @@ -159,46 +158,29 @@ export class NTQQGroupApi { } return this.groupMemberCache; } - // 后台补全复杂信息 - // let event = (async () => { - // let data = (await Promise.allSettled(members.result.ids.map(e => this.core.apis.UserApi.getUserDetailInfo(e.uid)))).filter(e => e.status === 'fulfilled').map(e => e.value); - // data.forEach(e => { - // const existingMember = members.result.infos.get(e.uid); - // if (existingMember) { - // members.result.infos.set(e.uid, { ...existingMember, ...e }); - // } - // }); - // this.groupMemberCache.set(groupCode, members.result.infos); - // })().then().catch(e => this.context.logger.logError(e)); - // 处理首次空缺 - // if (!this.groupMemberCache.get(groupCode)) { - // await event; - // } + async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) { const groupCodeStr = groupCode.toString(); const memberUinOrUidStr = memberUinOrUid.toString(); - // 检查群缓存 + // 获取群成员缓存 let members = this.groupMemberCache.get(groupCodeStr); if (!members) { members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr); } - function getMember() { - let member: GroupMember | undefined; + const getMember = () => { if (isNumeric(memberUinOrUidStr)) { - member = Array.from(members!.values()).find(member => member.uin === memberUinOrUidStr); + return Array.from(members!.values()).find(member => member.uin === memberUinOrUidStr); } else { - member = members!.get(memberUinOrUidStr); + return members!.get(memberUinOrUidStr); } - return member; - } - + }; let member = getMember(); - // 不存在群友缓存 尝试刷新 + // 如果缓存中不存在该成员,尝试刷新缓存 if (!member) { - await this.refreshGroupMemberCache(groupCode.toString()); + members = (await this.refreshGroupMemberCache(groupCodeStr)).get(groupCodeStr); member = getMember(); } return member; @@ -212,7 +194,7 @@ export class NTQQGroupApi { return this.context.session.getRichMediaService().createGroupFolder(groupCode, folderName); } - async DelGroupFile(groupCode: string, files: string[]) { + async DelGroupFile(groupCode: string, files: Array) { return this.context.session.getRichMediaService().deleteGroupFile(groupCode, [102], files); } @@ -220,14 +202,14 @@ export class NTQQGroupApi { 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({ chatType: 2, guildId: '', - peerUid: GroupCode, + peerUid: groupCode, }, msgId, 1, false); const param = { - groupCode: GroupCode, + groupCode: groupCode, msgRandom: parseInt(MsgData.msgList[0].msgRandom), msgSeq: parseInt(MsgData.msgList[0].msgSeq), }; @@ -238,9 +220,9 @@ export class NTQQGroupApi { 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')!; - return this.context.session.getGroupService().deleteGroupBulletin(GroupCode, psKey, noticeId); + return this.context.session.getGroupService().deleteGroupBulletin(groupCode, psKey, noticeId); } async quitGroupV2(GroupCode: string, needDeleteLocalMsg: boolean) { @@ -251,37 +233,37 @@ export class NTQQGroupApi { 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 = { - groupCode: GroupCode, + groupCode: groupCode, msgRandom: parseInt(msgRandom), msgSeq: parseInt(msgSeq), }; 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({ chatType: 2, guildId: '', - peerUid: GroupCode, + peerUid: groupCode, }, msgId, 1, false); const param = { - groupCode: GroupCode, + groupCode: groupCode, msgRandom: parseInt(MsgData.msgList[0].msgRandom), msgSeq: parseInt(MsgData.msgList[0].msgSeq), }; 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( 'NodeIKernelGroupService/getSingleScreenNotifies', 'NodeIKernelGroupListener/onGroupSingleScreenNotifies', [ doubt, '', - num, + count, ], ); return notifies; @@ -305,44 +287,43 @@ export class NTQQGroupApi { 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) => { return eventWrapper.callNormalEventV2( 'NodeIKernelGroupService/getMemberInfo', 'NodeIKernelGroupListener/onMemberInfoChange', - [GroupCode, [uid], forced], + [groupCode, [uid], forced], (ret) => ret.result === 0, (params, _, members) => params === GroupCode && members.size > 0 && members.has(uid), 1, 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)) { return data[3].get(uid); } 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; } return undefined; } - async getGroupFileCount(group_ids: Array) { - return this.context.session.getRichMediaService().batchGetGroupFileCount(group_ids); + async getGroupFileCount(groupCodes: Array) { + return this.context.session.getRichMediaService().batchGetGroupFileCount(groupCodes); } - async getArkJsonGroupShare(GroupCode: string) { + async getArkJsonGroupShare(groupCode: string) { const ret = await this.core.eventWrapper.callNoListenerEvent( 'NodeIKernelGroupService/getGroupRecommendContactArkJson', - GroupCode, + groupCode, ) as GeneralCallResult & { arkJson: string }; 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')!; - 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) { @@ -364,36 +345,36 @@ export class NTQQGroupApi { }); } - async quitGroup(groupQQ: string) { - return this.context.session.getGroupService().quitGroup(groupQQ); + async quitGroup(groupCode: string) { + return this.context.session.getGroupService().quitGroup(groupCode); } - async kickMember(groupQQ: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') { - return this.context.session.getGroupService().kickMember(groupQQ, kickUids, refuseForever, kickReason); + async kickMember(groupCode: string, kickUids: string[], refuseForever: boolean = false, kickReason: string = '') { + 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为解除禁言 - return this.context.session.getGroupService().setMemberShutUp(groupQQ, memList); + return this.context.session.getGroupService().setMemberShutUp(groupCode, memList); } - async banGroup(groupQQ: string, shutUp: boolean) { - return this.context.session.getGroupService().setGroupShutUp(groupQQ, shutUp); + async banGroup(groupCode: string, shutUp: boolean) { + return this.context.session.getGroupService().setGroupShutUp(groupCode, shutUp); } - async setMemberCard(groupQQ: string, memberUid: string, cardName: string) { - return this.context.session.getGroupService().modifyMemberCardName(groupQQ, memberUid, cardName); + async setMemberCard(groupCode: string, memberUid: string, cardName: string) { + return this.context.session.getGroupService().modifyMemberCardName(groupCode, memberUid, cardName); } - async setMemberRole(groupQQ: string, memberUid: string, role: NTGroupMemberRole) { - return this.context.session.getGroupService().modifyMemberRole(groupQQ, memberUid, role); + async setMemberRole(groupCode: string, memberUid: string, role: NTGroupMemberRole) { + return this.context.session.getGroupService().modifyMemberRole(groupCode, memberUid, role); } - async setGroupName(groupQQ: string, groupName: string) { - return this.context.session.getGroupService().modifyGroupName(groupQQ, groupName, false); + async setGroupName(groupCode: string, groupName: string) { + 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, width: number, height: number @@ -407,11 +388,11 @@ export class NTQQGroupApi { pinned: pinned, confirmRequired: confirmRequired, }; - return this.context.session.getGroupService().publishGroupBulletin(groupQQ, psKey!, data); + return this.context.session.getGroupService().publishGroupBulletin(groupCode, psKey!, data); } - async getGroupRemainAtTimes(GroupCode: string) { - return this.context.session.getGroupService().getGroupRemainAtTimes(GroupCode); + async getGroupRemainAtTimes(groupCode: string) { + return this.context.session.getGroupService().getGroupRemainAtTimes(groupCode); } async getMemberExtInfo(groupCode: string, uin: string) { diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index 90c99c36..99d437ea 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -18,6 +18,15 @@ export class NTQQUserApi { async getStatusByUid(uid: string) { return this.context.session.getProfileService().getStatus(uid); } + + async getCoreAndBaseInfo(uids: string[]) { + return await this.core.eventWrapper.callNoListenerEvent( + 'NodeIKernelProfileService/getCoreAndBaseInfo', + 'nodeStore', + uids, + ); + } + // 默认获取自己的 type = 2 获取别人 type = 1 async getProfileLike(uid: string, start: number, count: number, type: number = 2) { return this.context.session.getProfileLikeService().getBuddyProfileLike({ diff --git a/src/core/services/NodeIKernelGroupService.ts b/src/core/services/NodeIKernelGroupService.ts index 61f3179a..83a9cc92 100644 --- a/src/core/services/NodeIKernelGroupService.ts +++ b/src/core/services/NodeIKernelGroupService.ts @@ -193,7 +193,7 @@ export interface NodeIKernelGroupService { getGroupNotifiesUnreadCount(unknown: boolean): Promise; - clearGroupNotifiesUnreadCount(unknown: boolean): void; + clearGroupNotifiesUnreadCount(doubt: boolean): void; operateSysNotify( doubt: boolean, diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index a9decfea..7af4f11f 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -971,6 +971,7 @@ export class OneBotMsgApi { if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch(); + this.core.apis.GroupApi.refreshGroups().then().catch(); return new OB11GroupIncreaseEvent( this.core, groupChange.groupUin, @@ -981,6 +982,7 @@ export class OneBotMsgApi { } else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch(); + this.core.apis.GroupApi.refreshGroups().then().catch(); return new OB11GroupDecreaseEvent( this.core, groupChange.groupUin, From 1ce8be3c7e1bc73df57ab611b8842fcc40f76ed6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 10:47:21 +0800 Subject: [PATCH 26/57] =?UTF-8?q?fix:=20=E7=B2=BE=E7=AE=80GroupApi?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/group.ts | 35 +++-------------------------------- src/onebot/api/msg.ts | 2 -- 2 files changed, 3 insertions(+), 34 deletions(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index 41fa1057..fd347c3e 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -16,7 +16,6 @@ import { NTEventWrapper } from '@/common/event'; export class NTQQGroupApi { context: InstanceContext; core: NapCatCore; - groupCache: Map = new Map(); groupMemberCache: Map> = new Map>(); essenceLRU = new LimitedHashTable(1000); @@ -24,17 +23,15 @@ export class NTQQGroupApi { this.context = context; this.core = core; } - + async initApi() { this.initCache().then().catch(e => this.context.logger.logError(e)); } async initCache() { - await this.core.apis.GroupApi.refreshGroups(); - for (const group of this.groupCache.keys()) { - await this.refreshGroupMemberCache(group); + for (const group of await this.getGroups(true)) { + this.refreshGroupMemberCache(group.groupCode).then().catch(); } - this.context.logger.logDebug(`加载${this.groupCache.size}个群组缓存完成`); } async fetchGroupEssenceList(groupCode: string) { @@ -111,36 +108,10 @@ 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) { return this.context.session.getGroupService().getAllMemberList(groupCode, forced); } - async refreshGroups() { - let groups = await this.getGroups(true); - let tempGroupCache = new Map(); - for (const group of groups) { - tempGroupCache.set(group.groupCode, group); - } - this.groupCache = tempGroupCache; - } async refreshGroupMemberCache(groupCode: string) { try { diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index 7af4f11f..a9decfea 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -971,7 +971,6 @@ export class OneBotMsgApi { if (SysMessage.contentHead.type == 33 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch(); - this.core.apis.GroupApi.refreshGroups().then().catch(); return new OB11GroupIncreaseEvent( this.core, groupChange.groupUin, @@ -982,7 +981,6 @@ export class OneBotMsgApi { } else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch(); - this.core.apis.GroupApi.refreshGroups().then().catch(); return new OB11GroupDecreaseEvent( this.core, groupChange.groupUin, From 41748c0b3f1956974dfacb860515c641be47ed3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 10:55:56 +0800 Subject: [PATCH 27/57] =?UTF-8?q?refactor:=20=E6=95=B0=E6=8D=AE=E6=B8=85?= =?UTF-8?q?=E7=90=86=E4=B8=8E=E5=88=B7=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/group.ts | 19 +++++-------------- .../action/go-cqhttp/CreateGroupFileFolder.ts | 2 +- .../action/go-cqhttp/DeleteGroupFile.ts | 2 +- .../action/go-cqhttp/DeleteGroupFileFolder.ts | 2 +- src/onebot/api/msg.ts | 9 ++++++++- 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index fd347c3e..35674530 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -1,6 +1,5 @@ import { GeneralCallResult, - Group, GroupMember, NTGroupMemberRole, NTGroupRequestOperateTypes, @@ -112,24 +111,16 @@ export class NTQQGroupApi { return this.context.session.getGroupService().getAllMemberList(groupCode, forced); } - async refreshGroupMemberCache(groupCode: string) { try { const members = await this.getGroupMemberAll(groupCode, true); - let existingMembers = this.groupMemberCache.get(groupCode); - if (!existingMembers) { - existingMembers = new Map(); - this.groupMemberCache.set(groupCode, existingMembers); - } - members.result.infos.forEach((value) => { - existingMembers.set(value.uid, { ...value, ...existingMembers.get(value.uid) }); - }); + 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) { const groupCodeStr = groupCode.toString(); const memberUinOrUidStr = memberUinOrUid.toString(); @@ -161,15 +152,15 @@ export class NTQQGroupApi { 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); } - async DelGroupFile(groupCode: string, files: Array) { + async delGroupFile(groupCode: string, files: Array) { 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); } diff --git a/src/onebot/action/go-cqhttp/CreateGroupFileFolder.ts b/src/onebot/action/go-cqhttp/CreateGroupFileFolder.ts index 01af44c6..3764f148 100644 --- a/src/onebot/action/go-cqhttp/CreateGroupFileFolder.ts +++ b/src/onebot/action/go-cqhttp/CreateGroupFileFolder.ts @@ -13,6 +13,6 @@ export class CreateGroupFileFolder extends OneBotAction { actionName = ActionName.GoCQHTTP_CreateGroupFileFolder; payloadSchema = SchemaData; 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; } } diff --git a/src/onebot/action/go-cqhttp/DeleteGroupFile.ts b/src/onebot/action/go-cqhttp/DeleteGroupFile.ts index 244a5368..85b43cce 100644 --- a/src/onebot/action/go-cqhttp/DeleteGroupFile.ts +++ b/src/onebot/action/go-cqhttp/DeleteGroupFile.ts @@ -17,6 +17,6 @@ export class DeleteGroupFile extends OneBotAction { async _handle(payload: Payload) { const data = FileNapCatOneBotUUID.decodeModelId(payload.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]); } } diff --git a/src/onebot/action/go-cqhttp/DeleteGroupFileFolder.ts b/src/onebot/action/go-cqhttp/DeleteGroupFileFolder.ts index c6e20df9..f8a813a4 100644 --- a/src/onebot/action/go-cqhttp/DeleteGroupFileFolder.ts +++ b/src/onebot/action/go-cqhttp/DeleteGroupFileFolder.ts @@ -14,7 +14,7 @@ export class DeleteGroupFileFolder extends OneBotAction { actionName = ActionName.GoCQHTTP_DeleteGroupFileFolder; payloadSchema = SchemaData; 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; } } diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index a9decfea..94c99fd1 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -980,7 +980,14 @@ export class OneBotMsgApi { ); } else if (SysMessage.contentHead.type == 34 && SysMessage.body?.msgContent) { const groupChange = new NapProtoMsg(GroupChange).decode(SysMessage.body.msgContent); - this.core.apis.GroupApi.refreshGroupMemberCache(groupChange.groupUin.toString()).then().catch(); + 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( this.core, groupChange.groupUin, From 6cf047af3953ea08f321531c2c1d5b9f02e5fadd Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Wed, 4 Dec 2024 02:57:21 +0000 Subject: [PATCH 28/57] release: v4.2.17 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index a8b4b262..f5a4713e 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.16", + "version": "4.2.17", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index cc8c4c22..84772ddc 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.16", + "version": "4.2.17", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index a256536f..95c83215 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.16'; +export const napCatVersion = '4.2.17'; From a482fa3a8db74767770cd3a1c04bddfd1ae534f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 11:38:59 +0800 Subject: [PATCH 29/57] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E8=B0=83?= =?UTF-8?q?=E6=95=B4=E6=96=87=E4=BB=B6=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/file.ts | 113 ++++-------------- src/core/apis/file.ts | 26 ++-- src/core/helper/msg.ts | 4 +- src/onebot/action/extends/OCRImage.ts | 4 +- src/onebot/action/extends/SetQQAvatar.ts | 4 +- .../action/go-cqhttp/SendGroupNotice.ts | 4 +- .../action/go-cqhttp/SetGroupPortrait.ts | 4 +- .../action/go-cqhttp/UploadGroupFile.ts | 4 +- .../action/go-cqhttp/UploadPrivateFile.ts | 4 +- src/onebot/action/group/SendGroupAiRecord.ts | 4 +- src/onebot/api/msg.ts | 8 +- 11 files changed, 55 insertions(+), 124 deletions(-) diff --git a/src/common/file.ts b/src/common/file.ts index cab8abb0..aa1a019e 100644 --- a/src/common/file.ts +++ b/src/common/file.ts @@ -1,9 +1,7 @@ import fs from 'fs'; import { stat } from 'fs/promises'; import crypto, { randomUUID } from 'crypto'; -import util from 'util'; import path from 'node:path'; -import * as fileType from 'file-type'; import { solveProblem } from '@/common/helper'; export interface HttpDownloadOptions { @@ -15,7 +13,6 @@ type Uri2LocalRes = { success: boolean, errMsg: string, fileName: string, - ext: string, path: string } @@ -73,27 +70,6 @@ async function checkFile(path: string): Promise { // 如果文件存在,则无需做任何事情,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 { return new Promise((resolve, reject) => { // 创建一个流式读取器 @@ -160,20 +136,6 @@ export async function httpDownload(options: string | HttpDownloadOptions): Promi 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 { Unknown = 0, Local = 1, @@ -213,63 +175,32 @@ export async function checkUriType(Uri: string) { return { Uri: Uri, Type: FileUriType.Unknown }; } -export async function uri2local(dir: string, uri: string, filename: string | undefined = undefined): Promise { +export async function uriToLocalFile(dir: string, uri: string): Promise { const { Uri: HandledUri, Type: UriType } = await checkUriType(uri); - //解析失败 - const tempName = randomUUID(); - if (!filename) filename = randomUUID(); + const filename = randomUUID(); + const filePath = path.join(dir, filename); - //解析Http和Https协议 - if (UriType == FileUriType.Unknown) { - return { success: false, errMsg: `未知文件类型, uri= ${uri}`, fileName: '', ext: '', path: '' }; - } + switch (UriType) { + case FileUriType.Local: + const fileExt = path.extname(HandledUri); + const localFileName = path.basename(HandledUri, fileExt) + fileExt; + const tempFilePath = path.join(dir, filename + fileExt); + fs.copyFileSync(HandledUri, tempFilePath); + return { success: true, errMsg: '', fileName: localFileName, path: tempFilePath }; - //解析File协议和本地文件 - if (UriType == FileUriType.Local) { - const fileExt = path.extname(HandledUri); - let filename = path.basename(HandledUri, fileExt); - filename += fileExt; - //复制文件到临时文件并保持后缀 - const filenameTemp = tempName + fileExt; - const filePath = path.join(dir, filenameTemp); - fs.copyFileSync(HandledUri, filePath); - return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; - } + case FileUriType.Remote: + const buffer = await httpDownload(HandledUri); + fs.writeFileSync(filePath, buffer, { flag: 'wx' }); + return { success: true, errMsg: '', fileName: filename, path: filePath }; - //接下来都要有文件名 - 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); - //没有文件就创建 - fs.writeFileSync(filePath, buffer, { flag: 'wx' }); - return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; - } + case FileUriType.Base64: + const base64 = HandledUri.replace(/^base64:\/\//, ''); + const base64Buffer = Buffer.from(base64, 'base64'); + fs.writeFileSync(filePath, base64Buffer, { flag: 'wx' }); + return { success: true, errMsg: '', fileName: filename, path: filePath }; - //解析Base64 - if (UriType == FileUriType.Base64) { - const base64 = HandledUri.replace(/^base64:\/\//, ''); - const buffer = Buffer.from(base64, 'base64'); - let filePath = path.join(dir, filename); - let fileExt = ''; - fs.writeFileSync(filePath, buffer); - const { success, ext, path: fileTypePath } = await checkFileV2(filePath); - if (success) { - filePath = fileTypePath; - fileExt = ext; - filename = filename + '.' + ext; - } - return { success: true, errMsg: '', fileName: filename, ext: fileExt, path: filePath }; + default: + return { success: false, errMsg: `识别URL失败, uri= ${uri}`, fileName: '', path: '' }; } - return { success: false, errMsg: `未知文件类型, uri= ${uri}`, fileName: '', ext: '', path: '' }; -} +} \ No newline at end of file diff --git a/src/core/apis/file.ts b/src/core/apis/file.ts index 07860ca8..eb58c916 100644 --- a/src/core/apis/file.ts +++ b/src/core/apis/file.ts @@ -6,7 +6,6 @@ import { Peer, PicElement, PicSubType, - PicType, RawMessage, SendFileElement, SendPicElement, @@ -17,7 +16,7 @@ import path from 'path'; import fs from 'fs'; import fsPromises from 'fs/promises'; import { InstanceContext, NapCatCore, SearchResultItem } from '@/core'; -import * as fileType from 'file-type'; +import { fileTypeFromFile } from 'file-type'; import imageSize from 'image-size'; import { ISizeCalculationResult } from 'image-size/dist/types/interface'; import { RkeyManager } from '@/core/helper/rkey'; @@ -41,7 +40,7 @@ export class NTQQFileApi { this.rkeyManager = new RkeyManager([ 'https://rkey.napneko.icu/rkeys' ], - this.context.logger + this.context.logger ); } @@ -62,7 +61,7 @@ export class NTQQFileApi { async uploadFile(filePath: string, elementType: ElementType = ElementType.PIC, elementSubType: number = 0) { 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}` : ''; let fileName = `${path.basename(filePath)}`; if (fileName.indexOf('.') === -1) { @@ -81,6 +80,7 @@ export class NTQQFileApi { }); await this.copyFile(filePath, mediaPath); + console.log('copyFile', filePath, mediaPath); const fileSize = await this.getFileSize(filePath); return { md5: fileMd5, @@ -158,7 +158,7 @@ export class NTQQFileApi { let fileExt = 'mp4'; try { - const tempExt = (await fileType.fileTypeFromFile(filePath))?.ext; + const tempExt = (await fileTypeFromFile(filePath))?.ext; if (tempExt) fileExt = tempExt; } catch (e) { this.context.logger.logError('获取文件类型失败', e); @@ -306,18 +306,18 @@ export class NTQQFileApi { element.elementType === ElementType.FILE ) { switch (element.elementType) { - case ElementType.PIC: + case ElementType.PIC: element.picElement!.sourcePath = elementResults[elementIndex]; - break; - case ElementType.VIDEO: + break; + case ElementType.VIDEO: element.videoElement!.filePath = elementResults[elementIndex]; - break; - case ElementType.PTT: + break; + case ElementType.PTT: element.pttElement!.filePath = elementResults[elementIndex]; - break; - case ElementType.FILE: + break; + case ElementType.FILE: element.fileElement!.filePath = elementResults[elementIndex]; - break; + break; } elementIndex++; } diff --git a/src/core/helper/msg.ts b/src/core/helper/msg.ts index e1d3992e..c28858d8 100644 --- a/src/core/helper/msg.ts +++ b/src/core/helper/msg.ts @@ -1,7 +1,7 @@ -import * as fileType from 'file-type'; +import { fileTypeFromFile } from 'file-type'; import { PicType } from '../types'; export async function getFileTypeForSendType(picPath: string): Promise { - const fileTypeResult = (await fileType.fileTypeFromFile(picPath))?.ext ?? 'jpg'; + const fileTypeResult = (await fileTypeFromFile(picPath))?.ext ?? 'jpg'; const picTypeMap: { [key: string]: PicType } = { //'webp': PicType.NEWPIC_WEBP, 'gif': PicType.NEWPIC_GIF, diff --git a/src/onebot/action/extends/OCRImage.ts b/src/onebot/action/extends/OCRImage.ts index 7f79eab4..dc39b9e0 100644 --- a/src/onebot/action/extends/OCRImage.ts +++ b/src/onebot/action/extends/OCRImage.ts @@ -1,6 +1,6 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; -import { checkFileExist, uri2local } from '@/common/file'; +import { checkFileExist, uriToLocalFile } from '@/common/file'; import fs from 'fs'; import { Static, Type } from '@sinclair/typebox'; @@ -15,7 +15,7 @@ export class OCRImage extends OneBotAction { payloadSchema = SchemaData; 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) { throw new Error(`OCR ${payload.image}失败,image字段可能格式不正确`); } diff --git a/src/onebot/action/extends/SetQQAvatar.ts b/src/onebot/action/extends/SetQQAvatar.ts index f8003fec..d5b656a1 100644 --- a/src/onebot/action/extends/SetQQAvatar.ts +++ b/src/onebot/action/extends/SetQQAvatar.ts @@ -1,7 +1,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; import fs from 'node:fs/promises'; -import { checkFileExist, uri2local } from '@/common/file'; +import { checkFileExist, uriToLocalFile } from '@/common/file'; import { Static, Type } from '@sinclair/typebox'; const SchemaData = Type.Object({ @@ -14,7 +14,7 @@ export default class SetAvatar extends OneBotAction { actionName = ActionName.SetQQAvatar; payloadSchema = SchemaData; async _handle(payload: Payload): Promise { - const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file)); + const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file)); if (!success) { throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`); } diff --git a/src/onebot/action/go-cqhttp/SendGroupNotice.ts b/src/onebot/action/go-cqhttp/SendGroupNotice.ts index 998d711f..6dd8a343 100644 --- a/src/onebot/action/go-cqhttp/SendGroupNotice.ts +++ b/src/onebot/action/go-cqhttp/SendGroupNotice.ts @@ -1,4 +1,4 @@ -import { checkFileExist, uri2local } from '@/common/file'; +import { checkFileExist, uriToLocalFile } from '@/common/file'; import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; import { unlink } from 'node:fs/promises'; @@ -28,7 +28,7 @@ export class SendGroupNotice extends OneBotAction { const { path, success, - } = (await uri2local(this.core.NapCatTempPath, payload.image)); + } = (await uriToLocalFile(this.core.NapCatTempPath, payload.image)); if (!success) { throw new Error(`群公告${payload.image}设置失败,image字段可能格式不正确`); } diff --git a/src/onebot/action/go-cqhttp/SetGroupPortrait.ts b/src/onebot/action/go-cqhttp/SetGroupPortrait.ts index b9354dd5..41532109 100644 --- a/src/onebot/action/go-cqhttp/SetGroupPortrait.ts +++ b/src/onebot/action/go-cqhttp/SetGroupPortrait.ts @@ -1,6 +1,6 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; 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 fs from 'node:fs/promises'; const SchemaData = Type.Object({ @@ -15,7 +15,7 @@ export default class SetGroupPortrait extends OneBotAction { payloadSchema = SchemaData; async _handle(payload: Payload): Promise { - const { path, success } = (await uri2local(this.core.NapCatTempPath, payload.file)); + const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file)); if (!success) { throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`); } diff --git a/src/onebot/action/go-cqhttp/UploadGroupFile.ts b/src/onebot/action/go-cqhttp/UploadGroupFile.ts index 62e5cf70..3c44458a 100644 --- a/src/onebot/action/go-cqhttp/UploadGroupFile.ts +++ b/src/onebot/action/go-cqhttp/UploadGroupFile.ts @@ -2,7 +2,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; import { ChatType, Peer } from '@/core/types'; import fs from 'fs'; -import { uri2local } from '@/common/file'; +import { uriToLocalFile } from '@/common/file'; import { SendMessageContext } from '@/onebot/api'; import { Static, Type } from '@sinclair/typebox'; @@ -25,7 +25,7 @@ export default class GoCQHTTPUploadGroupFile extends OneBotAction if (fs.existsSync(file)) { file = `file://${file}`; } - const downloadResult = await uri2local(this.core.NapCatTempPath, file); + const downloadResult = await uriToLocalFile(this.core.NapCatTempPath, file); const peer: Peer = { chatType: ChatType.KCHATTYPEGROUP, peerUid: payload.group_id.toString(), diff --git a/src/onebot/action/go-cqhttp/UploadPrivateFile.ts b/src/onebot/action/go-cqhttp/UploadPrivateFile.ts index 4633162e..aa555852 100644 --- a/src/onebot/action/go-cqhttp/UploadPrivateFile.ts +++ b/src/onebot/action/go-cqhttp/UploadPrivateFile.ts @@ -2,7 +2,7 @@ import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; import { ChatType, Peer, SendFileElement } from '@/core/types'; import fs from 'fs'; -import { uri2local } from '@/common/file'; +import { uriToLocalFile } from '@/common/file'; import { SendMessageContext } from '@/onebot/api'; import { ContextMode, createContext } from '@/onebot/action/msg/SendMsg'; import { Static, Type } from '@sinclair/typebox'; @@ -36,7 +36,7 @@ export default class GoCQHTTPUploadPrivateFile extends OneBotAction Date: Wed, 4 Dec 2024 11:40:36 +0800 Subject: [PATCH 30/57] =?UTF-8?q?chore:=20=E7=A7=BB=E5=87=BA=E8=B0=83?= =?UTF-8?q?=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/file.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/core/apis/file.ts b/src/core/apis/file.ts index eb58c916..186c7ba6 100644 --- a/src/core/apis/file.ts +++ b/src/core/apis/file.ts @@ -80,7 +80,6 @@ export class NTQQFileApi { }); await this.copyFile(filePath, mediaPath); - console.log('copyFile', filePath, mediaPath); const fileSize = await this.getFileSize(filePath); return { md5: fileMd5, From 388eb57d0d4b2bef4210958317d38c88ff5c031b Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Wed, 4 Dec 2024 03:40:59 +0000 Subject: [PATCH 31/57] release: v4.2.18 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index f5a4713e..d1ddcacb 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.17", + "version": "4.2.18", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index 84772ddc..ccc31de9 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.17", + "version": "4.2.18", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 95c83215..9a4ae69c 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.17'; +export const napCatVersion = '4.2.18'; From eb99379a79b77c619466ab30b9ee1e5539f32ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 18:29:33 +0800 Subject: [PATCH 32/57] =?UTF-8?q?fix:=20=E6=80=A7=E8=83=BD=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/onebot/action/group/GetGroupMemberList.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/onebot/action/group/GetGroupMemberList.ts b/src/onebot/action/group/GetGroupMemberList.ts index 0bb3fda4..35500059 100644 --- a/src/onebot/action/group/GetGroupMemberList.ts +++ b/src/onebot/action/group/GetGroupMemberList.ts @@ -21,7 +21,8 @@ export class GetGroupMemberList extends OneBotAction const memberCache = this.core.apis.GroupApi.groupMemberCache; let groupMembers = memberCache.get(groupIdStr); if (noCache || !groupMembers) { - await this.core.apis.GroupApi.refreshGroupMemberCache(groupIdStr); + this.core.apis.GroupApi.refreshGroupMemberCache(groupIdStr).then().catch(); + //下次刷新 groupMembers = memberCache.get(groupIdStr); if (!groupMembers) { throw new Error(`Failed to get group member list for group ${groupIdStr}`); From d51d6a5cc1dcd68aecf8e125e0a65442426d412f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 18:41:28 +0800 Subject: [PATCH 33/57] refactor: getUidByUinV2/getUinByUidV2 --- src/core/apis/user.ts | 51 ++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index 99d437ea..43a81cf6 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -26,7 +26,7 @@ export class NTQQUserApi { uids, ); } - + // 默认获取自己的 type = 2 获取别人 type = 1 async getProfileLike(uid: string, start: number, count: number, type: number = 2) { return this.context.session.getProfileLikeService().getBuddyProfileLike({ @@ -99,7 +99,7 @@ export class NTQQUserApi { }; return RetUser; } - + async getUserDetailInfo(uid: string): Promise { let retUser = await solveAsyncProblem(async (uid) => this.fetchUserDetailInfo(uid, UserDetailSource.KDB), uid); if (retUser && retUser.uin !== '0') { @@ -173,31 +173,38 @@ export class NTQQUserApi { return skey; } - //后期改成流水线处理 async getUidByUinV2(Uin: string) { - let uid = (await this.context.session.getGroupService().getUidByUins([Uin])).uids.get(Uin); - if (uid) return uid; - uid = (await this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin])).get(Uin); - if (uid) return uid; - uid = (await this.context.session.getUixConvertService().getUid([Uin])).uidInfo.get(Uin); - if (uid) return uid; - const unverifiedUid = (await this.getUserDetailInfoByUin(Uin)).detail.uid;//从QQ Native 特殊转换 - if (unverifiedUid.indexOf('*') == -1) uid = unverifiedUid; - //if (uid) return uid; + const services = [ + () => this.context.session.getGroupService().getUidByUins([Uin]).then((data) => data.uids.get(Uin)).catch(() => undefined), + () => this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin]).then((data) => data.get(Uin)).catch(() => undefined), + () => this.context.session.getUixConvertService().getUid([Uin]).then((data) => data.uidInfo.get(Uin)).catch(() => undefined), + () => this.getUserDetailInfoByUin(Uin).then((data) => data.detail.uid).catch(() => undefined), + ]; + 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) { - let uin = (await this.context.session.getGroupService().getUinByUids([Uid])).uins.get(Uid); - if (uin && uin !== '0') return uin; - uin = (await this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid])).get(Uid); - if (uin && uin !== '0') return uin; - uin = (await this.context.session.getUixConvertService().getUin([Uid])).uinInfo.get(Uid); - if (uin && uin !== '0') return uin; - uin = (await this.core.apis.FriendApi.getBuddyIdMap(true)).getKey(Uid); - if (uin && uin !== '0') return uin; - uin = (await this.getUserDetailInfo(Uid)).uin; //从QQ Native 转换 + const services = [ + () => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined), + () => this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid]).then((data) => data.get(Uid)).catch(() => undefined), + () => this.context.session.getUixConvertService().getUin([Uid]).then((data) => data.uinInfo.get(Uid)).catch(() => undefined), + () => this.core.apis.FriendApi.getBuddyIdMap(true).then((data) => data.getKey(Uid)).catch(() => undefined), + () => 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; } From bb0f65a52d1e3c61f9c1454e98293e3de2140001 Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Wed, 4 Dec 2024 10:42:32 +0000 Subject: [PATCH 34/57] release: v4.2.19 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index d1ddcacb..5a4fe128 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.18", + "version": "4.2.19", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index ccc31de9..12ba1551 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.18", + "version": "4.2.19", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 9a4ae69c..eb3bf162 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.18'; +export const napCatVersion = '4.2.19'; From 02cff040e3732cce1bf1db3330161b8631f134d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 18:50:51 +0800 Subject: [PATCH 35/57] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E6=9C=AA?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84createUidFromTinyId=E5=92=8CgetStatu?= =?UTF-8?q?sByUid=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/user.ts | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index 43a81cf6..f51fd2df 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -11,13 +11,6 @@ export class NTQQUserApi { this.context = context; this.core = core; } - //self_tind格式 - async createUidFromTinyId(tinyId: string) { - return this.context.session.getMsgService().createUidFromTinyId(this.core.selfInfo.uin, tinyId); - } - async getStatusByUid(uid: string) { - return this.context.session.getProfileService().getStatus(uid); - } async getCoreAndBaseInfo(uids: string[]) { return await this.core.eventWrapper.callNoListenerEvent( From e295235a89d156612a92729073aee962c6116967 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 19:45:46 +0800 Subject: [PATCH 36/57] fix: #596 --- src/core/apis/user.ts | 25 ++++++++++++++++--- .../services/NodeIKernelProfileService.ts | 6 ++--- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index f51fd2df..34dd046b 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -2,14 +2,20 @@ import { ModifyProfileParams, User, UserDetailSource } from '@/core/types'; import { RequestUtil } from '@/common/request'; import { InstanceContext, NapCatCore, ProfileBizType } from '..'; import { solveAsyncProblem } from '@/common/helper'; +import { promisify } from 'node:util'; +import { LRUCache } from '@/common/lru-cache'; export class NTQQUserApi { context: InstanceContext; core: NapCatCore; + private uidCache: LRUCache; + private uinCache: LRUCache; constructor(context: InstanceContext, core: NapCatCore) { this.context = context; this.core = core; + this.uidCache = new LRUCache(1000); + this.uinCache = new LRUCache(1000); } async getCoreAndBaseInfo(uids: string[]) { @@ -163,20 +169,26 @@ export class NTQQUserApi { if (!skey) { throw new Error('SKey is Empty'); } + return skey; } async getUidByUinV2(Uin: string) { + if (this.uidCache.get(Uin)) { + return this.uidCache.get(Uin); + } const services = [ - () => this.context.session.getGroupService().getUidByUins([Uin]).then((data) => data.uids.get(Uin)).catch(() => undefined), - () => this.context.session.getProfileService().getUidByUin('FriendsServiceImpl', [Uin]).then((data) => data.get(Uin)).catch(() => undefined), () => this.context.session.getUixConvertService().getUid([Uin]).then((data) => data.uidInfo.get(Uin)).catch(() => undefined), + () => promisify> + (this.context.session.getProfileService().getUidByUin)('FriendsServiceImpl', [Uin]).then((data) => data.get(Uin)).catch(() => undefined), + () => this.context.session.getGroupService().getUidByUins([Uin]).then((data) => data.uids.get(Uin)).catch(() => undefined), () => this.getUserDetailInfoByUin(Uin).then((data) => data.detail.uid).catch(() => undefined), ]; let uid: string | undefined = undefined; for (const service of services) { uid = await service(); if (uid && uid.indexOf('*') == -1 && uid !== '') { + this.uidCache.put(Uin, uid); break; } } @@ -184,10 +196,14 @@ export class NTQQUserApi { } async getUinByUidV2(Uid: string) { + if (this.uinCache.get(Uid)) { + return this.uinCache.get(Uid); + } const services = [ - () => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined), - () => this.context.session.getProfileService().getUinByUid('FriendsServiceImpl', [Uid]).then((data) => data.get(Uid)).catch(() => undefined), () => this.context.session.getUixConvertService().getUin([Uid]).then((data) => data.uinInfo.get(Uid)).catch(() => undefined), + () => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined), + () => promisify> + (this.context.session.getProfileService().getUinByUid)('FriendsServiceImpl', [Uid]).then((data) => data.get(Uid)).catch(() => undefined), () => this.core.apis.FriendApi.getBuddyIdMap(true).then((data) => data.getKey(Uid)).catch(() => undefined), () => this.getUserDetailInfo(Uid).then((data) => data.uin).catch(() => undefined), ]; @@ -195,6 +211,7 @@ export class NTQQUserApi { for (const service of services) { uin = await service(); if (uin && uin !== '0' && uin !== '') { + this.uinCache.put(Uid, uin); break; } } diff --git a/src/core/services/NodeIKernelProfileService.ts b/src/core/services/NodeIKernelProfileService.ts index cfcb18bf..e4f6e6a6 100644 --- a/src/core/services/NodeIKernelProfileService.ts +++ b/src/core/services/NodeIKernelProfileService.ts @@ -4,14 +4,14 @@ import { GeneralCallResult } from '@/core/services/common'; export interface NodeIKernelProfileService { getOtherFlag(callfrom: string, uids: string[]): Promise>; - + getVasInfo(callfrom: string, uids: string[]): Promise>; getRelationFlag(callfrom: string, uids: string[]): Promise>; - getUidByUin(callfrom: string, uin: Array): Promise>; + getUidByUin(callfrom: string, uin: Array): Map; - getUinByUid(callfrom: string, uid: Array): Promise>; + getUinByUid(callfrom: string, uid: Array): Map; getCoreAndBaseInfo(callfrom: string, uids: string[]): Promise>; From 4a2884509ebde3734f96fac42b540e6874afb864 Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Wed, 4 Dec 2024 11:46:12 +0000 Subject: [PATCH 37/57] release: v4.2.20 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 5a4fe128..5075b688 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.19", + "version": "4.2.20", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index 12ba1551..7aeff5df 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.19", + "version": "4.2.20", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index eb3bf162..19ede97e 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.19'; +export const napCatVersion = '4.2.20'; From 8fb8c888f554187fee01dc00e5f713f210b49cd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 19:48:59 +0800 Subject: [PATCH 38/57] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=E6=9C=AA?= =?UTF-8?q?=E4=BD=BF=E7=94=A8=E7=9A=84uidCache=E5=92=8CuinCache=E9=80=BB?= =?UTF-8?q?=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/user.ts | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index 34dd046b..5d8c7881 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -8,14 +8,10 @@ import { LRUCache } from '@/common/lru-cache'; export class NTQQUserApi { context: InstanceContext; core: NapCatCore; - private uidCache: LRUCache; - private uinCache: LRUCache; constructor(context: InstanceContext, core: NapCatCore) { this.context = context; this.core = core; - this.uidCache = new LRUCache(1000); - this.uinCache = new LRUCache(1000); } async getCoreAndBaseInfo(uids: string[]) { @@ -174,9 +170,6 @@ export class NTQQUserApi { } async getUidByUinV2(Uin: string) { - if (this.uidCache.get(Uin)) { - return this.uidCache.get(Uin); - } const services = [ () => this.context.session.getUixConvertService().getUid([Uin]).then((data) => data.uidInfo.get(Uin)).catch(() => undefined), () => promisify> @@ -188,7 +181,6 @@ export class NTQQUserApi { for (const service of services) { uid = await service(); if (uid && uid.indexOf('*') == -1 && uid !== '') { - this.uidCache.put(Uin, uid); break; } } @@ -196,9 +188,6 @@ export class NTQQUserApi { } async getUinByUidV2(Uid: string) { - if (this.uinCache.get(Uid)) { - return this.uinCache.get(Uid); - } const services = [ () => this.context.session.getUixConvertService().getUin([Uid]).then((data) => data.uinInfo.get(Uid)).catch(() => undefined), () => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined), @@ -211,7 +200,6 @@ export class NTQQUserApi { for (const service of services) { uin = await service(); if (uin && uin !== '0' && uin !== '') { - this.uinCache.put(Uid, uin); break; } } From 5b17a14a2a1702a486220171b57e77d4f9813ded Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Wed, 4 Dec 2024 11:49:26 +0000 Subject: [PATCH 39/57] release: v4.2.21 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 5075b688..47324e8a 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.20", + "version": "4.2.21", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index 7aeff5df..40b2f1fe 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.20", + "version": "4.2.21", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 19ede97e..a5c3dd1b 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.20'; +export const napCatVersion = '4.2.21'; From e939ec0e52473b2d4968ee0a75430fe0eb7c5810 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 20:37:08 +0800 Subject: [PATCH 40/57] fix: #597 --- src/core/apis/user.ts | 4 ++-- src/core/services/NodeIKernelGroupService.ts | 4 ++-- src/onebot/index.ts | 12 +++++++----- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index 5d8c7881..a715d83d 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -184,7 +184,7 @@ export class NTQQUserApi { break; } } - return uid; + return uid ?? ''; } async getUinByUidV2(Uid: string) { @@ -203,7 +203,7 @@ export class NTQQUserApi { break; } } - return uin; + return uin ?? '0'; } async getRecentContactListSnapShot(count: number) { diff --git a/src/core/services/NodeIKernelGroupService.ts b/src/core/services/NodeIKernelGroupService.ts index 83a9cc92..1b748883 100644 --- a/src/core/services/NodeIKernelGroupService.ts +++ b/src/core/services/NodeIKernelGroupService.ts @@ -187,11 +187,11 @@ export interface NodeIKernelGroupService { destroyGroup(groupCode: string): void; - getSingleScreenNotifies(doubted: boolean, start_seq: string, num: number): Promise; + getSingleScreenNotifies(doubt: boolean, startSeq: string, count: number): Promise; clearGroupNotifies(groupCode: string): void; - getGroupNotifiesUnreadCount(unknown: boolean): Promise; + getGroupNotifiesUnreadCount(doubt: boolean): Promise; clearGroupNotifiesUnreadCount(doubt: boolean): void; diff --git a/src/onebot/index.ts b/src/onebot/index.ts index 85303d87..5c9c4419 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -405,8 +405,8 @@ export class NapCatOneBot11Adapter { this.context.logger.logDebug(`收到邀请我加群通知:${notify}`); const groupInviteEvent = new OB11GroupRequestEvent( this.core, - parseInt(notify.group.groupCode), - parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid)), + +notify.group.groupCode, + +await this.core.apis.UserApi.getUinByUidV2(notify.user2.uid), 'invite', notify.postscript, flag @@ -423,8 +423,8 @@ export class NapCatOneBot11Adapter { this.context.logger.logDebug(`收到群员邀请加群通知:${notify}`); const groupInviteEvent = new OB11GroupRequestEvent( this.core, - parseInt(notify.group.groupCode), - parseInt(await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid)), + +notify.group.groupCode, + +await this.core.apis.UserApi.getUinByUidV2(notify.user1.uid), 'add', notify.postscript, flag @@ -571,6 +571,8 @@ export class NapCatOneBot11Adapter { } private async emitFriendRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) { + const operatorUid = element.grayTipElement?.revokeElement.operatorUid; + if (!operatorUid) return undefined; return new OB11FriendRecallNoticeEvent( this.core, +message.senderUin, @@ -581,7 +583,7 @@ export class NapCatOneBot11Adapter { private async emitGroupRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) { const operatorUid = element.grayTipElement?.revokeElement.operatorUid; 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( this.core, +message.peerUin, From eac58a2a502d02805c01ee71930648b0a8456f6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 21:04:24 +0800 Subject: [PATCH 41/57] fix: 9.9.17-30366 --- src/core/external/appid.json | 6 +++++- src/core/external/offset.json | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/core/external/appid.json b/src/core/external/appid.json index c1284c54..0f209ee8 100644 --- a/src/core/external/appid.json +++ b/src/core/external/appid.json @@ -98,5 +98,9 @@ "6.9.61-29927": { "appid": 537255836, "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" } -} +} \ No newline at end of file diff --git a/src/core/external/offset.json b/src/core/external/offset.json index f6dabbb7..239e4f77 100644 --- a/src/core/external/offset.json +++ b/src/core/external/offset.json @@ -102,5 +102,9 @@ "6.9.61-29927-arm64": { "send": "4038740", "recv": "403AF58" + }, + "9.9.17-30366-x64": { + "send": "39AB0B0", + "recv": "39AF4E4" } -} +} \ No newline at end of file From 2e5dd6535af5836665b82305252ed578054cb0ad Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Wed, 4 Dec 2024 13:18:58 +0000 Subject: [PATCH 42/57] release: v4.2.22 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 47324e8a..3d286364 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.21", + "version": "4.2.22", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index 40b2f1fe..df768956 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.21", + "version": "4.2.22", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index a5c3dd1b..7802e3e9 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.21'; +export const napCatVersion = '4.2.22'; From 22d3ac33a27618b6aec102b6e2d02eccf0d22b21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 21:59:53 +0800 Subject: [PATCH 43/57] =?UTF-8?q?Refactor:=20=E6=9B=B4=E6=96=B0=E7=BE=A4?= =?UTF-8?q?=E7=BB=84=E9=80=9A=E7=9F=A5=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= =?UTF-8?q?=EF=BC=8C=E4=BC=98=E5=8C=96=E6=95=B0=E6=8D=AE=E7=BB=93=E6=9E=84?= =?UTF-8?q?=E5=92=8C=E5=BC=82=E6=AD=A5=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../action/extends/GetGroupAddRequest.ts | 56 ++++++++++------ .../action/group/GetGroupIgnoredNotifies.ts | 67 +++++++++++++------ src/onebot/action/system/GetSystemMsg.ts | 63 +++++++++++------ 3 files changed, 126 insertions(+), 60 deletions(-) diff --git a/src/onebot/action/extends/GetGroupAddRequest.ts b/src/onebot/action/extends/GetGroupAddRequest.ts index 8bdd65d8..9f0598db 100644 --- a/src/onebot/action/extends/GetGroupAddRequest.ts +++ b/src/onebot/action/extends/GetGroupAddRequest.ts @@ -2,32 +2,44 @@ import { GroupNotifyMsgStatus } from '@/core'; import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; -interface OB11GroupRequestNotify { - group_id: number, - user_id: number, - flag: string +interface Notify { + request_id: string; + invitor_uin: number; + invitor_nick?: string; + group_id?: number; + group_name?: string; + checked: boolean; + requester_nick?: string; + actor: number; } -export default class GetGroupAddRequest extends OneBotAction { +export default class GetGroupAddRequest extends OneBotAction { actionName = ActionName.GetGroupIgnoreAddRequest; - async _handle(payload: null): Promise { - const ignoredNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(true, 10); - const retData: any = { - join_requests: await Promise.all( - ignoredNotifies - .filter(notify => notify.type === 7) - .map(async SSNotify => ({ - request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type, - requester_uin: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1?.uid), - requester_nick: SSNotify.user1?.nickName, - group_id: SSNotify.group?.groupCode, - group_name: SSNotify.group?.groupName, - checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, - actor: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, - }))), - }; + async _handle(payload: null): Promise { + const NTQQUserApi = this.core.apis.UserApi; + const NTQQGroupApi = this.core.apis.GroupApi; + const ignoredNotifies = await NTQQGroupApi.getSingleScreenNotifies(true, 10); + const retData: Notify[] = []; + + const notifyPromises = ignoredNotifies + .filter(notify => notify.type === 7) + .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; + retData.push({ + request_id: `${SSNotify.group.groupCode}|${SSNotify.seq}|${SSNotify.type}`, + invitor_uin: invitorUin, + requester_nick: SSNotify.user1?.nickName, + group_id: +SSNotify.group?.groupCode, + group_name: SSNotify.group?.groupName, + checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, + actor: actorUin, + }); + }); + + await Promise.all(notifyPromises); return retData; } -} +} \ No newline at end of file diff --git a/src/onebot/action/group/GetGroupIgnoredNotifies.ts b/src/onebot/action/group/GetGroupIgnoredNotifies.ts index a639ed1e..4137e735 100644 --- a/src/onebot/action/group/GetGroupIgnoredNotifies.ts +++ b/src/onebot/action/group/GetGroupIgnoredNotifies.ts @@ -1,26 +1,55 @@ import { GroupNotifyMsgStatus } from '@/core'; import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; -export class GetGroupIgnoredNotifies extends OneBotAction { - actionName = ActionName.GetGroupIgnoredNotifies; - async _handle(payload: void) { - const ignoredNotifies = await this.core.apis.GroupApi.getSingleScreenNotifies(true, 10); - const retData: any = { - join_requests: await Promise.all( - ignoredNotifies - .filter(notify => notify.type === 7) - .map(async SSNotify => ({ - request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type, - requester_uin: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1?.uid), - requester_nick: SSNotify.user1?.nickName, - group_id: SSNotify.group?.groupCode, - group_name: SSNotify.group?.groupName, - checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, - actor: await this.core.apis.UserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, - }))), - }; +interface Notify { + request_id: string; + invitor_uin: number; + invitor_nick?: string; + group_id?: number; + group_name?: string; + checked: boolean; + requester_nick?: string; + actor: number; +} +interface RetData { + InvitedRequest: Notify[]; + join_requests: Notify[]; +} + +export class GetGroupIgnoredNotifies extends OneBotAction { + actionName = ActionName.GetGroupSystemMsg; + + async _handle(): Promise { + 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, + checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, + 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; } -} +} \ No newline at end of file diff --git a/src/onebot/action/system/GetSystemMsg.ts b/src/onebot/action/system/GetSystemMsg.ts index f1a904c5..a8a22228 100644 --- a/src/onebot/action/system/GetSystemMsg.ts +++ b/src/onebot/action/system/GetSystemMsg.ts @@ -1,38 +1,63 @@ import { GroupNotifyMsgStatus } from '@/core'; import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; -export class GetGroupSystemMsg extends OneBotAction { + +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 { actionName = ActionName.GetGroupSystemMsg; - async _handle() { + async _handle(): Promise { const NTQQUserApi = this.core.apis.UserApi; const NTQQGroupApi = this.core.apis.GroupApi; - // 默认10条 该api未完整实现 包括响应数据规范化 类型规范化 - const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false, 10); - const retData: any = { InvitedRequest: [], join_requests: [] }; - for (const SSNotify of SingleScreenNotifies) { - if (SSNotify.type == 1) { + 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; + + if (SSNotify.type === 1) { retData.InvitedRequest.push({ - request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type, - invitor_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid), + request_id: `${SSNotify.group.groupCode}|${SSNotify.seq}|${SSNotify.type}`, + invitor_uin: invitorUin, invitor_nick: SSNotify.user1?.nickName, - group_id: SSNotify.group?.groupCode, + group_id: +SSNotify.group?.groupCode, group_name: SSNotify.group?.groupName, - checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true, - actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, + checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, + actor: actorUin, }); - } else if (SSNotify.type == 7) { + } else if (SSNotify.type === 7) { retData.join_requests.push({ - request_id: SSNotify.group.groupCode + '|' + SSNotify.seq + '|' + SSNotify.type, - requester_uin: await NTQQUserApi.getUinByUidV2(SSNotify.user1?.uid), + request_id: `${SSNotify.group.groupCode}|${SSNotify.seq}|${SSNotify.type}`, + invitor_uin: invitorUin, requester_nick: SSNotify.user1?.nickName, - group_id: SSNotify.group?.groupCode, + group_id: +SSNotify.group?.groupCode, group_name: SSNotify.group?.groupName, - checked: SSNotify.status === GroupNotifyMsgStatus.KUNHANDLE ? false : true, - actor: await NTQQUserApi.getUinByUidV2(SSNotify.user2?.uid) || 0, + checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, + actor: actorUin, }); } - } + }); + + await Promise.all(notifyPromises); return retData; } From c4b5f3427177833825cbdb02222b15eadcc24523 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 22:02:11 +0800 Subject: [PATCH 44/57] =?UTF-8?q?fix:=20=E5=85=9C=E5=BA=95=20=E9=98=B2?= =?UTF-8?q?=E6=AD=A2=E8=BF=9B=E5=85=A5=E5=BD=B1=E5=93=8D=E9=80=9F=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/user.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index a715d83d..f2b9bb0a 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -170,6 +170,9 @@ export class NTQQUserApi { } async getUidByUinV2(Uin: string) { + if (!Uin) { + return ''; + } const services = [ () => this.context.session.getUixConvertService().getUid([Uin]).then((data) => data.uidInfo.get(Uin)).catch(() => undefined), () => promisify> @@ -188,6 +191,9 @@ export class NTQQUserApi { } async getUinByUidV2(Uid: string) { + if (!Uid) { + return '0'; + } const services = [ () => this.context.session.getUixConvertService().getUin([Uid]).then((data) => data.uinInfo.get(Uid)).catch(() => undefined), () => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined), From 3de54381397789521f4c90a3403a5ffcf6a9ce95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 22:16:59 +0800 Subject: [PATCH 45/57] fix: poke report --- src/core/types/element.ts | 1 + src/onebot/api/msg.ts | 11 +++++++++++ src/onebot/types/message.ts | 10 +++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/core/types/element.ts b/src/core/types/element.ts index 579603ba..8a848876 100644 --- a/src/core/types/element.ts +++ b/src/core/types/element.ts @@ -29,6 +29,7 @@ export interface TextElement { } export interface FaceElement { + pokeType?: number; faceIndex: number; faceType: FaceType; faceText?: string; diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index 86da6c77..e512386d 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -153,6 +153,17 @@ export class OneBotMsgApi { faceElement: async element => { 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) { return { type: OB11MessageDataType.dice, diff --git a/src/onebot/types/message.ts b/src/onebot/types/message.ts index 12a7aa9e..645f0b5f 100644 --- a/src/onebot/types/message.ts +++ b/src/onebot/types/message.ts @@ -71,6 +71,14 @@ export enum OB11MessageDataType { location = 'location' } +export interface OB11MessagePoke { + type: OB11MessageDataType.poke; + data: { + type: string; + id: string; + }; +} + // 商城表情消息接口定义 export interface OB11MessageMFace { type: OB11MessageDataType.mface; @@ -247,7 +255,7 @@ export type OB11MessageData = OB11MessageAt | OB11MessageReply | OB11MessageImage | OB11MessageRecord | OB11MessageFile | OB11MessageVideo | OB11MessageNode | OB11MessageIdMusic | OB11MessageCustomMusic | OB11MessageJson | - OB11MessageDice | OB11MessageRPS | OB11MessageMarkdown | OB11MessageForward | OB11MessageContext; + OB11MessageDice | OB11MessageRPS | OB11MessageMarkdown | OB11MessageForward | OB11MessageContext | OB11MessagePoke; // 发送消息接口定义 export interface OB11PostSendMsg { From 932ffc267392d86aa7fb7cf09f50f3a42ddc2114 Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Wed, 4 Dec 2024 14:18:02 +0000 Subject: [PATCH 46/57] release: v4.2.23 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 3d286364..3d223b91 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.22", + "version": "4.2.23", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index df768956..ded51e7b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.22", + "version": "4.2.23", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index 7802e3e9..d43e0bff 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.22'; +export const napCatVersion = '4.2.23'; From 7c694e7fae6d612db001859525228de9983f429d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 23:03:46 +0800 Subject: [PATCH 47/57] =?UTF-8?q?chore:=20=E8=B7=91=E8=B7=AF=E7=9A=84?= =?UTF-8?q?=E8=A7=84=E8=8C=83/=E5=9B=B0=E9=9A=BE=E7=9A=84=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/README.md b/README.md index 309364fd..dbaacc21 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,13 @@ NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现 ## 回家旅途 [QQ Group](https://qm.qq.com/q/I6LU87a0Yq) +## 性能/OneBot协议标准问题/开发方向 +NapCat 已实现90%+的OneBot/GoCQ标准接口,并提供兼容性保留接口,其设计理念遵守 无数据库/异步优先/后台刷新 的性能思想。 + +由此设计带来一系列好处,在开发中,获取群员列表通常小于50Ms,单条文本消息发送在320Ms以内,在1k+的群聊流程运行,同时带来一些副作用,上报数据中大量使用Magic生成字段, 消息Id无法持久,无法上报撤回消息原始内容。 + +NapCat在设计理念下遵守OneBot规范大多数要求并且积极改进,任何合理的标准化issue与pr将被接收。 + ## 感谢他们 感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权 From f374cc77ae500211bc68e7f80569c41e4431a770 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Wed, 4 Dec 2024 23:04:37 +0800 Subject: [PATCH 48/57] chore: readme --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index dbaacc21..9be9f2c8 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现 ## 回家旅途 [QQ Group](https://qm.qq.com/q/I6LU87a0Yq) -## 性能/OneBot协议标准问题/开发方向 +## 性能设计/协议标准 NapCat 已实现90%+的OneBot/GoCQ标准接口,并提供兼容性保留接口,其设计理念遵守 无数据库/异步优先/后台刷新 的性能思想。 由此设计带来一系列好处,在开发中,获取群员列表通常小于50Ms,单条文本消息发送在320Ms以内,在1k+的群聊流程运行,同时带来一些副作用,上报数据中大量使用Magic生成字段, 消息Id无法持久,无法上报撤回消息原始内容。 From f52b8d1f04054f71fadf44b61495d2040f918e19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Thu, 5 Dec 2024 11:15:10 +0800 Subject: [PATCH 49/57] =?UTF-8?q?feat:=2030366=20=E5=85=A8=E5=B9=B3?= =?UTF-8?q?=E5=8F=B0=E9=80=9A=E7=94=A8=E6=80=A7=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/external/appid.json | 8 ++++++++ src/core/external/offset.json | 16 ++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/src/core/external/appid.json b/src/core/external/appid.json index 0f209ee8..d728732a 100644 --- a/src/core/external/appid.json +++ b/src/core/external/appid.json @@ -102,5 +102,13 @@ "9.9.17-30366": { "appid": 537258389, "qua": "V1_WIN_NQ_9.9.17_30366_GW_B" + }, + "3.2.15-30366": { + "appid": 537258413, + "qua": "V1_LNX_NQ_3.2.15_30366_GW_B" + }, + "6.9.62-30366": { + "appid": 537258401, + "qua": "V1_MAC_NQ_6.9.62_30366_GW_B" } } \ No newline at end of file diff --git a/src/core/external/offset.json b/src/core/external/offset.json index 239e4f77..47e466ad 100644 --- a/src/core/external/offset.json +++ b/src/core/external/offset.json @@ -106,5 +106,21 @@ "9.9.17-30366-x64": { "send": "39AB0B0", "recv": "39AF4E4" + }, + "3.2.15-30366-x64": { + "send": "A402380", + "recv": "A405C80" + }, + "3.2.15-30366-arm64": { + "send": "70C3FA8", + "recv": "70C77E0" + }, + "6.9.62-30366-x64": { + "send": "4669760", + "recv": "466BFCC" + }, + "6.9.62-30366-arm64": { + "send": "4189770", + "recv": "418BF88" } } \ No newline at end of file From 0e8ceeb6c9c9fb46c210aa46c2aabad21ced75ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Thu, 5 Dec 2024 11:36:06 +0800 Subject: [PATCH 50/57] refactor: CardChangedEvent --- src/onebot/api/group.ts | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/onebot/api/group.ts b/src/onebot/api/group.ts index 0a57086d..f4d3666b 100644 --- a/src/onebot/api/group.ts +++ b/src/onebot/api/group.ts @@ -51,9 +51,9 @@ export class OneBotGroupApi { if (memberUin && adminUin) { return new OB11GroupBanEvent( this.core, - parseInt(GroupCode), - parseInt(memberUin), - parseInt(adminUin), + +GroupCode, + +memberUin, + +adminUin, duration, subType, ); @@ -98,8 +98,8 @@ export class OneBotGroupApi { } return new OB11GroupMsgEmojiLikeEvent( this.core, - parseInt(groupCode), - parseInt(senderUin), + +groupCode, + +senderUin, MessageUnique.getShortIdByMsgId(replyMsg.msgId)!, [{ emoji_id: emojiId, @@ -111,9 +111,10 @@ export class OneBotGroupApi { async parseCardChangedEvent(msg: RawMessage) { if (msg.senderUin && msg.senderUin !== '0') { const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUid, msg.senderUin); - if (member && member.cardName !== msg.sendMemberName) { + const oldName = member?.cardName || member?.nick || ''; + if (member && oldName !== msg.sendMemberName) { const newCardName = msg.sendMemberName ?? ''; - const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName); + const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, oldName); member.cardName = newCardName; return event; } From 57112c21a29fb561fa73397f4141153c00b65fa7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Thu, 5 Dec 2024 14:17:09 +0800 Subject: [PATCH 51/57] =?UTF-8?q?refactor:=20flag=20handle&onebot=E6=A0=87?= =?UTF-8?q?=E5=87=86=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/friend.ts | 14 ++--- src/core/apis/group.ts | 16 ++---- .../action/extends/GetGroupAddRequest.ts | 18 ++---- .../action/group/GetGroupIgnoredNotifies.ts | 25 +++------ src/onebot/action/group/SetGroupAddRequest.ts | 26 +++++++-- src/onebot/action/system/GetSystemMsg.ts | 56 ++++++------------- src/onebot/action/user/SetFriendAddRequest.ts | 15 +++-- src/onebot/index.ts | 5 +- src/onebot/types/data.ts | 11 ++++ 9 files changed, 81 insertions(+), 105 deletions(-) diff --git a/src/core/apis/friend.ts b/src/core/apis/friend.ts index eff2fcd1..7714d683 100644 --- a/src/core/apis/friend.ts +++ b/src/core/apis/friend.ts @@ -1,4 +1,4 @@ -import { FriendV2 } from '@/core/types'; +import { FriendRequest, FriendV2 } from '@/core/types'; import { BuddyListReqType, InstanceContext, NapCatCore } from '@/core'; import { LimitedHashTable } from '@/common/message-unique'; @@ -79,16 +79,10 @@ export class NTQQFriendApi { return ret; } - async handleFriendRequest(flag: string, accept: boolean) { - const data = flag.split('|'); - if (data.length < 2) { - return; - } - const friendUid = data[0]; - const reqTime = data[1]; + async handleFriendRequest(notify: FriendRequest, accept: boolean) { this.context.session.getBuddyService()?.approvalFriendRequest({ - friendUid: friendUid, - reqTime: reqTime, + friendUid: notify.friendUid, + reqTime: notify.reqTime, accept, }); } diff --git a/src/core/apis/group.ts b/src/core/apis/group.ts index 35674530..60bf065a 100644 --- a/src/core/apis/group.ts +++ b/src/core/apis/group.ts @@ -7,6 +7,7 @@ import { KickMemberV2Req, MemberExtSourceType, NapCatCore, + GroupNotify, } from '@/core'; import { isNumeric, solveAsyncProblem } from '@/common/helper'; import { LimitedHashTable } from '@/common/message-unique'; @@ -120,7 +121,7 @@ export class NTQQGroupApi { } return this.groupMemberCache; } - + async getGroupMember(groupCode: string | number, memberUinOrUid: string | number) { const groupCodeStr = groupCode.toString(); const memberUinOrUidStr = memberUinOrUid.toString(); @@ -288,20 +289,15 @@ export class NTQQGroupApi { return this.context.session.getGroupService().uploadGroupBulletinPic(groupCode, _Pskey, imageurl); } - async handleGroupRequest(flag: string, operateType: NTGroupRequestOperateTypes, reason?: string) { - const flagitem = flag.split('|'); - const groupCode = flagitem[0]; - const seq = flagitem[1]; - const type = parseInt(flagitem[2]); - + async handleGroupRequest(notify: GroupNotify, operateType: NTGroupRequestOperateTypes, reason?: string) { return this.context.session.getGroupService().operateSysNotify( false, { operateType: operateType, targetMsg: { - seq: seq, // 通知序列号 - type: type, - groupCode: groupCode, + seq: notify.seq, // 通知序列号 + type: notify.type, + groupCode: notify.group.groupCode, postscript: reason ?? ' ', // 仅传空值可能导致处理失败,故默认给个空格 }, }); diff --git a/src/onebot/action/extends/GetGroupAddRequest.ts b/src/onebot/action/extends/GetGroupAddRequest.ts index 9f0598db..1469292a 100644 --- a/src/onebot/action/extends/GetGroupAddRequest.ts +++ b/src/onebot/action/extends/GetGroupAddRequest.ts @@ -1,17 +1,7 @@ import { GroupNotifyMsgStatus } from '@/core'; import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; - -interface Notify { - request_id: string; - invitor_uin: number; - invitor_nick?: string; - group_id?: number; - group_name?: string; - checked: boolean; - requester_nick?: string; - actor: number; -} +import { Notify } from '@/onebot/types'; export default class GetGroupAddRequest extends OneBotAction { actionName = ActionName.GetGroupIgnoreAddRequest; @@ -28,13 +18,15 @@ export default class GetGroupAddRequest extends OneBotAction { - actionName = ActionName.GetGroupSystemMsg; + actionName = ActionName.GetGroupIgnoredNotifies; async _handle(): Promise { - const NTQQUserApi = this.core.apis.UserApi; - const NTQQGroupApi = this.core.apis.GroupApi; - const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false, 50); + const SingleScreenNotifies = await this.core.apis.GroupApi.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 invitorUin = SSNotify.user1?.uid ? +await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1.uid) : 0; + const actorUin = SSNotify.user2?.uid ? +await this.core.apis.UserApi.getUinByUidV2(SSNotify.user2.uid) : 0; const commonData = { - request_id: `${SSNotify.group.groupCode}|${SSNotify.seq}|${SSNotify.type}`, + request_id: +SSNotify.seq, invitor_uin: invitorUin, invitor_nick: SSNotify.user1?.nickName, group_id: +SSNotify.group?.groupCode, + message: SSNotify?.postscript, group_name: SSNotify.group?.groupName, checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, actor: actorUin, diff --git a/src/onebot/action/group/SetGroupAddRequest.ts b/src/onebot/action/group/SetGroupAddRequest.ts index 389e71ff..f9f54cf7 100644 --- a/src/onebot/action/group/SetGroupAddRequest.ts +++ b/src/onebot/action/group/SetGroupAddRequest.ts @@ -4,9 +4,9 @@ import { ActionName } from '@/onebot/action/router'; import { Static, Type } from '@sinclair/typebox'; const SchemaData = Type.Object({ - flag: Type.String(), + flag: Type.Union([Type.String(), Type.Number()]), approve: Type.Optional(Type.Union([Type.Boolean(), Type.String()])), - reason: Type.Union([Type.String({ default: ' ' }), Type.Null()]), + reason: Type.Optional(Type.Union([Type.String({ default: ' ' }), Type.Null()])), }); type Payload = Static; @@ -18,10 +18,26 @@ export default class SetGroupAddRequest extends OneBotAction { async _handle(payload: Payload): Promise { const flag = payload.flag.toString(); const approve = payload.approve?.toString() !== 'false'; - await this.core.apis.GroupApi.handleGroupRequest(flag, + const reason = payload.reason ?? ' '; + + let notify = await this.findNotify(flag); + if (!notify) { + throw new Error('No such request'); + } + + await this.core.apis.GroupApi.handleGroupRequest( + notify, approve ? NTGroupRequestOperateTypes.KAGREE : NTGroupRequestOperateTypes.KREFUSE, - payload.reason ?? ' ', + reason, ); return null; } -} + + private async findNotify(flag: string) { + let notify = (await this.core.apis.GroupApi.getSingleScreenNotifies(false, 100)).find(e => e.seq == flag); + if (!notify) { + notify = (await this.core.apis.GroupApi.getSingleScreenNotifies(true, 100)).find(e => e.seq == flag); + } + return notify; + } +} \ No newline at end of file diff --git a/src/onebot/action/system/GetSystemMsg.ts b/src/onebot/action/system/GetSystemMsg.ts index a8a22228..01605869 100644 --- a/src/onebot/action/system/GetSystemMsg.ts +++ b/src/onebot/action/system/GetSystemMsg.ts @@ -1,59 +1,39 @@ import { GroupNotifyMsgStatus } from '@/core'; import { OneBotAction } from '@/onebot/action/OneBotAction'; import { ActionName } from '@/onebot/action/router'; - -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; -} +import { Notify } from '@/onebot/types'; interface RetData { InvitedRequest: Notify[]; - join_requests: JoinRequest[]; + join_requests: Notify[]; } export class GetGroupSystemMsg extends OneBotAction { actionName = ActionName.GetGroupSystemMsg; async _handle(): Promise { - const NTQQUserApi = this.core.apis.UserApi; - const NTQQGroupApi = this.core.apis.GroupApi; - const SingleScreenNotifies = await NTQQGroupApi.getSingleScreenNotifies(false, 50); + const SingleScreenNotifies = await this.core.apis.GroupApi.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 invitorUin = SSNotify.user1?.uid ? +await this.core.apis.UserApi.getUinByUidV2(SSNotify.user1.uid) : 0; + const actorUin = SSNotify.user2?.uid ? +await this.core.apis.UserApi.getUinByUidV2(SSNotify.user2.uid) : 0; + const commonData = { + request_id: +SSNotify.seq, + invitor_uin: invitorUin, + invitor_nick: SSNotify.user1?.nickName, + group_id: +SSNotify.group?.groupCode, + message: SSNotify?.postscript, + group_name: SSNotify.group?.groupName, + checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, + actor: actorUin, + requester_nick: SSNotify.user1?.nickName, + }; if (SSNotify.type === 1) { - retData.InvitedRequest.push({ - 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, - checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, - actor: actorUin, - }); + retData.InvitedRequest.push(commonData); } else if (SSNotify.type === 7) { - retData.join_requests.push({ - request_id: `${SSNotify.group.groupCode}|${SSNotify.seq}|${SSNotify.type}`, - invitor_uin: invitorUin, - requester_nick: SSNotify.user1?.nickName, - group_id: +SSNotify.group?.groupCode, - group_name: SSNotify.group?.groupName, - checked: SSNotify.status !== GroupNotifyMsgStatus.KUNHANDLE, - actor: actorUin, - }); + retData.join_requests.push(commonData); } }); diff --git a/src/onebot/action/user/SetFriendAddRequest.ts b/src/onebot/action/user/SetFriendAddRequest.ts index 1170b552..aaa739c7 100644 --- a/src/onebot/action/user/SetFriendAddRequest.ts +++ b/src/onebot/action/user/SetFriendAddRequest.ts @@ -3,7 +3,7 @@ import { ActionName } from '@/onebot/action/router'; import { Static, Type } from '@sinclair/typebox'; const SchemaData = Type.Object({ - flag: Type.String(), + flag: Type.Union([Type.String(), Type.Number()]), approve: Type.Optional(Type.Union([Type.String(), Type.Boolean()])), remark: Type.Optional(Type.String()) }); @@ -16,14 +16,13 @@ export default class SetFriendAddRequest extends OneBotAction { async _handle(payload: Payload): Promise { const approve = payload.approve?.toString() !== 'false'; - await this.core.apis.FriendApi.handleFriendRequest(payload.flag, approve); + let notify = (await this.core.apis.FriendApi.getBuddyReq()).buddyReqs.find(e => e.reqTime == payload.flag.toString()); + if (!notify) { + throw new Error('No such request'); + } + await this.core.apis.FriendApi.handleFriendRequest(notify, approve); if (payload.remark) { - const data = payload.flag.split('|'); - if (data.length < 2) { - throw new Error('Invalid flag'); - } - const friendUid = data[0]; - await this.core.apis.FriendApi.setBuddyRemark(friendUid, payload.remark); + await this.core.apis.FriendApi.setBuddyRemark(notify.friendUid, payload.remark); } return null; } diff --git a/src/onebot/index.ts b/src/onebot/index.ts index 5c9c4419..29fc73f7 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -333,7 +333,7 @@ export class NapCatOneBot11Adapter { this.core, +requesterUin, req.extWords, - req.friendUid + '|' + req.reqTime + req.reqTime ) ); } catch (e) { @@ -365,8 +365,7 @@ export class NapCatOneBot11Adapter { if (notifyTime < this.bootTime) { continue; } - - const flag = notify.group.groupCode + '|' + notify.seq + '|' + notify.type; + const flag = notify.seq; this.context.logger.logDebug('收到群通知', notify); if ( [GroupNotifyMsgType.REQUEST_JOIN_NEED_ADMINI_STRATOR_PASS].includes(notify.type) && diff --git a/src/onebot/types/data.ts b/src/onebot/types/data.ts index 3039ba0b..8b3acab2 100644 --- a/src/onebot/types/data.ts +++ b/src/onebot/types/data.ts @@ -11,6 +11,17 @@ export interface OB11User { categoryName?: string; // 分组名称 categoryId?: number; // 分组ID 999为特别关心 } +export interface Notify { + request_id: number; + invitor_uin: number; + invitor_nick?: string; + group_id?: number; + group_name?: string; + message?: string; + checked: boolean; + actor: number; + requester_nick?: string; +} export enum OB11UserSex { male = 'male', // 男性 From 4aa24b5d6771643ffe442bea82668f4bc89f2dfb Mon Sep 17 00:00:00 2001 From: Mlikiowa Date: Thu, 5 Dec 2024 06:17:46 +0000 Subject: [PATCH 52/57] release: v4.2.24 --- manifest.json | 2 +- package.json | 2 +- src/common/version.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/manifest.json b/manifest.json index 3d223b91..6453a0aa 100644 --- a/manifest.json +++ b/manifest.json @@ -4,7 +4,7 @@ "name": "NapCatQQ", "slug": "NapCat.Framework", "description": "高性能的 OneBot 11 协议实现", - "version": "4.2.23", + "version": "4.2.24", "icon": "./logo.png", "authors": [ { diff --git a/package.json b/package.json index ded51e7b..bc82c9c5 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "napcat", "private": true, "type": "module", - "version": "4.2.23", + "version": "4.2.24", "scripts": { "build:universal": "npm run build:webui && vite build --mode universal || exit 1", "build:framework": "npm run build:webui && vite build --mode framework || exit 1", diff --git a/src/common/version.ts b/src/common/version.ts index d43e0bff..4fada46c 100644 --- a/src/common/version.ts +++ b/src/common/version.ts @@ -1 +1 @@ -export const napCatVersion = '4.2.23'; +export const napCatVersion = '4.2.24'; From 82afb88e53cf238150a0e7ee9148e6a817e6fb01 Mon Sep 17 00:00:00 2001 From: Nanako Date: Thu, 5 Dec 2024 14:33:20 +0800 Subject: [PATCH 53/57] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 9be9f2c8..9a54e4ed 100644 --- a/README.md +++ b/README.md @@ -37,11 +37,11 @@ NapCatQQ 是现代化的基于 NTQQ 的 Bot 协议端实现 [QQ Group](https://qm.qq.com/q/I6LU87a0Yq) ## 性能设计/协议标准 -NapCat 已实现90%+的OneBot/GoCQ标准接口,并提供兼容性保留接口,其设计理念遵守 无数据库/异步优先/后台刷新 的性能思想。 +NapCat 已实现90%+的 OneBot / GoCQ 标准接口,并提供兼容性保留接口,其设计理念遵守 无数据库/异步优先/后台刷新 的性能思想。 -由此设计带来一系列好处,在开发中,获取群员列表通常小于50Ms,单条文本消息发送在320Ms以内,在1k+的群聊流程运行,同时带来一些副作用,上报数据中大量使用Magic生成字段, 消息Id无法持久,无法上报撤回消息原始内容。 +由此设计带来一系列好处,在开发中,获取群员列表通常小于50Ms,单条文本消息发送在320Ms以内,在1k+的群聊流畅运行,同时带来一些副作用,上报数据中大量使用Magic生成字段,消息Id无法持久,无法上报撤回消息原始内容。 -NapCat在设计理念下遵守OneBot规范大多数要求并且积极改进,任何合理的标准化issue与pr将被接收。 +NapCat 在设计理念下遵守 OneBot 规范大多数要求并且积极改进,任何合理的标准化 Issue 与 Pr 将被接收。 ## 感谢他们 感谢 [Lagrange](https://github.com/LagrangeDev/Lagrange.Core) 对本项目的大力支持 参考部分代码 已获授权 From 7cf3be8333713ffe84b115380f6ef30159dc0e1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Thu, 5 Dec 2024 14:42:45 +0800 Subject: [PATCH 54/57] refactor: predict time --- src/onebot/api/msg.ts | 64 +++++++++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index e512386d..ef0dde6e 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -891,51 +891,55 @@ export class OneBotMsgApi { if (!sendElements.length) { throw new Error('消息体无法解析, 请检查是否发送了不支持的消息类型'); } - let totalSize = 0; - let timeout = 10000; - try { - for (const fileElement of sendElements) { - if (fileElement.elementType === ElementType.PTT) { - totalSize += (await fsPromise.stat(fileElement.pttElement.filePath)).size; + + const calculateTotalSize = async (elements: SendMessageElement[]): Promise => { + const sizePromises = elements.map(async element => { + switch (element.elementType) { + case ElementType.PTT: + return (await fsPromise.stat(element.pttElement.filePath)).size; + case ElementType.FILE: + return (await fsPromise.stat(element.fileElement.filePath)).size; + case ElementType.VIDEO: + return (await fsPromise.stat(element.videoElement.filePath)).size; + case ElementType.PIC: + return (await fsPromise.stat(element.picElement.sourcePath)).size; + default: + return 0; } - if (fileElement.elementType === ElementType.FILE) { - totalSize += (await fsPromise.stat(fileElement.fileElement.filePath)).size; - } - if (fileElement.elementType === ElementType.VIDEO) { - totalSize += (await fsPromise.stat(fileElement.videoElement.filePath)).size; - } - if (fileElement.elementType === ElementType.PIC) { - totalSize += (await fsPromise.stat(fileElement.picElement.sourcePath)).size; - } - } - //且 PredictTime ((totalSize / 1024 / 512) * 1000)不等于Nan - const PredictTime = totalSize / 1024 / 256 * 1000; - if (!Number.isNaN(PredictTime)) { - timeout += PredictTime;// 10S Basic Timeout + PredictTime( For File 512kb/s ) - } - } catch (e) { + }); + const sizes = await Promise.all(sizePromises); + return sizes.reduce((total, size) => total + size, 0); + }; + + const totalSize = await calculateTotalSize(sendElements).catch(e => { this.core.context.logger.logError('发送消息计算预计时间异常', e); - } + return 0; + }); + + const timeout = 10000 + (totalSize / 1024 / 256 * 1000); + const returnMsg = await this.core.apis.MsgApi.sendMsg(peer, sendElements, waitComplete, timeout); if (!returnMsg) throw new Error('发送消息失败'); + returnMsg.id = MessageUnique.createUniqueMsgId({ chatType: peer.chatType, guildId: '', peerUid: peer.peerUid, }, returnMsg.msgId); - - setTimeout(() => { - deleteAfterSentFiles.forEach(async file => { + + setTimeout(async () => { + const deletePromises = deleteAfterSentFiles.map(async file => { try { if (await fsPromise.access(file, constants.W_OK).then(() => true).catch(() => false)) { - fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError('发送消息删除文件失败', e)); + await fsPromise.unlink(file); } - } catch (error) { - this.core.context.logger.logError('发送消息删除文件失败', (error as Error).message); + } catch (e) { + this.core.context.logger.logError('发送消息删除文件失败', e); } }); + await Promise.all(deletePromises); }, 60000); - + return returnMsg; } From 9a33039d73eb6c74fa84eca17af6ae0353a60bdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Thu, 5 Dec 2024 14:45:12 +0800 Subject: [PATCH 55/57] =?UTF-8?q?fix:=20=E6=9A=82=E6=97=B6=E7=A7=BB?= =?UTF-8?q?=E9=99=A4=20QunAlbum?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/webapi.ts | 46 ----------------------------------------- 1 file changed, 46 deletions(-) diff --git a/src/core/apis/webapi.ts b/src/core/apis/webapi.ts index 8cb147b1..933a0941 100644 --- a/src/core/apis/webapi.ts +++ b/src/core/apis/webapi.ts @@ -366,50 +366,4 @@ export class NTQQWebApi { return post; } - - async uploadQunAlbumSlice(path: string, session: string, skey: string, pskey: string, uin: string, slice_size: number) { - const img_size = statSync(path).size; - const img_name = basename(path); - let seq = 0; - let offset = 0; - const GTK = this.getBknFromSKey(pskey); - const cookie = `p_uin=${uin}; p_skey=${pskey}; skey=${skey}; uin=${uin}`; - - const stream = createReadStream(path, { highWaterMark: slice_size }); - - for await (const chunk of stream) { - const end = Math.min(offset + chunk.length, img_size); - const boundary = `----WebKitFormBoundary${Math.random().toString(36).substring(2)}`; - const formData = await RequestUtil.createFormData(boundary, path); - - const api = `https://h5.qzone.qq.com/webapp/json/sliceUpload/FileUpload?seq=${seq}&retry=0&offset=${offset}&end=${end}&total=${img_size}&type=form&g_tk=${GTK}`; - const body = { - uin: uin, - appid: "qun", - session: session, - offset: offset, - data: formData, - checksum: "", - check_type: 0, - retry: 0, - seq: seq, - end: end, - cmd: "FileUpload", - slice_size: slice_size, - "biz_req.iUploadType": 0 - }; - - const post = await RequestUtil.HttpGetJson(api, 'POST', body, { - "Cookie": cookie, - "Content-Type": `multipart/form-data; boundary=${boundary}` - }); - - offset += chunk.length; - seq++; - } - } - async uploadQunAlbum(path: string, albumId: string, group: string, skey: string, pskey: string, uin: string) { - const session = (await this.createQunAlbumSession(group, albumId, group, path, skey, pskey, uin) as { data: { session: string } }).data.session; - return await this.uploadQunAlbumSlice(path, session, skey, pskey, uin, 1024 * 1024); - } } From 9f31cdbf5bcb05dbc0ec60066ec0b0a6879c27dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Thu, 5 Dec 2024 14:46:05 +0800 Subject: [PATCH 56/57] =?UTF-8?q?fix:=20=E7=A7=BB=E9=99=A4=E4=B8=8D?= =?UTF-8?q?=E4=BD=BF=E7=94=A8api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/core/apis/sign.ts | 25 ------------------------- 1 file changed, 25 deletions(-) delete mode 100644 src/core/apis/sign.ts diff --git a/src/core/apis/sign.ts b/src/core/apis/sign.ts deleted file mode 100644 index c8e2ad6f..00000000 --- a/src/core/apis/sign.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { InstanceContext, NapCatCore } from '..'; - -export class NTQQMusicSignApi { - context: InstanceContext; - core: NapCatCore; - - constructor(context: InstanceContext, core: NapCatCore) { - this.context = context; - this.core = core; - } - //转换外域名为 https://qq.ugcimg.cn/v1/cpqcbu4b8870i61bde6k7cbmjgejq8mr3in82qir4qi7ielffv5slv8ck8g42novtmev26i233ujtuab6tvu2l2sjgtupfr389191v00s1j5oh5325j5eqi40774jv1i/khovifoh7jrqd6eahoiv7koh8o - //https://cgi.connect.qq.com/qqconnectopen/openapi/change_image_url?url=https://th.bing.com/th?id=OSK.b8ed36f1fb1889de6dc84fd81c187773&w=46&h=46&c=11&rs=1&qlt=80&o=6&dpr=2&pid=SANGAM - - //外域名不行得走qgroup中转 - //https://proxy.gtimg.cn/tx_tls_gate=y.qq.com/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg - - //可外域名 - //https://pic.ugcimg.cn/500955bdd6657ecc8e82e02d2df06800/jpg1 - - //QQ音乐gtimg接口 - //https://y.gtimg.cn/music/photo_new/T002R800x800M000000y5gq7449K9I.jpg?max_age=2592000 - - //还有一处公告上传可以上传高质量图片 持久为qq域名 -} - From bb53f245cfc1842e270d5b727e53e85c618b3d78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=89=8B=E7=93=9C=E4=B8=80=E5=8D=81=E9=9B=AA?= Date: Thu, 5 Dec 2024 14:50:27 +0800 Subject: [PATCH 57/57] style: lint check --- src/common/file.ts | 37 +++++++++-------- src/common/log.ts | 2 +- src/core/apis/file.ts | 20 +++++----- src/core/apis/user.ts | 4 +- .../transformer/proto/message/groupAdmin.ts | 6 +-- src/onebot/action/group/SetGroupAddRequest.ts | 2 +- src/onebot/action/group/SetGroupBan.ts | 2 +- src/onebot/action/user/SetFriendAddRequest.ts | 2 +- src/onebot/api/msg.ts | 40 +++++++++---------- src/onebot/index.ts | 17 ++++++-- 10 files changed, 72 insertions(+), 60 deletions(-) diff --git a/src/common/file.ts b/src/common/file.ts index aa1a019e..6dac529b 100644 --- a/src/common/file.ts +++ b/src/common/file.ts @@ -182,25 +182,28 @@ export async function uriToLocalFile(dir: string, uri: string): Promise e?.ext ?? '').catch(e => ''); + const extOrEmpty = await fileTypeFromFile(filePath).then(e => e?.ext ?? '').catch(e => ''); const ext = extOrEmpty ? `.${extOrEmpty}` : ''; let fileName = `${path.basename(filePath)}`; if (fileName.indexOf('.') === -1) { @@ -305,18 +305,18 @@ export class NTQQFileApi { element.elementType === ElementType.FILE ) { switch (element.elementType) { - case ElementType.PIC: + case ElementType.PIC: element.picElement!.sourcePath = elementResults[elementIndex]; - break; - case ElementType.VIDEO: + break; + case ElementType.VIDEO: element.videoElement!.filePath = elementResults[elementIndex]; - break; - case ElementType.PTT: + break; + case ElementType.PTT: element.pttElement!.filePath = elementResults[elementIndex]; - break; - case ElementType.FILE: + break; + case ElementType.FILE: element.fileElement!.filePath = elementResults[elementIndex]; - break; + break; } elementIndex++; } diff --git a/src/core/apis/user.ts b/src/core/apis/user.ts index f2b9bb0a..010318d1 100644 --- a/src/core/apis/user.ts +++ b/src/core/apis/user.ts @@ -176,7 +176,7 @@ export class NTQQUserApi { const services = [ () => this.context.session.getUixConvertService().getUid([Uin]).then((data) => data.uidInfo.get(Uin)).catch(() => undefined), () => promisify> - (this.context.session.getProfileService().getUidByUin)('FriendsServiceImpl', [Uin]).then((data) => data.get(Uin)).catch(() => undefined), + (this.context.session.getProfileService().getUidByUin)('FriendsServiceImpl', [Uin]).then((data) => data.get(Uin)).catch(() => undefined), () => this.context.session.getGroupService().getUidByUins([Uin]).then((data) => data.uids.get(Uin)).catch(() => undefined), () => this.getUserDetailInfoByUin(Uin).then((data) => data.detail.uid).catch(() => undefined), ]; @@ -198,7 +198,7 @@ export class NTQQUserApi { () => this.context.session.getUixConvertService().getUin([Uid]).then((data) => data.uinInfo.get(Uid)).catch(() => undefined), () => this.context.session.getGroupService().getUinByUids([Uid]).then((data) => data.uins.get(Uid)).catch(() => undefined), () => promisify> - (this.context.session.getProfileService().getUinByUid)('FriendsServiceImpl', [Uid]).then((data) => data.get(Uid)).catch(() => undefined), + (this.context.session.getProfileService().getUinByUid)('FriendsServiceImpl', [Uid]).then((data) => data.get(Uid)).catch(() => undefined), () => this.core.apis.FriendApi.getBuddyIdMap(true).then((data) => data.getKey(Uid)).catch(() => undefined), () => this.getUserDetailInfo(Uid).then((data) => data.uin).catch(() => undefined), ]; diff --git a/src/core/packet/transformer/proto/message/groupAdmin.ts b/src/core/packet/transformer/proto/message/groupAdmin.ts index 0aa130b9..42030063 100644 --- a/src/core/packet/transformer/proto/message/groupAdmin.ts +++ b/src/core/packet/transformer/proto/message/groupAdmin.ts @@ -3,16 +3,16 @@ 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), -} \ No newline at end of file +}; \ No newline at end of file diff --git a/src/onebot/action/group/SetGroupAddRequest.ts b/src/onebot/action/group/SetGroupAddRequest.ts index f9f54cf7..97a2540b 100644 --- a/src/onebot/action/group/SetGroupAddRequest.ts +++ b/src/onebot/action/group/SetGroupAddRequest.ts @@ -20,7 +20,7 @@ export default class SetGroupAddRequest extends OneBotAction { const approve = payload.approve?.toString() !== 'false'; const reason = payload.reason ?? ' '; - let notify = await this.findNotify(flag); + const notify = await this.findNotify(flag); if (!notify) { throw new Error('No such request'); } diff --git a/src/onebot/action/group/SetGroupBan.ts b/src/onebot/action/group/SetGroupBan.ts index 1a02dcc7..7b73a007 100644 --- a/src/onebot/action/group/SetGroupBan.ts +++ b/src/onebot/action/group/SetGroupBan.ts @@ -18,7 +18,7 @@ export default class SetGroupBan extends OneBotAction { const uid = await this.core.apis.UserApi.getUidByUinV2(payload.user_id.toString()); if (!uid) throw new Error('uid error'); await this.core.apis.GroupApi.banMember(payload.group_id.toString(), - [{ uid: uid, timeStamp: +payload.duration}]); + [{ uid: uid, timeStamp: +payload.duration }]); return null; } } diff --git a/src/onebot/action/user/SetFriendAddRequest.ts b/src/onebot/action/user/SetFriendAddRequest.ts index aaa739c7..dfe29e6c 100644 --- a/src/onebot/action/user/SetFriendAddRequest.ts +++ b/src/onebot/action/user/SetFriendAddRequest.ts @@ -16,7 +16,7 @@ export default class SetFriendAddRequest extends OneBotAction { async _handle(payload: Payload): Promise { const approve = payload.approve?.toString() !== 'false'; - let notify = (await this.core.apis.FriendApi.getBuddyReq()).buddyReqs.find(e => e.reqTime == payload.flag.toString()); + const notify = (await this.core.apis.FriendApi.getBuddyReq()).buddyReqs.find(e => e.reqTime == payload.flag.toString()); if (!notify) { throw new Error('No such request'); } diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index ef0dde6e..09564992 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -160,7 +160,7 @@ export class OneBotMsgApi { type: element?.pokeType?.toString() ?? '0', id: faceIndex.toString(), } - } + }; } @@ -461,7 +461,7 @@ export class OneBotMsgApi { }, [OB11MessageDataType.face]: async ({ data: { id } }) => { - let parsedFaceId = +id; + const parsedFaceId = +id; // 从face_config.json中获取表情名称 const sysFaces = faceConfig.sysface; const face: any = sysFaces.find((systemFace) => systemFace.QSid === parsedFaceId.toString()); @@ -895,16 +895,16 @@ export class OneBotMsgApi { const calculateTotalSize = async (elements: SendMessageElement[]): Promise => { const sizePromises = elements.map(async element => { switch (element.elementType) { - case ElementType.PTT: - return (await fsPromise.stat(element.pttElement.filePath)).size; - case ElementType.FILE: - return (await fsPromise.stat(element.fileElement.filePath)).size; - case ElementType.VIDEO: - return (await fsPromise.stat(element.videoElement.filePath)).size; - case ElementType.PIC: - return (await fsPromise.stat(element.picElement.sourcePath)).size; - default: - return 0; + case ElementType.PTT: + return (await fsPromise.stat(element.pttElement.filePath)).size; + case ElementType.FILE: + return (await fsPromise.stat(element.fileElement.filePath)).size; + case ElementType.VIDEO: + return (await fsPromise.stat(element.videoElement.filePath)).size; + case ElementType.PIC: + return (await fsPromise.stat(element.picElement.sourcePath)).size; + default: + return 0; } }); const sizes = await Promise.all(sizePromises); @@ -970,14 +970,14 @@ export class OneBotMsgApi { } groupChangDecreseType2String(type: number): GroupDecreaseSubType { switch (type) { - case 130: - return 'leave'; - case 131: - return 'kick'; - case 3: - return 'kick_me'; - default: - return 'kick'; + case 130: + return 'leave'; + case 131: + return 'kick'; + case 3: + return 'kick_me'; + default: + return 'kick'; } } diff --git a/src/onebot/index.ts b/src/onebot/index.ts index 29fc73f7..95035243 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -521,21 +521,27 @@ export class NapCatOneBot11Adapter { // 群名片修改事件解析 任何都该判断 if (message.senderUin && message.senderUin !== '0') { const cardChangedEvent = await this.apis.GroupApi.parseCardChangedEvent(message); - cardChangedEvent && await this.networkManager.emitEvent(cardChangedEvent); + if (cardChangedEvent) { + await this.networkManager.emitEvent(cardChangedEvent); + } } if (message.msgType === NTMsgType.KMSGTYPEFILE) { // 文件为单元素消息 const elementWrapper = message.elements.find(e => !!e.fileElement); if (elementWrapper?.fileElement) { const uploadGroupFileEvent = await this.apis.GroupApi.parseGroupUploadFileEvene(message, elementWrapper.fileElement, elementWrapper); - uploadGroupFileEvent && await this.networkManager.emitEvent(uploadGroupFileEvent); + if (uploadGroupFileEvent) { + await this.networkManager.emitEvent(uploadGroupFileEvent); + } } } else if (message.msgType === NTMsgType.KMSGTYPEGRAYTIPS) { // 灰条为单元素消息 const grayTipElement = message.elements[0].grayTipElement; if (grayTipElement) { const event = await this.apis.GroupApi.parseGrayTipElement(message, grayTipElement); - event && await this.networkManager.emitEvent(event); + if (event) { + await this.networkManager.emitEvent(event); + } } } } catch (e) { @@ -550,7 +556,10 @@ export class NapCatOneBot11Adapter { const grayTipElement = message.elements[0].grayTipElement; if (grayTipElement) { const event = await this.apis.MsgApi.parsePrivateMsgEvent(message, grayTipElement); - event && await this.networkManager.emitEvent(event); + if (event) { + await this.networkManager.emitEvent(event); + } + } } } catch (e) {