import { OneBotAction } from '@/napcat-onebot/action/OneBotAction'; import { OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageForward, OB11MessageNodePlain as OB11MessageNode } from '@/napcat-onebot/index'; import { ActionName } from '@/napcat-onebot/action/router'; import { MessageUnique } from 'napcat-common/src/message-unique'; import { Static, Type } from '@sinclair/typebox'; import { ChatType, ElementType, MsgSourceType, NTMsgType, RawMessage } from 'napcat-core'; import { isNumeric } from 'napcat-common/src/helper'; import { GoCQHTTPActionsExamples } from '../example/GoCQHTTPActionsExamples'; const PayloadSchema = Type.Object({ message_id: Type.Optional(Type.String({ description: '消息ID' })), id: Type.Optional(Type.String({ description: '消息ID' })), }); type PayloadType = Static; const ReturnSchema = Type.Object({ messages: Type.Optional(Type.Array(Type.Unknown(), { description: '消息列表' })), }, { description: '合并转发消息' }); type ReturnType = Static; function isForward (msg: OB11MessageData | string): msg is OB11MessageForward { return typeof msg !== 'string' && msg.type === OB11MessageDataType.forward; } export class GoCQHTTPGetForwardMsgAction extends OneBotAction { override actionName = ActionName.GoCQHTTP_GetForwardMsg; override payloadSchema = PayloadSchema; override returnSchema = ReturnSchema; override actionSummary = '获取合并转发消息'; override actionDescription = '获取合并转发消息的具体内容'; override actionTags = ['Go-CQHTTP']; override payloadExample = GoCQHTTPActionsExamples.GetForwardMsg.payload; override returnExample = GoCQHTTPActionsExamples.GetForwardMsg.response; private createTemplateNode (message: OB11Message): OB11MessageNode { return { type: OB11MessageDataType.node, data: { user_id: message.user_id, nickname: message.sender.nickname, message: [], content: [], }, }; } async parseForward (messages: OB11Message[]): Promise { const retMsg: OB11MessageNode[] = []; for (const message of messages) { const templateNode = this.createTemplateNode(message); for (const msgdata of message.message) { if (isForward(msgdata)) { const newNode = this.createTemplateNode(message); newNode.data.message = await this.parseForward(msgdata.data.content ?? []); templateNode.data.message.push(newNode); } else { if (typeof msgdata === 'string') { const textMsg: OB11MessageData = { type: OB11MessageDataType.text, data: { text: msgdata } }; templateNode.data.message.push(textMsg); } else { templateNode.data.message.push(msgdata); } } } retMsg.push(templateNode); } return retMsg; } async _handle (payload: PayloadType) { // 1. 检查消息ID是否存在 const msgId = payload.message_id || payload.id; if (!msgId) { throw new Error('message_id is required'); } // 2. 定义辅助函数 - 创建伪转发消息对象 const createFakeForwardMsg = (resId: string): RawMessage => { const fakeMsg: RawMessage = { chatType: ChatType.KCHATTYPEGROUP, elements: [{ elementType: ElementType.MULTIFORWARD, elementId: '', multiForwardMsgElement: { resId, fileName: '', xmlContent: '', }, }], guildId: '', isOnlineMsg: false, msgId: '', // TODO: no necessary msgRandom: '0', msgSeq: '', msgTime: '', msgType: NTMsgType.KMSGTYPEMIX, parentMsgIdList: [], parentMsgPeer: { chatType: ChatType.KCHATTYPEGROUP, peerUid: '', }, peerName: '', peerUid: '284840486', peerUin: '284840486', recallTime: '0', records: [], sendNickName: '', sendRemarkName: '', senderUid: '', senderUin: '1094950020', sourceType: MsgSourceType.K_DOWN_SOURCETYPE_UNKNOWN, subMsgType: 1, }; return fakeMsg; }; // 3. 定义协议回退逻辑函数 const protocolFallbackLogic = async (resId: string) => { const ob = (await this.obContext.apis.MsgApi.parseMessageV2(createFakeForwardMsg(resId), true))?.arrayMsg; const firstMsg = ob?.message?.[0]; if (firstMsg && isForward(firstMsg)) { return { messages: firstMsg.data.content, }; } throw new Error('protocolFallbackLogic: 找不到相关的聊天记录'); }; // 4. 尝试通过正常渠道获取消息 // 如果是数字ID,优先使用getMsgsByMsgId获取 if (!isNumeric(msgId)) { const ret = await protocolFallbackLogic(msgId); if (ret.messages) { return ret; } throw new Error('ResId无效: 找不到相关的聊天记录'); } const rootMsgId = MessageUnique.getShortIdByMsgId(msgId.toString()); const rootMsg = MessageUnique.getMsgIdAndPeerByShortId(rootMsgId ?? +msgId); if (rootMsg) { // 5. 获取消息内容 const data = await this.core.apis.MsgApi.getMsgHistory(rootMsg.Peer, rootMsg.MsgId, 1);//getMsgsIncludeSelf if (data && data.result === 0 && data.msgList.length > 0) { const singleMsg = data.msgList[0]; if (!singleMsg) { throw new Error('消息不存在或已过期'); } // 6. 解析消息内容 const resMsg = (await this.obContext.apis.MsgApi.parseMessage(singleMsg, 'array', true)); const firstMsg = resMsg?.message?.[0]; if (firstMsg && isForward(firstMsg)) { const forwardContent = firstMsg.data.content; if (forwardContent) { return { messages: forwardContent }; } } } } // 说明消息已过期或者为内层消息 NapCat 一次返回不处理内层消息 throw new Error('消息已过期或者为内层消息,无法获取转发消息'); } }