diff --git a/src/core/listeners/NodeIKernelMsgListener.ts b/src/core/listeners/NodeIKernelMsgListener.ts index bdfcd0a5..379f0289 100644 --- a/src/core/listeners/NodeIKernelMsgListener.ts +++ b/src/core/listeners/NodeIKernelMsgListener.ts @@ -255,7 +255,7 @@ export class NodeIKernelMsgListener { } - onMsgRecall(i2: unknown, str: unknown, j2: unknown): any { + onMsgRecall(chatType: ChatType, uid: string, msgSeq: string): any { } diff --git a/src/core/types/element.ts b/src/core/types/element.ts index 6211650b..579603ba 100644 --- a/src/core/types/element.ts +++ b/src/core/types/element.ts @@ -40,17 +40,18 @@ export interface FaceElement { surpriseId?: string; randomType?: number; } +export interface GrayTipRovokeElement { + operatorRole: string; + operatorUid: string; + operatorNick: string; + operatorRemark: string; + operatorMemRemark?: string; + wording: string; // 自定义的撤回提示语 +} export interface GrayTipElement { subElementType: NTGrayTipElementSubTypeV2; - revokeElement: { - operatorRole: string; - operatorUid: string; - operatorNick: string; - operatorRemark: string; - operatorMemRemark?: string; - wording: string; // 自定义的撤回提示语 - }; + revokeElement: GrayTipRovokeElement; aioOpGrayTipElement: TipAioOpGrayTipElement; groupElement: TipGroupElement; xmlElement: { diff --git a/src/onebot/api/group.ts b/src/onebot/api/group.ts index bd3300d4..5a189a78 100644 --- a/src/onebot/api/group.ts +++ b/src/onebot/api/group.ts @@ -1,10 +1,15 @@ import { ChatType, + FileElement, GrayTipElement, + InstanceContext, JsonGrayBusiId, + MessageElement, NapCatCore, NTGrayTipElementSubTypeV2, + NTMsgType, RawMessage, + TipGroupElement, TipGroupElementType, } from '@/core'; import { NapCatOneBot11Adapter } from '@/onebot'; @@ -15,13 +20,13 @@ import fastXmlParser from 'fast-xml-parser'; import { OB11GroupMsgEmojiLikeEvent } from '@/onebot/event/notice/OB11MsgEmojiLikeEvent'; import { MessageUnique } from '@/common/message-unique'; import { OB11GroupCardEvent } from '@/onebot/event/notice/OB11GroupCardEvent'; -import { OB11GroupUploadNoticeEvent } from '@/onebot/event/notice/OB11GroupUploadNoticeEvent'; import { OB11GroupPokeEvent } from '@/onebot/event/notice/OB11PokeEvent'; import { OB11GroupEssenceEvent } from '@/onebot/event/notice/OB11GroupEssenceEvent'; import { OB11GroupTitleEvent } from '@/onebot/event/notice/OB11GroupTitleEvent'; -import { FileNapCatOneBotUUID } from '@/common/helper'; +import { OB11EmitEventContent } from '../network'; +import { OB11GroupUploadNoticeEvent } from '../event/notice/OB11GroupUploadNoticeEvent'; import { pathToFileURL } from 'node:url'; - +import { FileNapCatOneBotUUID } from '@/common/helper'; export class OneBotGroupApi { obContext: NapCatOneBot11Adapter; @@ -32,137 +37,6 @@ export class OneBotGroupApi { this.core = core; } - async parseGroupEvent(msg: RawMessage) { - const logger = this.core.context.logger; - if (msg.chatType !== ChatType.KCHATTYPEGROUP) { - return; - } - //log("group msg", msg); - 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 newCardName = msg.sendMemberName ?? ''; - const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName); - member.cardName = newCardName; - return event; - } - } - - for (const element of msg.elements) { - if (element.grayTipElement?.groupElement) { - const groupElement = element.grayTipElement.groupElement; - if (groupElement.type == TipGroupElementType.KMEMBERADD) { - const MemberIncreaseEvent = await this.obContext.apis.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement); - if (MemberIncreaseEvent) return MemberIncreaseEvent; - } else if (groupElement.type === TipGroupElementType.KSHUTUP) { - const BanEvent = await this.obContext.apis.GroupApi.parseGroupBanEvent(msg.peerUid, element.grayTipElement); - 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, element.grayTipElement); - if (KickEvent) return KickEvent; - } catch (e) { - return new OB11GroupDecreaseEvent( - this.core, - parseInt(msg.peerUid), - parseInt(this.core.selfInfo.uin), - 0, - 'leave', - ); - } - } - } else if (element.fileElement) { - return new OB11GroupUploadNoticeEvent( - this.core, - parseInt(msg.peerUid), parseInt(msg.senderUin || ''), - { - id: FileNapCatOneBotUUID.encode({ - chatType: ChatType.KCHATTYPEGROUP, - peerUid: msg.peerUid, - }, msg.msgId, element.elementId, element.fileElement.fileUuid, "." + element.fileElement.fileName), - url: pathToFileURL(element.fileElement.filePath).href, - name: element.fileElement.fileName, - size: parseInt(element.fileElement.fileSize), - busid: element.fileElement.fileBizId ?? 0, - }, - ); - } - if (element.grayTipElement) { - if (element.grayTipElement.xmlElement?.templId === '10382') { - const emojiLikeEvent = await this.obContext.apis.GroupApi.parseGroupEmojiLikeEventByGrayTip(msg.peerUid, element.grayTipElement); - if (emojiLikeEvent) return emojiLikeEvent; - } - if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) { - const GroupIncreaseEvent = await this.obContext.apis.GroupApi.parseGroupIncreaseEvent(msg.peerUid, element.grayTipElement); - if (GroupIncreaseEvent) return GroupIncreaseEvent; - } - - else if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) { - const json = JSON.parse(element.grayTipElement.jsonGrayTipElement.jsonStr); - if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) { - //判断业务类型 - //Poke事件 - const pokedetail: any[] = json.items; - //筛选item带有uid的元素 - const poke_uid = pokedetail.filter(item => item.uid); - if (poke_uid.length == 2) { - return new OB11GroupPokeEvent( - this.core, - parseInt(msg.peerUid), - +await this.core.apis.UserApi.getUinByUidV2(poke_uid[0].uid), - +await this.core.apis.UserApi.getUinByUidV2(poke_uid[1].uid), - pokedetail, - ); - } - } - if (element.grayTipElement.jsonGrayTipElement.busiId == JsonGrayBusiId.AIO_GROUP_ESSENCE_MSG_TIP) { - const searchParams = new URL(json.items[0].jp).searchParams; - const msgSeq = searchParams.get('msgSeq')!; - const Group = searchParams.get('groupCode'); - if (!Group) return; - // const businessId = searchParams.get('businessid'); - const Peer = { - guildId: '', - chatType: ChatType.KCHATTYPEGROUP, - peerUid: Group, - }; - const msgData = await this.core.apis.MsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true); - const msgList = (await this.core.apis.WebApi.getGroupEssenceMsgAll(Group)).flatMap((e) => e.data.msg_list); - const realMsg = msgList.find((e) => e.msg_seq.toString() == msgSeq); - return new OB11GroupEssenceEvent( - this.core, - parseInt(msg.peerUid), - MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!, - parseInt(msgData.msgList[0].senderUin), - parseInt(realMsg?.add_digest_uin ?? '0'), - ); - // 获取MsgSeq+Peer可获取具体消息 - } - if (element.grayTipElement.jsonGrayTipElement.busiId == JsonGrayBusiId.GROUP_AIO_CONFIGURABLE_GRAY_TIPS) { - const type = json.items[json.items.length - 1]?.txt; - if (type === "头衔") { - const memberUin = json.items[1].param[0]; - const title = json.items[3].txt; - logger.logDebug('收到群成员新头衔消息', json); - return new OB11GroupTitleEvent( - this.core, - parseInt(msg.peerUid), - parseInt(memberUin), - title, - ); - } else if (type === "移出") { - logger.logDebug('收到机器人被踢消息', json); - return; - } else { - logger.logWarn('收到未知的灰条消息', json); - } - } - } - } - } - } - async parseGroupBanEvent(GroupCode: string, grayTipElement: GrayTipElement) { const groupElement = grayTipElement?.groupElement; if (!groupElement?.shutUp) return undefined; @@ -300,4 +174,151 @@ 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 newCardName = msg.sendMemberName ?? ''; + const event = new OB11GroupCardEvent(this.core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName); + member.cardName = newCardName; + return event; + } + } + 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); + + //判断业务类型 + //Poke事件 + const pokedetail: any[] = json.items; + //筛选item带有uid的元素 + const poke_uid = pokedetail.filter(item => item.uid); + if (poke_uid.length == 2) { + return new OB11GroupPokeEvent( + this.core, + parseInt(msg.peerUid), + +await this.core.apis.UserApi.getUinByUidV2(poke_uid[0].uid), + +await this.core.apis.UserApi.getUinByUidV2(poke_uid[1].uid), + pokedetail, + ); + } + return undefined; + } + + async parseOtherJsonEvent(msg: RawMessage, jsonStr: string, context: InstanceContext) { + const json = JSON.parse(jsonStr); + const type = json.items[json.items.length - 1]?.txt; + if (type === "头衔") { + const memberUin = json.items[1].param[0]; + const title = json.items[3].txt; + context.logger.logDebug('收到群成员新头衔消息', json); + return new OB11GroupTitleEvent( + this.core, + parseInt(msg.peerUid), + parseInt(memberUin), + title, + ); + } else if (type === "移出") { + context.logger.logDebug('收到机器人被踢消息', json); + return; + } else { + context.logger.logWarn('收到未知的灰条消息', json); + } + } + + async parseEssenceMsg(msg: RawMessage, jsonStr: string) { + const json = JSON.parse(jsonStr); + const searchParams = new URL(json.items[0].jp).searchParams; + const msgSeq = searchParams.get('msgSeq')!; + const Group = searchParams.get('groupCode'); + if (!Group) return; + // const businessId = searchParams.get('businessid'); + const Peer = { + guildId: '', + chatType: ChatType.KCHATTYPEGROUP, + peerUid: Group, + }; + const msgData = await this.core.apis.MsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true); + const msgList = (await this.core.apis.WebApi.getGroupEssenceMsgAll(Group)).flatMap((e) => e.data.msg_list); + const realMsg = msgList.find((e) => e.msg_seq.toString() == msgSeq); + return new OB11GroupEssenceEvent( + this.core, + parseInt(msg.peerUid), + MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!, + parseInt(msgData.msgList[0].senderUin), + parseInt(realMsg?.add_digest_uin ?? '0'), + ); + // 获取MsgSeq+Peer可获取具体消息 + } + + async parseGroupUploadFileEvene(msg: RawMessage, element: FileElement, elementWrapper: MessageElement) { + return new OB11GroupUploadNoticeEvent( + this.core, + parseInt(msg.peerUid), parseInt(msg.senderUin || ''), + { + id: FileNapCatOneBotUUID.encode({ + chatType: ChatType.KCHATTYPEGROUP, + peerUid: msg.peerUid, + }, msg.msgId, elementWrapper.elementId, elementWrapper?.fileElement?.fileUuid, "." + element.fileName), + url: pathToFileURL(element.filePath).href, + name: element.fileName, + size: parseInt(element.fileSize), + busid: element.fileBizId ?? 0, + }, + ); + } + + async parseGrayTipElement(msg: RawMessage, grayTipElement: GrayTipElement) { + if (grayTipElement.subElementType === NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_GROUP) { + // 解析群组事件 + return await this.parseGroupElement(msg, grayTipElement.groupElement, grayTipElement); + + } else if (grayTipElement.subElementType === NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) { + // 筛选出表情回应 事件 + if (grayTipElement.xmlElement?.templId === '10382') { + return await this.obContext.apis.GroupApi.parseGroupEmojiLikeEventByGrayTip(msg.peerUid, grayTipElement); + + } else { + return await this.obContext.apis.GroupApi.parseGroupIncreaseEvent(msg.peerUid, grayTipElement); + } + } else if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) { + // 解析json事件 + if (grayTipElement.jsonGrayTipElement.busiId == 1061) { + return await this.parsePaiYiPai(msg, grayTipElement.jsonGrayTipElement.jsonStr); + } else if (grayTipElement.jsonGrayTipElement.busiId == JsonGrayBusiId.AIO_GROUP_ESSENCE_MSG_TIP) { + return await this.parseEssenceMsg(msg, grayTipElement.jsonGrayTipElement.jsonStr); + } else { + return await this.parseOtherJsonEvent(msg, grayTipElement.jsonGrayTipElement.jsonStr, this.core.context) + } + } + return undefined; + } } diff --git a/src/onebot/api/msg.ts b/src/onebot/api/msg.ts index a1a771e0..c987d400 100644 --- a/src/onebot/api/msg.ts +++ b/src/onebot/api/msg.ts @@ -17,6 +17,7 @@ import { SendTextElement, BaseEmojiType, FaceType, + GrayTipElement, } from '@/core'; import faceConfig from '@/core/external/face_config.json'; import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataType, OB11MessageFileBase, OB11MessageForward, } from '@/onebot'; @@ -664,20 +665,13 @@ export class OneBotMsgApi { this.core = core; } - async parsePrivateMsgEvent(msg: RawMessage) { - if (msg.chatType !== ChatType.KCHATTYPEC2C) { - return; - } - for (const element of msg.elements) { - if (element.grayTipElement && element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) { - if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) { - const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(element.grayTipElement); - if (PokeEvent) return PokeEvent; - } - //好友添加成功事件 - if (element.grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') { - return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid))); - } + async parsePrivateMsgEvent(msg: RawMessage, grayTipElement: GrayTipElement) { + if (grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) { + if (grayTipElement.jsonGrayTipElement.busiId == 1061) { + const PokeEvent = await this.obContext.apis.FriendApi.parsePrivatePokeEvent(grayTipElement); + if (PokeEvent) { return PokeEvent }; + } else if (grayTipElement.jsonGrayTipElement.busiId == 19324 && msg.peerUid !== '') { + return new OB11FriendAddNoticeEvent(this.core, Number(await this.core.apis.UserApi.getUinByUidV2(msg.peerUid))); } } } @@ -727,8 +721,33 @@ export class OneBotMsgApi { ) { if (msg.senderUin == '0' || msg.senderUin == '') return; if (msg.peerUin == '0' || msg.peerUin == '') return; - //跳过空消息 - const resMsg: OB11Message = { + + const resMsg = this.initializeMessage(msg); + + if (this.core.selfInfo.uin == msg.senderUin) { + resMsg.message_sent_type = 'self'; + } + + if (msg.chatType == ChatType.KCHATTYPEGROUP) { + await this.handleGroupMessage(resMsg, msg); + } else if (msg.chatType == ChatType.KCHATTYPEC2C) { + await this.handlePrivateMessage(resMsg, msg); + } else if (msg.chatType == ChatType.KCHATTYPETEMPC2CFROMGROUP) { + await this.handleTempGroupMessage(resMsg, msg); + } else { + return undefined; + } + + const validSegments = await this.parseMessageSegments(msg, parseMultMsg); + resMsg.message = validSegments; + resMsg.raw_message = validSegments.map(msg => encodeCQCode(msg)).join('').trim(); + + const stringMsg = await this.convertArrayToStringMessage(resMsg); + return { stringMsg, arrayMsg: resMsg }; + } + + private initializeMessage(msg: RawMessage): OB11Message { + return { self_id: parseInt(this.core.selfInfo.uin), user_id: parseInt(msg.senderUin), time: parseInt(msg.msgTime) || Date.now(), @@ -748,37 +767,40 @@ export class OneBotMsgApi { message_format: 'array', post_type: this.core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE, }; - if (this.core.selfInfo.uin == msg.senderUin) { - resMsg.message_sent_type = 'self'; - } - if (msg.chatType == ChatType.KCHATTYPEGROUP) { - resMsg.sub_type = 'normal'; // 这里go-cqhttp是group,而onebot11标准是normal, 蛋疼 - resMsg.group_id = parseInt(msg.peerUin); - let member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin); - if (!member) member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin); - if (member) { - resMsg.sender.role = OB11Construct.groupMemberRole(member.role); - resMsg.sender.nickname = member.nick; - } - } else if (msg.chatType == ChatType.KCHATTYPEC2C) { - resMsg.sub_type = 'friend'; - resMsg.sender.nickname = (await this.core.apis.UserApi.getUserDetailInfo(msg.senderUid)).nick; - } else if (msg.chatType == ChatType.KCHATTYPETEMPC2CFROMGROUP) { - resMsg.sub_type = 'group'; - const ret = await this.core.apis.MsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, msg.senderUid); - if (ret.result === 0) { - const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin); - resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode); - resMsg.sender.nickname = member?.nick ?? member?.cardName ?? '临时会话'; - resMsg.temp_source = resMsg.group_id; - } else { - resMsg.group_id = 284840486; //兜底数据 - resMsg.temp_source = resMsg.group_id; - resMsg.sender.nickname = '临时会话'; - } - } + } - // 处理消息段 + private async handleGroupMessage(resMsg: OB11Message, msg: RawMessage) { + resMsg.sub_type = 'normal'; + resMsg.group_id = parseInt(msg.peerUin); + let member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin); + if (!member) member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin); + if (member) { + resMsg.sender.role = OB11Construct.groupMemberRole(member.role); + resMsg.sender.nickname = member.nick; + } + } + + private async handlePrivateMessage(resMsg: OB11Message, msg: RawMessage) { + resMsg.sub_type = 'friend'; + resMsg.sender.nickname = (await this.core.apis.UserApi.getUserDetailInfo(msg.senderUid)).nick; + } + + private async handleTempGroupMessage(resMsg: OB11Message, msg: RawMessage) { + resMsg.sub_type = 'group'; + const ret = await this.core.apis.MsgApi.getTempChatInfo(ChatType.KCHATTYPETEMPC2CFROMGROUP, msg.senderUid); + if (ret.result === 0) { + const member = await this.core.apis.GroupApi.getGroupMember(msg.peerUin, msg.senderUin); + resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode); + resMsg.sender.nickname = member?.nick ?? member?.cardName ?? '临时会话'; + resMsg.temp_source = resMsg.group_id; + } else { + resMsg.group_id = 284840486; + resMsg.temp_source = resMsg.group_id; + resMsg.sender.nickname = '临时会话'; + } + } + + private async parseMessageSegments(msg: RawMessage, parseMultMsg: boolean): Promise { const msgSegments = await Promise.allSettled(msg.elements.map( async (element) => { for (const key in element) { @@ -793,43 +815,41 @@ export class OneBotMsgApi { element[key], msg, element, - { - parseMultMsg: parseMultMsg - } + { parseMultMsg } ); - // 对于 face 类型的消息,检查是否存在 if (key === 'faceElement' && !parsedElement) { - return null; // 如果没有找到对应的表情,返回 null + return null; } - return parsedElement; } } }, )); - // 过滤掉无效的消息段 - const validSegments = msgSegments.filter(entry => { + return msgSegments.filter(entry => { if (entry.status === 'fulfilled') { return !!entry.value; } else { - this.core.context.logger.logError.bind(this.core.context.logger)('消息段解析失败', entry.reason); + this.core.context.logger.logError('消息段解析失败', entry.reason); return false; } }).map((entry) => (>entry).value).filter(value => value != null); - - const msgAsCQCode = validSegments.map(msg => encodeCQCode(msg)).join('').trim(); - resMsg.message = validSegments; - resMsg.raw_message = msgAsCQCode; - let stringMsg = structuredClone(resMsg); - stringMsg = await this.importArrayTostringMsg(stringMsg); - return { stringMsg: stringMsg, arrayMsg: resMsg }; } - async importArrayTostringMsg(msg: OB11Message) { + + private async convertArrayToStringMessage(originMsg: OB11Message): Promise { + let msg = structuredClone(originMsg); msg.message_format = 'string'; msg.message = msg.raw_message; return msg; } + + async importArrayTostringMsg(originMsg: OB11Message) { + let msg = structuredClone(originMsg); + msg.message_format = 'string'; + msg.message = msg.raw_message; + return msg; + } + async createSendElements( messageData: OB11MessageData[], peer: Peer, @@ -892,11 +912,19 @@ export class OneBotMsgApi { guildId: '', peerUid: peer.peerUid, }, returnMsg.msgId); + setTimeout(() => { deleteAfterSentFiles.forEach(file => { - fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', e)); + try { + if (fs.existsSync(file)) { + fsPromise.unlink(file).then().catch(e => this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', e)); + } + } catch (error) { + this.core.context.logger.logError.bind(this.core.context.logger)('发送消息删除文件失败', (error as Error).message) + } }); }, 60000); + return returnMsg; } diff --git a/src/onebot/api/user.ts b/src/onebot/api/user.ts index eeb1fcc2..68a7a295 100644 --- a/src/onebot/api/user.ts +++ b/src/onebot/api/user.ts @@ -1,4 +1,4 @@ -import { NapCatCore } from '@/core'; +import { GrayTipRovokeElement, NapCatCore, RawMessage } from '@/core'; import { NapCatOneBot11Adapter } from '@/onebot'; import { OB11ProfileLikeEvent } from '@/onebot/event/notice/OB11ProfileLikeEvent'; import { decodeProfileLikeTip } from "@/core/helper/adaptDecoder"; @@ -11,7 +11,7 @@ export class OneBotUserApi { this.obContext = obContext; this.core = core; } - + async parseLikeEvent(wrappedBody: Uint8Array): Promise { const likeTip = decodeProfileLikeTip(Uint8Array.from(wrappedBody)); if (likeTip?.msgType !== 0 || likeTip?.subType !== 203) return; diff --git a/src/onebot/index.ts b/src/onebot/index.ts index 0eb5bdfc..a8858bc6 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -13,6 +13,8 @@ import { Peer, RawMessage, SendStatusType, + NTMsgType, + MessageElement, } from '@/core'; import { OB11ConfigLoader } from '@/onebot/config'; import { @@ -263,33 +265,45 @@ export class NapCatOneBot11Adapter { } }; - const msgIdSend = new LRUCache(100); - const recallMsgs = new LRUCache(100); msgListener.onAddSendMsg = async (msg) => { if (msg.sendStatus == SendStatusType.KSEND_STATUS_SENDING) { - msgIdSend.put(msg.msgId, 0); + await this.core.eventWrapper.registerListen('NodeIKernelMsgListener/onMsgInfoListUpdate', (msgList: RawMessage[]) => { + const report = msgList.find((e) => + e.senderUin == this.core.selfInfo.uin && e.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && e.msgId === msg.msgId + ); + return !!report; + }, 1, 300); + msg.id = MessageUnique.createUniqueMsgId( + { + chatType: msg.chatType, + peerUid: msg.peerUid, + guildId: '', + }, + msg.msgId + ); + //此时上报的seq不是对的 不过对onebot业务无影响 + this.emitMsg(msg); } }; - msgListener.onMsgInfoListUpdate = async (msgList) => { - this.emitRecallMsg(msgList, recallMsgs).catch((e) => - this.context.logger.logError.bind(this.context.logger)('处理消息失败', e) - ); - for (const msg of msgList.filter((e) => e.senderUin == this.core.selfInfo.uin)) { - if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && msgIdSend.get(msg.msgId) == 0) { - msgIdSend.put(msg.msgId, 1); - // 完成后再post - msg.id = MessageUnique.createUniqueMsgId( - { - chatType: msg.chatType, - peerUid: msg.peerUid, - guildId: '', - }, - msg.msgId - ); - this.emitMsg(msg); + msgListener.onMsgRecall = async (chatType: ChatType, uid: string, msgSeq: string) => { + const peer: Peer = { + chatType: chatType, + peerUid: uid, + guildId: '' + }; + let msg = (await this.core.apis.MsgApi.queryMsgsWithFilterExWithSeq(peer, msgSeq)).msgList.find(e => e.msgType == NTMsgType.KMSGTYPEGRAYTIPS); + let element = msg?.elements[0]; + if (msg && element) { + let recallEvent = await this.emitRecallMsg(msg, element); + try { + if (recallEvent) { + await this.networkManager.emitEvent(recallEvent); + } + } catch (e) { + this.context.logger.logError('处理消息撤回失败', e); } } - }; + } msgListener.onKickedOffLine = async (kick) => { const event = new BotOfflineEvent(this.core, kick.tipsTitle, kick.tipsDesc); this.networkManager @@ -526,10 +540,12 @@ export class NapCatOneBot11Adapter { private async emitMsg(message: RawMessage) { const network = Object.values(this.configLoader.configData.network).flat() as Array; this.context.logger.logDebug('收到新消息 RawMessage', message); - await this.handleMsg(message, network); - await this.handleGroupEvent(message); - await this.handlePrivateMsgEvent(message); + await Promise.allSettled([ + this.handleMsg(message, network), + message.chatType == ChatType.KCHATTYPEGROUP ? this.handleGroupEvent(message) : this.handlePrivateMsgEvent(message) + ]); } + private async handleMsg(message: RawMessage, network: Array) { try { const ob11Msg = await this.apis.MsgApi.parseMessageV2(message, this.configLoader.configData.parseMultMsg); @@ -597,9 +613,25 @@ export class NapCatOneBot11Adapter { private async handleGroupEvent(message: RawMessage) { try { - const groupEvent = await this.apis.GroupApi.parseGroupEvent(message); - if (groupEvent) { - this.networkManager.emitEvent(groupEvent); + // 群名片修改事件解析 任何都该判断 + if (message.senderUin && message.senderUin !== '0') { + const cardChangedEvent = await this.apis.GroupApi.parseCardChangedEvent(message); + 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); + } + } 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); + } } } catch (e) { this.context.logger.logError('constructGroupEvent error: ', e); @@ -608,59 +640,51 @@ export class NapCatOneBot11Adapter { private async handlePrivateMsgEvent(message: RawMessage) { try { - const privateEvent = await this.apis.MsgApi.parsePrivateMsgEvent(message); - if (privateEvent) { - this.networkManager.emitEvent(privateEvent); + if (message.msgType === NTMsgType.KMSGTYPEGRAYTIPS) { + // 灰条为单元素消息 + const grayTipElement = message.elements[0].grayTipElement; + if (grayTipElement) { + const event = await this.apis.MsgApi.parsePrivateMsgEvent(message, grayTipElement); + event && await this.networkManager.emitEvent(event); + } } } catch (e) { this.context.logger.logError('constructPrivateEvent error: ', e); } } - private async emitRecallMsg(msgList: RawMessage[], cache: LRUCache) { - for (const message of msgList) { - // log("message update", message.sendStatus, message.msgId, message.msgSeq) - const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' }; - if (message.recallTime != '0' && !cache.get(message.msgId)) { - //TODO: 这个判断方法不太好,应该使用灰色消息元素来判断? - cache.put(message.msgId, true); - // 撤回消息上报 - let oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId); - if (!oriMessageId) { - oriMessageId = MessageUnique.createUniqueMsgId(peer, message.msgId); - } - if (message.chatType == ChatType.KCHATTYPEC2C) { - const friendRecallEvent = new OB11FriendRecallNoticeEvent( - this.core, - +message.senderUin, - oriMessageId - ); - this.networkManager - .emitEvent(friendRecallEvent) - .catch((e) => - this.context.logger.logError.bind(this.context.logger)('处理好友消息撤回失败', e) - ); - } else if (message.chatType == ChatType.KCHATTYPEGROUP) { - let operatorId = message.senderUin; - for (const element of message.elements) { - const operatorUid = element.grayTipElement?.revokeElement.operatorUid; - if (!operatorUid) return; - const operator = await this.core.apis.GroupApi.getGroupMember(message.peerUin, operatorUid); - operatorId = operator?.uin ?? message.senderUin; - } - const groupRecallEvent = new OB11GroupRecallNoticeEvent( - this.core, - +message.peerUin, - +message.senderUin, - +operatorId, - oriMessageId - ); - this.networkManager - .emitEvent(groupRecallEvent) - .catch((e) => this.context.logger.logError.bind(this.context.logger)('处理群消息撤回失败', e)); - } - } + + private async emitRecallMsg(message: RawMessage, element: MessageElement) { + const peer: Peer = { chatType: message.chatType, peerUid: message.peerUid, guildId: '' }; + let oriMessageId = MessageUnique.getShortIdByMsgId(message.msgId) ?? MessageUnique.createUniqueMsgId(peer, message.msgId); + if (message.chatType == ChatType.KCHATTYPEC2C) { + return await this.emitFriendRecallMsg(message, oriMessageId, element); + } else if (message.chatType == ChatType.KCHATTYPEGROUP) { + return await this.emitGroupRecallMsg(message, oriMessageId, element); } + } + + private async emitFriendRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) { + return new OB11FriendRecallNoticeEvent( + this.core, + +message.senderUin, + oriMessageId + ); + } + + private async emitGroupRecallMsg(message: RawMessage, oriMessageId: number, element: MessageElement) { + const operatorUid = element.grayTipElement?.revokeElement.operatorUid; + if (!operatorUid) return undefined; + let operatorId = message.senderUin ?? await this.core.apis.UserApi.getUinByUidV2(operatorUid); + return new OB11GroupRecallNoticeEvent( + this.core, + +message.peerUin, + +message.senderUin, + +operatorId, + oriMessageId + ); + } + } export * from './types'; diff --git a/src/onebot/network/index.ts b/src/onebot/network/index.ts index 5663f241..cbe1140b 100644 --- a/src/onebot/network/index.ts +++ b/src/onebot/network/index.ts @@ -37,6 +37,10 @@ export class OB11NetworkManager { return Promise.all(Array.from(this.adapters.values()).map(adapter => adapter.onEvent(event))); } + async emitEvents(events: OB11EmitEventContent[]) { + return Promise.all(events.map(event => this.emitEvent(event))); + } + async emitEventByName(names: string[], event: OB11EmitEventContent) { return Promise.all(names.map(name => { const adapter = this.adapters.get(name); @@ -71,7 +75,7 @@ export class OB11NetworkManager { async closeSomeAdaterWhenOpen(adaptersToClose: IOB11NetworkAdapter[]) { for (const adapter of adaptersToClose) { this.adapters.delete(adapter.name); - if(adapter.isEnable){ + if (adapter.isEnable) { await adapter.close(); } }