From 7ab6a10fc9cd26ab5fc37daad26100b91168ebfa Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Sun, 27 Oct 2024 04:16:15 +0800 Subject: [PATCH 1/7] refactor & fix: refactor msg entity & adjust some wrong definition --- src/core/apis/file.ts | 2 +- src/core/entities/msg.ts | 244 +++++++------------------------ src/core/packet/msg/converter.ts | 135 ++++++++++------- src/onebot/api/msg.ts | 1 + 4 files changed, 136 insertions(+), 246 deletions(-) diff --git a/src/core/apis/file.ts b/src/core/apis/file.ts index 5202850a..de41cda5 100644 --- a/src/core/apis/file.ts +++ b/src/core/apis/file.ts @@ -238,7 +238,7 @@ export class NTQQFileApi { fileName: fileName, filePath: path, md5HexStr: md5, - fileSize: fileSize, + fileSize: fileSize.toString(), duration: duration ?? 1, formatType: 1, voiceType: 1, diff --git a/src/core/entities/msg.ts b/src/core/entities/msg.ts index 0c77e3b7..1a4a38ce 100644 --- a/src/core/entities/msg.ts +++ b/src/core/entities/msg.ts @@ -27,94 +27,70 @@ export interface GetFileListParam { export enum ElementType { UNKNOWN = 0, - TEXT = 1, - PIC = 2, - FILE = 3, - PTT = 4, - VIDEO = 5, - FACE = 6, - REPLY = 7, - + GreyTip = 8, // “小灰条”,包括拍一拍 (Poke)、撤回提示等 WALLET = 9, - - /** - * “小灰条”,包括拍一拍 (Poke)、撤回提示等 - */ - GreyTip = 8, - ARK = 10, - MFACE = 11, - LIVEGIFT = 12, - STRUCTLONGMSG = 13, - MARKDOWN = 14, - GIPHY = 15, - MULTIFORWARD = 16, - INLINEKEYBOARD = 17, - INTEXTGIFT = 18, - CALENDAR = 19, - YOLOGAMERESULT = 20, - AVRECORD = 21, - FEED = 22, - TOFURECORD = 23, - ACEBUBBLE = 24, - ACTIVITY = 25, - TOFU = 26, - FACEBUBBLE = 27, - SHARELOCATION = 28, - TASKTOPMSG = 29, - RECOMMENDEDMSG = 43, - ACTIONBAR = 44 } +type ElementFullBase = Omit; + +type ElementBase< + K extends keyof ElementFullBase, + S extends Partial<{ [P in K]: keyof NonNullable | Array> }> = {} +> = { + [P in K]: + S[P] extends Array + ? Pick, U & keyof NonNullable> + : S[P] extends keyof NonNullable + ? Pick, S[P]> + : NonNullable; +}; + +export interface SendElementBase { + elementType: ET; + elementId: string; + extBufForUI?: string; +} + export interface ActionBarElement { rows: InlineKeyboardRow[]; botAppid: string; } -export interface SendActionBarElement { - elementType: ElementType.ACTIONBAR; - elementId: string; - actionBarElement: ActionBarElement; -} - export interface RecommendedMsgElement { rows: InlineKeyboardRow[]; botAppid: string; } -export interface SendRecommendedMsgElement { - elementType: ElementType.RECOMMENDEDMSG; - elementId: string; - recommendedMsgElement: RecommendedMsgElement; -} +export type SendRecommendedMsgElement = SendElementBase & ElementBase<'recommendedMsgElement'>; export interface InlineKeyboardButton { id: string; @@ -171,11 +147,7 @@ export enum NTMsgType { KMSGTYPEWALLET = 10 } -export interface SendTaskTopMsgElement { - elementType: ElementType.TASKTOPMSG; - elementId: string; - taskTopMsgElement: TaskTopMsgElement; -} +export type SendTaskTopMsgElement = SendElementBase & ElementBase<'taskTopMsgElement'>; export interface TofuRecordElement { type: number; @@ -194,11 +166,7 @@ export interface TofuRecordElement { onscreennotify: boolean; } -export interface SendTofuRecordElement { - elementType: ElementType.TOFURECORD; - elementId: string; - tofuRecordElement: TofuRecordElement; -} +export type SendTofuRecordElement = SendElementBase & ElementBase<'tofuRecordElement'>; export interface FaceBubbleElement { faceCount: number; @@ -216,12 +184,7 @@ export interface FaceBubbleElement { }; } -export interface SendFaceBubbleElement { - elementType: ElementType.FACEBUBBLE; - elementId: string; - faceBubbleElement: FaceBubbleElement; - -} +export type SendFaceBubbleElement = SendElementBase & ElementBase<'faceBubbleElement'>; export interface AvRecordElement { type: number; @@ -232,11 +195,7 @@ export interface AvRecordElement { extraType: number; } -export interface SendavRecordElement { - elementType: ElementType.AVRECORD; - elementId: string; - avRecordElement: AvRecordElement; -} +export type SendAvRecordElement = SendElementBase & ElementBase<'avRecordElement'>; export interface YoloUserInfo { uid: string; @@ -245,24 +204,13 @@ export interface YoloUserInfo { bizId: string; } -export interface SendInlineKeyboardElement { - elementType: ElementType.INLINEKEYBOARD; - elementId: string; - inlineKeyboardElement: { - rows: number; - botAppid: string; - }; - -} +export type SendInlineKeyboardElement = SendElementBase & ElementBase<'inlineKeyboardElement'>; export interface YoloGameResultElement { UserInfo: YoloUserInfo[]; } -export interface SendYoloGameResultElement { - elementType: ElementType.YOLOGAMERESULT; - yoloGameResultElement: YoloGameResultElement; -} +export type SendYoloGameResultElement = SendElementBase & ElementBase<'yoloGameResultElement'>; export interface GiphyElement { id: string; @@ -271,17 +219,9 @@ export interface GiphyElement { height: number; } -export interface SendGiphyElement { - elementType: ElementType.GIPHY; - elementId: string; - giphyElement: GiphyElement; -} +export type SendGiphyElement = SendElementBase & ElementBase<'giphyElement'>; -export interface SendWalletElement { - elementType: ElementType.UNKNOWN;//不做 设置位置 - elementId: string; - walletElement: Record; -} +export type SendWalletElement = SendElementBase & ElementBase<'walletElement'>; export interface CalendarElement { summary: string; @@ -291,49 +231,16 @@ export interface CalendarElement { schema: string; } -export interface SendCalendarElement { - elementType: ElementType.CALENDAR; - elementId: string; - calendarElement: CalendarElement; -} +export type SendCalendarElement = SendElementBase & ElementBase<'calendarElement'>; -export interface SendliveGiftElement { - elementType: ElementType.LIVEGIFT; - elementId: string; - liveGiftElement: Record; -} +export type SendLiveGiftElement = SendElementBase & ElementBase<'liveGiftElement'>; -export interface SendTextElement { - elementType: ElementType.TEXT; - elementId: string; - textElement: { - content: string; - atType: number; - atUid: string; - atTinyId: string; - atNtUid: string; - }; -} +export type SendTextElement = SendElementBase & ElementBase<'textElement'>; -export interface SendPttElement { - elementType: ElementType.PTT; - elementId: string; - pttElement: { - fileName: string; - filePath: string; - md5HexStr: string; - fileSize: number; - duration: number; // 单位是秒 - formatType: number; - voiceType: number; - voiceChangeType: number; - canConvert2Text: boolean; - waveAmplitudes: number[]; - fileSubId: string; - playState: number; - autoConvertText: number; - }; -} +export type SendPttElement = SendElementBase & ElementBase<'pttElement', { + pttElement: ['fileName', 'filePath', 'md5HexStr', 'fileSize', 'duration', 'formatType', 'voiceType', + 'voiceChangeType', 'canConvert2Text', 'waveAmplitudes', 'fileSubId', 'playState', 'autoConvertText'] +}>; export enum PicType { gif = 2000, @@ -359,11 +266,7 @@ export enum NTMsgAtType { ATTYPEUNKNOWN = 0 } -export interface SendPicElement { - elementType: ElementType.PIC; - elementId: string; - picElement: PicElement; -} +export type SendPicElement = SendElementBase & ElementBase<'picElement'>; export interface ReplyElement { sourceMsgIdInRecords?: string; @@ -375,53 +278,27 @@ export interface ReplyElement { replyMsgClientSeq?: string; } -export interface SendReplyElement { - elementType: ElementType.REPLY; - elementId: string; - replyElement: ReplyElement; -} +export type SendReplyElement = SendElementBase & ElementBase<'replyElement'>; -export interface SendFaceElement { - elementType: ElementType.FACE; - elementId: string; - faceElement: FaceElement; -} +export type SendFaceElement = SendElementBase & ElementBase<'faceElement'>; -export interface SendMarketFaceElement { - elementType: ElementType.MFACE; - marketFaceElement: MarketFaceElement; -} +export type SendMarketFaceElement = SendElementBase & ElementBase<'marketFaceElement'>; -export interface SendStructLongMsgElement { - elementType: ElementType.STRUCTLONGMSG; - elementId: string; - structLongMsgElement: StructLongMsgElement; -} +export type SendStructLongMsgElement = SendElementBase & ElementBase<'structLongMsgElement'>; export interface StructLongMsgElement { xmlContent: string; resId: string; } -export interface SendactionBarElement { - elementType: ElementType.ACTIONBAR; - elementId: string; - actionBarElement: { - rows: number; - botAppid: string; - }; -} +export type SendActionBarElement = SendElementBase & ElementBase<'actionBarElement'>; export interface ShareLocationElement { text: string; ext: string; } -export interface SendShareLocationElement { - elementType: ElementType.SHARELOCATION; - elementId: string; - shareLocationElement?: ShareLocationElement; -} +export type SendShareLocationElement = SendElementBase & ElementBase<'shareLocationElement'>; export interface FileElement { fileMd5?: string; @@ -441,29 +318,13 @@ export interface FileElement { fileBizId?: number; } -export interface SendFileElement { - elementType: ElementType.FILE; - elementId: string; - fileElement: FileElement; -} +export type SendFileElement = SendElementBase & ElementBase<'fileElement'>; -export interface SendVideoElement { - elementType: ElementType.VIDEO; - elementId: string; - videoElement: VideoElement; -} +export type SendVideoElement = SendElementBase & ElementBase<'videoElement'>; -export interface SendArkElement { - elementType: ElementType.ARK; - elementId: string; - arkElement: ArkElement; -} +export type SendArkElement = SendElementBase & ElementBase<'arkElement'>; -export interface SendMarkdownElement { - elementType: ElementType.MARKDOWN; - elementId: string; - markdownElement: MarkdownElement; -} +export type SendMarkdownElement = SendElementBase & ElementBase<'markdownElement'>; export type SendMessageElement = SendTextElement | SendPttElement | SendPicElement | SendReplyElement | SendFaceElement | SendMarketFaceElement | SendFileElement | @@ -480,7 +341,7 @@ export interface TextElement { export interface MessageElement { elementType: ElementType, elementId: string, - extBufForUI: string,//"0x", + extBufForUI?: string, //"0x", textElement?: TextElement; faceElement?: FaceElement, marketFaceElement?: MarketFaceElement, @@ -509,7 +370,6 @@ export interface MessageElement { taskTopMsgElement?: TaskTopMsgElement, recommendedMsgElement?: RecommendedMsgElement, actionBarElement?: ActionBarElement - } export enum AtType { @@ -578,7 +438,7 @@ export interface PttElement { fileSize: string; // "4261" fileSubId: string; // "0" fileUuid: string; // "90j3z7rmRphDPrdVgP9udFBaYar#oK0TWZIV" - formatType: string; // 1 + formatType: number; // 1 invalidState: number; // 0 md5HexStr: string; // "e4d09c784d5a2abcb2f9980bdc7acfe6" playState: number; // 0 @@ -589,6 +449,7 @@ export interface PttElement { voiceChangeType: number; // 0 voiceType: number; // 0 waveAmplitudes: number[]; + autoConvertText: number; } export interface ArkElement { @@ -794,7 +655,8 @@ export interface InlineKeyboardElementRowButton { export interface InlineKeyboardElement { rows: [{ buttons: InlineKeyboardElementRowButton[] - }]; + }], + botAppid: string; } export interface TipAioOpGrayTipElement { // 这是什么提示来着? diff --git a/src/core/packet/msg/converter.ts b/src/core/packet/msg/converter.ts index 8e2091ec..2e4d4562 100644 --- a/src/core/packet/msg/converter.ts +++ b/src/core/packet/msg/converter.ts @@ -1,4 +1,6 @@ import { + ChatType, + ElementType, MessageElement, RawMessage, SendArkElement, @@ -28,30 +30,42 @@ import { PacketMsgVideoElement, PacketMultiMsgElement } from "@/core/packet/msg/element"; -import { PacketMsg, PacketSendMsgElement } from "@/core/packet/msg/message"; -import { LogWrapper } from "@/common/log"; +import {PacketMsg, PacketSendMsgElement} from "@/core/packet/msg/message"; +import {LogWrapper} from "@/common/log"; -type SendMessageElementMap = { - textElement: SendTextElement, - picElement: SendPicElement, - replyElement: SendReplyElement, - faceElement: SendFaceElement, - marketFaceElement: SendMarketFaceElement, - videoElement: SendVideoElement, - fileElement: SendFileElement, - pttElement: SendPttElement, - arkElement: SendArkElement, - markdownElement: SendMarkdownElement, - structLongMsgElement: SendStructLongMsgElement +const SupportedElementTypes = [ + ElementType.TEXT, + ElementType.PIC, + ElementType.REPLY, + ElementType.FACE, + ElementType.MFACE, + ElementType.VIDEO, + ElementType.FILE, + ElementType.PTT, + ElementType.ARK, + ElementType.MARKDOWN, + ElementType.STRUCTLONGMSG +]; + +type SendMessageTypeElementMap = { + [ElementType.TEXT]: SendTextElement, + [ElementType.PIC]: SendPicElement, + [ElementType.FILE]: SendFileElement, + [ElementType.PTT]: SendPttElement, + [ElementType.VIDEO]: SendVideoElement, + [ElementType.FACE]: SendFaceElement, + [ElementType.REPLY]: SendReplyElement, + [ElementType.ARK]: SendArkElement, + [ElementType.MFACE]: SendMarketFaceElement, + [ElementType.STRUCTLONGMSG]: SendStructLongMsgElement, + [ElementType.MARKDOWN]: SendMarkdownElement, }; -type RawToPacketMsgConverters = { - [K in keyof SendMessageElementMap]: ( - element: SendMessageElementMap[K], - msg?: RawMessage, - elementWrapper?: MessageElement, - ) => IPacketMsgElement | null; -}; +type ElementToPacketMsgConverters = { + [K in keyof SendMessageTypeElementMap]: ( + sendElement: MessageElement + ) => IPacketMsgElement; +} export type rawMsgWithSendMsg = { senderUin: number; @@ -69,6 +83,10 @@ export class PacketMsgConverter { this.logger = logger; } + private isValidElementType(type: ElementType): type is keyof ElementToPacketMsgConverters { + return SupportedElementTypes.includes(type); + } + rawMsgWithSendMsgToPacketMsg(msg: rawMsgWithSendMsg): PacketMsg { return { senderUid: msg.senderUid ?? '', @@ -77,55 +95,64 @@ export class PacketMsgConverter { groupId: msg.groupId, time: msg.time, msg: msg.msg.map((element) => { - const key = (Object.keys(this.rawToPacketMsgConverters) as Array).find( - (k) => (element as any)[k] !== undefined // TODO: - ); - if (key) { - const elementData = (element as any)[key]; // TODO: - if (elementData) return this.rawToPacketMsgConverters[key](element as any); - } - return null; + if (!this.isValidElementType(element.elementType)) return null; + return this.rawToPacketMsgConverters[element.elementType](element as MessageElement); }).filter((e) => e !== null) }; } - private rawToPacketMsgConverters: RawToPacketMsgConverters = { - textElement: (element: SendTextElement) => { - if (element.textElement.atType) { - return new PacketMsgAtElement(element); + rawMsgToPacketMsg(msg: RawMessage): PacketMsg { + return { + seq: +msg.msgSeq, + groupId: msg.chatType === ChatType.KCHATTYPEGROUP ? +msg.parentMsgPeer.peerUid : undefined, + senderUid: msg.senderUid, + senderUin: +msg.senderUin, + senderName: msg.sendMemberName ?? msg.sendRemarkName ?? msg.sendNickName ?? 'QQ用户', + time: +msg.msgTime, + msg: msg.elements.map((element) => { + if (!this.isValidElementType(element.elementType)) return null; + return this.rawToPacketMsgConverters[element.elementType](element); + }).filter((e) => e !== null) + } + } + + private rawToPacketMsgConverters: ElementToPacketMsgConverters = { + [ElementType.TEXT]: (element) => { + if (element.textElement?.atType) { + return new PacketMsgAtElement(element as SendTextElement); } - return new PacketMsgTextElement(element); + return new PacketMsgTextElement(element as SendTextElement); }, - picElement: (element: SendPicElement) => { - return new PacketMsgPicElement(element); + [ElementType.PIC]: (element) => { + return new PacketMsgPicElement(element as SendPicElement); }, - replyElement: (element: SendReplyElement) => { - return new PacketMsgReplyElement(element); + [ElementType.REPLY]: (element) => { + return new PacketMsgReplyElement(element as SendReplyElement); }, - faceElement: (element: SendFaceElement) => { - return new PacketMsgFaceElement(element); + [ElementType.FACE]: (element) => { + return new PacketMsgFaceElement(element as SendFaceElement); }, - marketFaceElement: (element: SendMarketFaceElement) => { - return new PacketMsgMarkFaceElement(element); + [ElementType.MFACE]: (element) => { + return new PacketMsgMarkFaceElement(element as SendMarketFaceElement); }, - videoElement: (element: SendVideoElement) => { - return new PacketMsgVideoElement(element); + [ElementType.VIDEO]: (element) => { + return new PacketMsgVideoElement(element as SendVideoElement); }, - fileElement: (element: SendFileElement) => { - return new PacketMsgFileElement(element); + [ElementType.FILE]: (element) => { + return new PacketMsgFileElement(element as SendFileElement); }, - pttElement: (element: SendPttElement) => { - return new PacketMsgPttElement(element); + [ElementType.PTT]: (element) => { + return new PacketMsgPttElement(element as SendPttElement); }, - arkElement: (element: SendArkElement) => { - return new PacketMsgLightAppElement(element); + [ElementType.ARK]: (element) => { + return new PacketMsgLightAppElement(element as SendArkElement); }, - markdownElement: (element: SendMarkdownElement) => { - return new PacketMsgMarkDownElement(element); + [ElementType.MARKDOWN]: (element) => { + return new PacketMsgMarkDownElement(element as SendMarkdownElement); }, // TODO: check this logic, move it in arkElement? - structLongMsgElement: (element: SendStructLongMsgElement) => { - return new PacketMultiMsgElement(element); + [ElementType.STRUCTLONGMSG]: (element) => { + return new PacketMultiMsgElement(element as SendStructLongMsgElement); } }; } diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index b4b008a3..1b4cd02b 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -466,6 +466,7 @@ export class OneBotMsgApi { }, }) => ({ elementType: ElementType.MFACE, + elementId: '', marketFaceElement: { emojiPackageId: emoji_package_id, emojiId: emoji_id, From 0be6effc320e6199add84cb08d7d90d5494867df Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Sun, 27 Oct 2024 05:19:53 +0800 Subject: [PATCH 2/7] feat: support node id in fake forward (broken impl) --- src/core/packet/msg/converter.ts | 4 ++-- src/onebot/action/msg/SendMsg.ts | 19 ++++++++++++++++--- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/core/packet/msg/converter.ts b/src/core/packet/msg/converter.ts index 2e4d4562..6e90a264 100644 --- a/src/core/packet/msg/converter.ts +++ b/src/core/packet/msg/converter.ts @@ -104,10 +104,10 @@ export class PacketMsgConverter { rawMsgToPacketMsg(msg: RawMessage): PacketMsg { return { seq: +msg.msgSeq, - groupId: msg.chatType === ChatType.KCHATTYPEGROUP ? +msg.parentMsgPeer.peerUid : undefined, + groupId: msg.chatType === ChatType.KCHATTYPEGROUP ? +msg.peerUid : undefined, senderUid: msg.senderUid, senderUin: +msg.senderUin, - senderName: msg.sendMemberName ?? msg.sendRemarkName ?? msg.sendNickName ?? 'QQ用户', + senderName: msg.sendNickName ?? 'QQ用户', time: +msg.msgTime, msg: msg.elements.map((element) => { if (!this.isValidElementType(element.elementType)) return null; diff --git a/src/onebot/action/msg/SendMsg.ts b/src/onebot/action/msg/SendMsg.ts index 92633dd6..36ac0e94 100644 --- a/src/onebot/action/msg/SendMsg.ts +++ b/src/onebot/action/msg/SendMsg.ts @@ -168,7 +168,7 @@ export class SendMsg extends BaseAction { logger.logWarn('转发消息深度超过3层,将停止解析!'); break; } - if ((node.data.id && typeof node.data.content !== "string") || !node.data.id) { + if (!node.data.id) { const OB11Data = normalize(node.type === OB11MessageDataType.node ? node.data.content : node); let sendElements: SendMessageElement[]; @@ -190,9 +190,21 @@ export class SendMsg extends BaseAction { time: Number(node.data.time) || Date.now(), msg: sendElements, }; - logger.logDebug(`handleForwardedNodesPacket 开始转换 ${JSON.stringify(packetMsgElements)}`); + logger.logDebug(`handleForwardedNodesPacket[SendRaw] 开始转换 ${JSON.stringify(packetMsgElements)}`); const transformedMsg = this.core.apis.PacketApi.packetSession?.packer.packetConverter.rawMsgWithSendMsgToPacketMsg(packetMsgElements); - logger.logDebug(`handleForwardedNodesPacket 转换为 ${JSON.stringify(transformedMsg)}`); + logger.logDebug(`handleForwardedNodesPacket[SendRaw] 转换为 ${JSON.stringify(transformedMsg)}`); + packetMsg.push(transformedMsg!); + } else if (node.data.id) { + const id = node.data.id; + const nodeMsg = MessageUnique.getMsgIdAndPeerByShortId(+id) || MessageUnique.getPeerByMsgId(id); + if (!nodeMsg) { + logger.logError.bind(this.core.context.logger)('转发消息失败,未找到消息', id); + continue; + } + const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsg.Peer, [nodeMsg.MsgId])).msgList[0]; + logger.logDebug(`handleForwardedNodesPacket[PureRaw] 开始转换 ${JSON.stringify(msg)}`); + const transformedMsg = this.core.apis.PacketApi.packetSession?.packer.packetConverter.rawMsgToPacketMsg(msg); + logger.logDebug(`handleForwardedNodesPacket[PureRaw] 转换为 ${JSON.stringify(transformedMsg)}`); packetMsg.push(transformedMsg!); } else { logger.logDebug(`handleForwardedNodesPacket 跳过元素 ${JSON.stringify(node)}`); @@ -262,6 +274,7 @@ export class SendMsg extends BaseAction { logger.logError.bind(this.core.context.logger)('子消息中包含非node消息 跳过不合法部分'); continue; } + // @ts-ignore const nodeMsg = await this.handleForwardedNodes(selfPeer, OB11Data.filter(e => e.type === OB11MessageDataType.node)); if (nodeMsg) { nodeMsgIds.push(nodeMsg.message!.msgId); From 9f07b07c827605b818dd66e64c3b9af787674328 Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Sun, 27 Oct 2024 06:50:59 +0800 Subject: [PATCH 3/7] feat: support node id in fake forward (with auto download richMedia in reference message) --- src/core/apis/file.ts | 24 ++++++++++++++++++++++++ src/onebot/action/msg/SendMsg.ts | 1 + 2 files changed, 25 insertions(+) diff --git a/src/core/apis/file.ts b/src/core/apis/file.ts index de41cda5..fd6063b4 100644 --- a/src/core/apis/file.ts +++ b/src/core/apis/file.ts @@ -6,6 +6,7 @@ import { Peer, PicElement, PicType, + RawMessage, SendFileElement, SendPicElement, SendPttElement, @@ -267,6 +268,29 @@ export class NTQQFileApi { return fileTransNotifyInfo.filePath; } + async downloadRawMsgMedia(msg: RawMessage[]) { + const res = await Promise.all(msg.map(m => + this.downloadMedia(m.msgId, m.chatType, m.peerUid, m.elements[0].elementId, '', '', 1000 * 60 * 2, true) + )); + msg.forEach((m, index) => { + const element = m.elements[0]; + switch (element.elementType) { + case ElementType.PIC: + element.picElement!.sourcePath = res[index]; + break; + case ElementType.VIDEO: + element.videoElement!.filePath = res[index]; + break; + case ElementType.PTT: + element.pttElement!.filePath = res[index]; + break; + case ElementType.FILE: + element.fileElement!.filePath = res[index]; + break; + } + }); + } + async downloadMedia(msgId: string, chatType: ChatType, peerUid: string, elementId: string, thumbPath: string, sourcePath: string, timeout = 1000 * 60 * 2, force: boolean = false) { // 用于下载收到的消息中的图片等 if (sourcePath && fs.existsSync(sourcePath)) { diff --git a/src/onebot/action/msg/SendMsg.ts b/src/onebot/action/msg/SendMsg.ts index 36ac0e94..18084b19 100644 --- a/src/onebot/action/msg/SendMsg.ts +++ b/src/onebot/action/msg/SendMsg.ts @@ -203,6 +203,7 @@ export class SendMsg extends BaseAction { } const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsg.Peer, [nodeMsg.MsgId])).msgList[0]; logger.logDebug(`handleForwardedNodesPacket[PureRaw] 开始转换 ${JSON.stringify(msg)}`); + await this.core.apis.FileApi.downloadRawMsgMedia([msg]); const transformedMsg = this.core.apis.PacketApi.packetSession?.packer.packetConverter.rawMsgToPacketMsg(msg); logger.logDebug(`handleForwardedNodesPacket[PureRaw] 转换为 ${JSON.stringify(transformedMsg)}`); packetMsg.push(transformedMsg!); From ad2f843c8ffe9daa93c6254f2c99843757e39bae Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Sun, 27 Oct 2024 07:31:32 +0800 Subject: [PATCH 4/7] fix: downloadRawMsgMedia --- src/core/apis/file.ts | 62 ++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 19 deletions(-) diff --git a/src/core/apis/file.ts b/src/core/apis/file.ts index fd6063b4..f1363a07 100644 --- a/src/core/apis/file.ts +++ b/src/core/apis/file.ts @@ -269,25 +269,49 @@ export class NTQQFileApi { } async downloadRawMsgMedia(msg: RawMessage[]) { - const res = await Promise.all(msg.map(m => - this.downloadMedia(m.msgId, m.chatType, m.peerUid, m.elements[0].elementId, '', '', 1000 * 60 * 2, true) - )); - msg.forEach((m, index) => { - const element = m.elements[0]; - switch (element.elementType) { - case ElementType.PIC: - element.picElement!.sourcePath = res[index]; - break; - case ElementType.VIDEO: - element.videoElement!.filePath = res[index]; - break; - case ElementType.PTT: - element.pttElement!.filePath = res[index]; - break; - case ElementType.FILE: - element.fileElement!.filePath = res[index]; - break; - } + const res = await Promise.all( + msg.map(m => + Promise.all( + m.elements + .filter(element => + element.elementType === ElementType.PIC || + element.elementType === ElementType.VIDEO || + element.elementType === ElementType.PTT || + element.elementType === ElementType.FILE + ) + .map(element => + this.downloadMedia(m.msgId, m.chatType, m.peerUid, element.elementId, '', '', 1000 * 60 * 2, true) + ) + ) + ) + ); + msg.forEach((m, msgIndex) => { + const elementResults = res[msgIndex]; + let elementIndex = 0; + m.elements.forEach(element => { + if ( + element.elementType === ElementType.PIC || + element.elementType === ElementType.VIDEO || + element.elementType === ElementType.PTT || + element.elementType === ElementType.FILE + ) { + switch (element.elementType) { + case ElementType.PIC: + element.picElement!.sourcePath = elementResults[elementIndex]; + break; + case ElementType.VIDEO: + element.videoElement!.filePath = elementResults[elementIndex]; + break; + case ElementType.PTT: + element.pttElement!.filePath = elementResults[elementIndex]; + break; + case ElementType.FILE: + element.fileElement!.filePath = elementResults[elementIndex]; + break; + } + elementIndex++; + } + }); }); } From 1eb5cd6237d4e0ee25476ebe882622ca506667a4 Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Sun, 27 Oct 2024 09:04:24 +0800 Subject: [PATCH 5/7] fix: downloadRawMsgMedia edge case --- src/core/apis/file.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/core/apis/file.ts b/src/core/apis/file.ts index f1363a07..cd193c98 100644 --- a/src/core/apis/file.ts +++ b/src/core/apis/file.ts @@ -344,7 +344,7 @@ export class NTQQFileApi { filePath: thumbPath, }], () => true, - (arg) => arg.msgId === msgId, + (arg) => arg.msgElementId === elementId && arg.msgId === msgId, 1, timeout, ); From 15f7cd981438242727c273c2b08e9e8159310e86 Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Sun, 27 Oct 2024 09:33:20 +0800 Subject: [PATCH 6/7] feat: better fake forwardMsg logic & display --- src/common/forward-msg-builder.ts | 2 +- src/core/packet/msg/converter.ts | 7 ++++--- src/onebot/action/msg/SendMsg.ts | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/common/forward-msg-builder.ts b/src/common/forward-msg-builder.ts index 256e0c35..78c98a4b 100644 --- a/src/common/forward-msg-builder.ts +++ b/src/common/forward-msg-builder.ts @@ -56,7 +56,7 @@ export class ForwardMsgBuilder { if (!source) { source = isGroupMsg ? "群聊的聊天记录" : msg.length - ? Array.from(new Set(msg.map(m => m.senderName))) + ? Array.from(new Set(msg.slice(0, 4).map(m => m.senderName))) .join('和') + '的聊天记录' : '聊天记录'; } diff --git a/src/core/packet/msg/converter.ts b/src/core/packet/msg/converter.ts index 6e90a264..34b64fda 100644 --- a/src/core/packet/msg/converter.ts +++ b/src/core/packet/msg/converter.ts @@ -1,4 +1,5 @@ import { + Peer, ChatType, ElementType, MessageElement, @@ -101,13 +102,13 @@ export class PacketMsgConverter { }; } - rawMsgToPacketMsg(msg: RawMessage): PacketMsg { + rawMsgToPacketMsg(msg: RawMessage, ctxPeer: Peer): PacketMsg { return { seq: +msg.msgSeq, - groupId: msg.chatType === ChatType.KCHATTYPEGROUP ? +msg.peerUid : undefined, + groupId: ctxPeer.chatType === ChatType.KCHATTYPEGROUP ? +msg.peerUid : undefined, senderUid: msg.senderUid, senderUin: +msg.senderUin, - senderName: msg.sendNickName ?? 'QQ用户', + senderName: msg.sendMemberName ?? '' !== '' ? msg.sendMemberName ?? '' : msg.sendNickName ?? '' !== '' ? msg.sendNickName ?? "QQ用户" : "QQ用户", time: +msg.msgTime, msg: msg.elements.map((element) => { if (!this.isValidElementType(element.elementType)) return null; diff --git a/src/onebot/action/msg/SendMsg.ts b/src/onebot/action/msg/SendMsg.ts index 18084b19..d2f79292 100644 --- a/src/onebot/action/msg/SendMsg.ts +++ b/src/onebot/action/msg/SendMsg.ts @@ -204,7 +204,7 @@ export class SendMsg extends BaseAction { const msg = (await this.core.apis.MsgApi.getMsgsByMsgId(nodeMsg.Peer, [nodeMsg.MsgId])).msgList[0]; logger.logDebug(`handleForwardedNodesPacket[PureRaw] 开始转换 ${JSON.stringify(msg)}`); await this.core.apis.FileApi.downloadRawMsgMedia([msg]); - const transformedMsg = this.core.apis.PacketApi.packetSession?.packer.packetConverter.rawMsgToPacketMsg(msg); + const transformedMsg = this.core.apis.PacketApi.packetSession?.packer.packetConverter.rawMsgToPacketMsg(msg, msgPeer); logger.logDebug(`handleForwardedNodesPacket[PureRaw] 转换为 ${JSON.stringify(transformedMsg)}`); packetMsg.push(transformedMsg!); } else { From 875e91fc0e6e12152ac3160dc50628890991f904 Mon Sep 17 00:00:00 2001 From: pk5ls20 Date: Sun, 27 Oct 2024 09:37:17 +0800 Subject: [PATCH 7/7] chore: simplify logic --- src/core/packet/msg/converter.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/core/packet/msg/converter.ts b/src/core/packet/msg/converter.ts index 34b64fda..f1b13278 100644 --- a/src/core/packet/msg/converter.ts +++ b/src/core/packet/msg/converter.ts @@ -108,7 +108,11 @@ export class PacketMsgConverter { groupId: ctxPeer.chatType === ChatType.KCHATTYPEGROUP ? +msg.peerUid : undefined, senderUid: msg.senderUid, senderUin: +msg.senderUin, - senderName: msg.sendMemberName ?? '' !== '' ? msg.sendMemberName ?? '' : msg.sendNickName ?? '' !== '' ? msg.sendNickName ?? "QQ用户" : "QQ用户", + senderName: msg.sendMemberName && msg.sendMemberName !== '' + ? msg.sendMemberName + : msg.sendNickName && msg.sendNickName !== '' + ? msg.sendNickName + : "QQ用户", time: +msg.msgTime, msg: msg.elements.map((element) => { if (!this.isValidElementType(element.elementType)) return null;