Refactor type handling and improve message parsing

Updated several actions to use more precise type casting and type guards, improving type safety and clarity. Enhanced message parsing logic for forward messages and group/friend message history. Standardized return schemas and error handling for avatar and group portrait actions.
This commit is contained in:
手瓜一十雪 2026-01-25 16:32:36 +08:00
parent e562a57713
commit 20398af648
10 changed files with 67 additions and 38 deletions

View File

@ -1,6 +1,5 @@
import { ActionName } from '@/napcat-onebot/action/router';
import { GetPacketStatusDepends } from '@/napcat-onebot/action/packet/GetPacketStatus';
import { AIVoiceChatType } from 'napcat-core/packet/entities/aiChat';
import { Type, Static } from '@sinclair/typebox';
const PayloadSchema = Type.Object({
@ -33,7 +32,8 @@ export class GetAiCharacters extends GetPacketStatusDepends<PayloadType, ReturnT
override returnSchema = ReturnSchema;
async _handle (payload: PayloadType) {
const rawList = await this.core.apis.PacketApi.pkt.operation.FetchAiVoiceList(+payload.group_id, +payload.chat_type as AIVoiceChatType);
const chatTypeNum = Number(payload.chat_type);
const rawList = await this.core.apis.PacketApi.pkt.operation.FetchAiVoiceList(+payload.group_id, chatTypeNum);
return rawList?.map((item) => ({
type: item.category,
characters: item.voices.map((voice) => ({

View File

@ -39,16 +39,18 @@ export class GetUnidirectionalFriendList extends OneBotAction<void, ReturnType>
};
const packed_data = await this.pack_data(JSON.stringify(req_json));
const data = Buffer.from(packed_data);
const rsq = { cmd: 'MQUpdateSvc_com_qq_ti.web.OidbSvc.0xe17_0', data: data as PacketBuf };
const rsq = { cmd: 'MQUpdateSvc_com_qq_ti.web.OidbSvc.0xe17_0', data: data as unknown as PacketBuf };
const rsp_data = await this.core.apis.PacketApi.pkt.operation.sendPacket(rsq, true);
const block_json = ProtoBuf(class extends ProtoBufBase { data = PBString(4); }).decode(rsp_data);
const block_list = JSON.parse(block_json.data).rpt_block_list as {
interface BlockItem {
uint64_uin: number;
str_uid: string;
bytes_nick: string;
uint32_age: number;
bytes_source: string;
}[];
}
const block_data: { rpt_block_list: BlockItem[]; } = JSON.parse(block_json.data);
const block_list = block_data.rpt_block_list;
return block_list.map((block) => ({
uin: block.uint64_uin,

View File

@ -21,7 +21,8 @@ export class SendPacket extends GetPacketStatusDepends<PayloadType, ReturnType>
override actionName = ActionName.SendPacket;
async _handle (payload: PayloadType) {
const rsp = typeof payload.rsp === 'boolean' ? payload.rsp : payload.rsp === 'true';
const data = await this.core.apis.PacketApi.pkt.operation.sendPacket({ cmd: payload.cmd, data: Buffer.from(payload.data, 'hex') as PacketBuf }, rsp);
const packetData = Buffer.from(payload.data, 'hex') as unknown as PacketBuf;
const data = await this.core.apis.PacketApi.pkt.operation.sendPacket({ cmd: payload.cmd, data: packetData }, rsp);
return typeof data === 'object' ? data.toString('hex') : undefined;
}
}

View File

@ -31,7 +31,7 @@ export default class SetAvatar extends OneBotAction<PayloadType, ReturnType> {
throw new Error(`头像${payload.file}设置失败,api无返回`);
}
// log(`头像设置返回:${JSON.stringify(ret)}`)
if (ret.result as number === 1004022) {
if (Number(ret.result) === 1004022) {
throw new Error(`头像${payload.file}设置失败,文件可能不是图片格式`);
} else if (ret.result !== 0) {
throw new Error(`头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`);

View File

@ -14,11 +14,15 @@ const PayloadSchema = Type.Object({
type PayloadType = Static<typeof PayloadSchema>;
const ReturnSchema = Type.Object({
messages: Type.Optional(Type.Array(Type.Any(), { description: '消息列表' })),
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;
@ -43,13 +47,18 @@ export class GoCQHTTPGetForwardMsgAction extends OneBotAction<PayloadType, Retur
const templateNode = this.createTemplateNode(message);
for (const msgdata of message.message) {
if ((msgdata as OB11MessageData).type === OB11MessageDataType.forward) {
if (isForward(msgdata)) {
const newNode = this.createTemplateNode(message);
newNode.data.message = await this.parseForward((msgdata as OB11MessageForward).data.content ?? []);
newNode.data.message = await this.parseForward(msgdata.data.content ?? []);
templateNode.data.message.push(newNode);
} else {
templateNode.data.message.push(msgdata as OB11MessageData);
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);
@ -67,7 +76,7 @@ export class GoCQHTTPGetForwardMsgAction extends OneBotAction<PayloadType, Retur
// 2. 定义辅助函数 - 创建伪转发消息对象
const createFakeForwardMsg = (resId: string): RawMessage => {
return {
const fakeMsg: RawMessage = {
chatType: ChatType.KCHATTYPEGROUP,
elements: [{
elementType: ElementType.MULTIFORWARD,
@ -80,7 +89,7 @@ export class GoCQHTTPGetForwardMsgAction extends OneBotAction<PayloadType, Retur
}],
guildId: '',
isOnlineMsg: false,
msgId: '', // TODO: no necessary
msgId: '', // TODO: no necessary
msgRandom: '0',
msgSeq: '',
msgTime: '',
@ -101,15 +110,17 @@ export class GoCQHTTPGetForwardMsgAction extends OneBotAction<PayloadType, Retur
senderUin: '1094950020',
sourceType: MsgSourceType.K_DOWN_SOURCETYPE_UNKNOWN,
subMsgType: 1,
} as RawMessage;
};
return fakeMsg;
};
// 3. 定义协议回退逻辑函数
const protocolFallbackLogic = async (resId: string) => {
const ob = (await this.obContext.apis.MsgApi.parseMessageV2(createFakeForwardMsg(resId), true))?.arrayMsg;
if (ob) {
const firstMsg = ob?.message?.[0];
if (firstMsg && isForward(firstMsg)) {
return {
messages: (ob?.message?.[0] as OB11MessageForward)?.data?.content,
messages: firstMsg.data.content,
};
}
throw new Error('protocolFallbackLogic: 找不到相关的聊天记录');
@ -138,9 +149,12 @@ export class GoCQHTTPGetForwardMsgAction extends OneBotAction<PayloadType, Retur
// 6. 解析消息内容
const resMsg = (await this.obContext.apis.MsgApi.parseMessage(singleMsg, 'array', true));
const forwardContent = (resMsg?.message?.[0] as OB11MessageForward)?.data?.content;
if (forwardContent) {
return { messages: forwardContent };
const firstMsg = resMsg?.message?.[0];
if (firstMsg && isForward(firstMsg)) {
const forwardContent = firstMsg.data.content;
if (forwardContent) {
return { messages: forwardContent };
}
}
}
}

View File

@ -50,7 +50,7 @@ export default class GetFriendMsgHistory extends OneBotAction<PayloadType, Retur
// 烘焙消息
const ob11MsgList = (await Promise.all(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat, payload.parse_mult_msg, payload.disable_get_url)))
).filter(msg => msg !== undefined);
return { messages: ob11MsgList as OB11Message[] };
).filter((msg): msg is OB11Message => msg !== undefined);
return { messages: ob11MsgList };
}
}

View File

@ -13,8 +13,8 @@ const PayloadSchema = Type.Object({
type PayloadType = Static<typeof PayloadSchema>;
const ReturnSchema = Type.Object({
files: Type.Array(Type.Any(), { description: '文件列表' }),
folders: Type.Array(Type.Any(), { description: '文件夹列表' }),
files: Type.Array(Type.Unknown(), { description: '文件列表' }),
folders: Type.Array(Type.Unknown(), { description: '文件夹列表' }),
}, { description: '群文件夹文件列表' });
type ReturnType = Static<typeof ReturnSchema>;
@ -23,19 +23,20 @@ export class GetGroupFilesByFolder extends OneBotAction<PayloadType, ReturnType>
override actionName = ActionName.GoCQHTTP_GetGroupFilesByFolder;
override payloadSchema = PayloadSchema;
override returnSchema = ReturnSchema;
async _handle (payload: PayloadType) {
const ret = await this.core.apis.MsgApi.getGroupFileList(payload.group_id.toString(), {
async _handle (payload: PayloadType): Promise<ReturnType> {
const retRaw = await this.core.apis.MsgApi.getGroupFileList(payload.group_id.toString(), {
sortType: 1,
fileCount: +payload.file_count,
startIndex: 0,
sortOrder: 2,
showOnlinedocFolder: 0,
folderId: payload.folder ?? payload.folder_id ?? '',
}).catch(() => []);
});
const ret = Array.isArray(retRaw) ? retRaw : [];
return {
files: ret.filter(item => item.fileInfo)
.map(item => OB11Construct.file(item.peerId, item.fileInfo!)),
folders: [] as [],
folders: [],
};
}
}

View File

@ -46,7 +46,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends OneBotAction<PayloadType
// 烘焙消息
const ob11MsgList = (await Promise.all(
msgList.map(msg => this.obContext.apis.MsgApi.parseMessage(msg, config.messagePostFormat, payload.parse_mult_msg, payload.disable_get_url, payload.quick_reply)))
).filter(msg => msg !== undefined);
return { messages: ob11MsgList as OB11Message[] };
).filter((msg): msg is OB11Message => msg !== undefined);
return { messages: ob11MsgList };
}
}

View File

@ -1,8 +1,9 @@
import { OB11MessageMixType } from '@/napcat-onebot/types';
import { ContextMode, normalize, ReturnDataType, SendMsgBase, SendMsgPayload } from '@/napcat-onebot/action/msg/SendMsg';
import { ActionName } from '@/napcat-onebot/action/router';
// 未验证
type GoCQHTTPSendForwardMsgPayload = SendMsgPayload & { messages?: any; };
type GoCQHTTPSendForwardMsgPayload = SendMsgPayload & { messages?: OB11MessageMixType; };
export class GoCQHTTPSendForwardMsgBase extends SendMsgBase {
protected override async check (payload: GoCQHTTPSendForwardMsgPayload) {
@ -20,14 +21,14 @@ export class GoCQHTTPSendForwardMsg extends GoCQHTTPSendForwardMsgBase {
}
export class GoCQHTTPSendPrivateForwardMsg extends GoCQHTTPSendForwardMsgBase {
override actionName = ActionName.GoCQHTTP_SendPrivateForwardMsg;
override async _handle (payload: SendMsgPayload): Promise<ReturnDataType> {
override async _handle (payload: GoCQHTTPSendForwardMsgPayload): Promise<ReturnDataType> {
return this.base_handle(payload, ContextMode.Private);
}
}
export class GoCQHTTPSendGroupForwardMsg extends GoCQHTTPSendForwardMsgBase {
override actionName = ActionName.GoCQHTTP_SendGroupForwardMsg;
override async _handle (payload: SendMsgPayload): Promise<ReturnDataType> {
override async _handle (payload: GoCQHTTPSendForwardMsgPayload): Promise<ReturnDataType> {
return this.base_handle(payload, ContextMode.Group);
}
}

View File

@ -10,12 +10,19 @@ export const SetGroupPortraitPayloadSchema = Type.Object({
export type SetGroupPortraitPayload = Static<typeof SetGroupPortraitPayloadSchema>;
export default class SetGroupPortrait extends OneBotAction<SetGroupPortraitPayload, any> {
const ReturnSchema = Type.Object({
result: Type.Number(),
errMsg: Type.String(),
}, { description: '设置结果' });
export type ReturnType = Static<typeof ReturnSchema>;
export default class SetGroupPortrait extends OneBotAction<SetGroupPortraitPayload, ReturnType> {
override actionName = ActionName.SetGroupPortrait;
override payloadSchema = SetGroupPortraitPayloadSchema;
override returnSchema = Type.Any({ description: '设置结果' });
override returnSchema = ReturnSchema;
async _handle (payload: SetGroupPortraitPayload): Promise<any> {
async _handle (payload: SetGroupPortraitPayload): Promise<ReturnType> {
const { path, success } = (await uriToLocalFile(this.core.NapCatTempPath, payload.file));
if (!success) {
throw new Error(`头像${payload.file}设置失败,file字段可能格式不正确`);
@ -27,12 +34,15 @@ export default class SetGroupPortrait extends OneBotAction<SetGroupPortraitPaylo
if (!ret) {
throw new Error(`头像${payload.file}设置失败,api无返回`);
}
if (ret.result as number === 1004022) {
if (Number(ret.result) === 1004022) {
throw new Error(`头像${payload.file}设置失败,文件可能不是图片格式或权限不足`);
} else if (ret.result !== 0) {
} else if (Number(ret.result) !== 0) {
throw new Error(`头像${payload.file}设置失败,未知的错误,${ret.result}:${ret.errMsg}`);
}
return ret;
return {
result: Number(ret.result),
errMsg: ret.errMsg,
};
} else {
fs.unlink(path).catch(() => { });
throw new Error(`头像${payload.file}设置失败,无法获取头像,文件可能不存在`);