mirror of
https://github.com/NapNeko/NapCatQQ.git
synced 2026-02-05 15:11:15 +00:00
Moved action example files into a new 'example' directory and updated all imports accordingly. Removed the monolithic 'examples.ts' and redefined ActionExamples in OneBotAction.ts to only include common error codes. This improves code organization and maintainability.
171 lines
6.0 KiB
TypeScript
171 lines
6.0 KiB
TypeScript
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<typeof PayloadSchema>;
|
||
|
||
const ReturnSchema = Type.Object({
|
||
messages: Type.Optional(Type.Array(Type.Unknown(), { description: '消息列表' })),
|
||
}, { description: '合并转发消息' });
|
||
|
||
type ReturnType = Static<typeof ReturnSchema>;
|
||
|
||
function isForward (msg: OB11MessageData | string): msg is OB11MessageForward {
|
||
return typeof msg !== 'string' && msg.type === OB11MessageDataType.forward;
|
||
}
|
||
|
||
export class GoCQHTTPGetForwardMsgAction extends OneBotAction<PayloadType, ReturnType> {
|
||
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<OB11MessageNode[]> {
|
||
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('消息已过期或者为内层消息,无法获取转发消息');
|
||
}
|
||
}
|