diff --git a/src/onebot/action/extends/GetFriendWithCategory.ts b/src/onebot/action/extends/GetFriendWithCategory.ts index 849bc4d1..08ac9627 100644 --- a/src/onebot/action/extends/GetFriendWithCategory.ts +++ b/src/onebot/action/extends/GetFriendWithCategory.ts @@ -1,4 +1,4 @@ -import { OB11Constructor } from '@/onebot/helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; diff --git a/src/onebot/action/go-cqhttp/GetForwardMsg.ts b/src/onebot/action/go-cqhttp/GetForwardMsg.ts index 8e37e342..0199593d 100644 --- a/src/onebot/action/go-cqhttp/GetForwardMsg.ts +++ b/src/onebot/action/go-cqhttp/GetForwardMsg.ts @@ -1,9 +1,10 @@ import BaseAction from '../BaseAction'; import { OB11ForwardMessage, OB11Message, OB11MessageData } from '../../types'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import { ActionName } from '../types'; import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { MessageUnique } from '@/common/utils/MessageUnique'; +import { RawNTMsg2Onebot } from '@/onebot/helper'; const SchemaData = { type: 'object', @@ -40,7 +41,7 @@ export class GoCQHTTPGetForwardMsgAction extends BaseAction { } const msgList = data.msgList; const messages = (await Promise.all(msgList.map(async msg => { - const resMsg = await OB11Constructor.message(this.CoreContext, this.OneBotContext, msg); + const resMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg); if (!resMsg) return; resMsg.message_id = MessageUnique.createMsg({ guildId: '', diff --git a/src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts b/src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts index cd75780c..31eac531 100644 --- a/src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts +++ b/src/onebot/action/go-cqhttp/GetFriendMsgHistory.ts @@ -2,9 +2,10 @@ import BaseAction from '../BaseAction'; import { OB11Message } from '../../types'; import { ActionName } from '../types'; import { ChatType, RawMessage } from '@/core/entities'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { MessageUnique } from '@/common/utils/MessageUnique'; +import { RawNTMsg2Onebot } from '@/onebot/helper'; interface Response { messages: OB11Message[]; @@ -53,7 +54,7 @@ export default class GetFriendMsgHistory extends BaseAction { msg.id = MessageUnique.createMsg({ guildId: '', chatType: msg.chatType, peerUid: msg.peerUid }, msg.msgId); })); //转换消息 - const ob11MsgList = (await Promise.all(msgList.map(msg => OB11Constructor.message(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg); + const ob11MsgList = (await Promise.all(msgList.map(msg => RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg); return { 'messages': ob11MsgList }; } } diff --git a/src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts b/src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts index 584ab42f..008e2a8a 100644 --- a/src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts +++ b/src/onebot/action/go-cqhttp/GetGroupMsgHistory.ts @@ -2,9 +2,10 @@ import BaseAction from '../BaseAction'; import { OB11Message } from '../../types'; import { ActionName } from '../types'; import { ChatType, Peer, RawMessage } from '@/core/entities'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { MessageUnique } from '@/common/utils/MessageUnique'; +import { RawNTMsg2Onebot } from '@/onebot/helper'; interface Response { messages: OB11Message[]; @@ -48,7 +49,7 @@ export default class GoCQHTTPGetGroupMsgHistory extends BaseAction OB11Constructor.message(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg); + const ob11MsgList = (await Promise.all(msgList.map(msg => RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg)))).filter(msg => !!msg); return { 'messages': ob11MsgList }; } } diff --git a/src/onebot/action/go-cqhttp/GetStrangerInfo.ts b/src/onebot/action/go-cqhttp/GetStrangerInfo.ts index aea502b7..cdc0a8cf 100644 --- a/src/onebot/action/go-cqhttp/GetStrangerInfo.ts +++ b/src/onebot/action/go-cqhttp/GetStrangerInfo.ts @@ -1,6 +1,6 @@ import BaseAction from '../BaseAction'; import { OB11User, OB11UserSex } from '../../types'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import { ActionName } from '../types'; import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { calcQQLevel } from '@/common/utils/helper'; diff --git a/src/onebot/action/group/GetGroupInfo.ts b/src/onebot/action/group/GetGroupInfo.ts index b8d9d1e1..fc069652 100644 --- a/src/onebot/action/group/GetGroupInfo.ts +++ b/src/onebot/action/group/GetGroupInfo.ts @@ -1,5 +1,5 @@ import { OB11Group } from '../../types'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; import { FromSchema, JSONSchema } from 'json-schema-to-ts'; diff --git a/src/onebot/action/group/GetGroupList.ts b/src/onebot/action/group/GetGroupList.ts index a64ad240..04b93413 100644 --- a/src/onebot/action/group/GetGroupList.ts +++ b/src/onebot/action/group/GetGroupList.ts @@ -1,5 +1,5 @@ import { OB11Group } from '../../types'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; import { Group } from '@/core/entities'; diff --git a/src/onebot/action/group/GetGroupMemberInfo.ts b/src/onebot/action/group/GetGroupMemberInfo.ts index 0c24f599..c399a030 100644 --- a/src/onebot/action/group/GetGroupMemberInfo.ts +++ b/src/onebot/action/group/GetGroupMemberInfo.ts @@ -1,5 +1,5 @@ import { OB11GroupMember } from '../../types'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; import { FromSchema, JSONSchema } from 'json-schema-to-ts'; diff --git a/src/onebot/action/group/GetGroupMemberList.ts b/src/onebot/action/group/GetGroupMemberList.ts index f6c77747..ab0015c0 100644 --- a/src/onebot/action/group/GetGroupMemberList.ts +++ b/src/onebot/action/group/GetGroupMemberList.ts @@ -1,5 +1,5 @@ import { OB11GroupMember } from '../../types'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; import { FromSchema, JSONSchema } from 'json-schema-to-ts'; diff --git a/src/onebot/action/msg/GetMsg.ts b/src/onebot/action/msg/GetMsg.ts index 72d383d3..656a97dc 100644 --- a/src/onebot/action/msg/GetMsg.ts +++ b/src/onebot/action/msg/GetMsg.ts @@ -1,9 +1,10 @@ import { OB11Message } from '../../types'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import { MessageUnique } from '@/common/utils/MessageUnique'; +import { RawNTMsg2Onebot } from '@/onebot/helper'; export type ReturnDataType = OB11Message @@ -37,7 +38,7 @@ class GetMsg extends BaseAction { const msg = await NTQQMsgApi.getMsgsByMsgId( peer, [msgIdWithPeer?.MsgId || payload.message_id.toString()]); - const retMsg = await OB11Constructor.message(this.CoreContext, this.OneBotContext, msg.msgList[0], 'array'); + const retMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext, msg.msgList[0], 'array'); if (!retMsg) throw Error('消息为空'); try { retMsg.message_id = MessageUnique.createMsg(peer, msg.msgList[0].msgId)!; diff --git a/src/onebot/action/system/GetLoginInfo.ts b/src/onebot/action/system/GetLoginInfo.ts index 4ae2c4e5..9f9ce789 100644 --- a/src/onebot/action/system/GetLoginInfo.ts +++ b/src/onebot/action/system/GetLoginInfo.ts @@ -1,5 +1,5 @@ import { OB11User } from '../../types'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; diff --git a/src/onebot/action/user/GetFriendList.ts b/src/onebot/action/user/GetFriendList.ts index a26689f9..1de6fc86 100644 --- a/src/onebot/action/user/GetFriendList.ts +++ b/src/onebot/action/user/GetFriendList.ts @@ -1,5 +1,5 @@ import { OB11User } from '../../types'; -import { OB11Constructor } from '../../helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; import { FromSchema, JSONSchema } from 'json-schema-to-ts'; diff --git a/src/onebot/action/user/GetRecentContact.ts b/src/onebot/action/user/GetRecentContact.ts index 8a43babc..4b2c61db 100644 --- a/src/onebot/action/user/GetRecentContact.ts +++ b/src/onebot/action/user/GetRecentContact.ts @@ -1,7 +1,8 @@ import { FromSchema, JSONSchema } from 'json-schema-to-ts'; import BaseAction from '../BaseAction'; import { ActionName } from '../types'; -import { OB11Constructor } from '@/onebot/helper/data'; +import { OB11Constructor } from '@/onebot/helper/converter'; +import { RawNTMsg2Onebot } from '@/onebot/helper'; const SchemaData = { type: 'object', @@ -24,7 +25,7 @@ export default class GetRecentContact extends BaseAction { const FastMsg = await NTQQMsgApi.getMsgsByMsgId({ chatType: t.chatType, peerUid: t.peerUid }, [t.msgId]); if (FastMsg.msgList.length > 0) { //扩展ret.info.changedList - const lastestMsg = await OB11Constructor.message(this.CoreContext, this.OneBotContext,FastMsg.msgList[0], 'array'); + const lastestMsg = await RawNTMsg2Onebot(this.CoreContext, this.OneBotContext,FastMsg.msgList[0], 'array'); return { lastestMsg: lastestMsg, peerUin: t.peerUin, diff --git a/src/onebot/api/group.ts b/src/onebot/api/group.ts index 1c9ede44..34defecc 100644 --- a/src/onebot/api/group.ts +++ b/src/onebot/api/group.ts @@ -45,6 +45,32 @@ export class OneBotGroupApi { } return undefined; } + async parseGroupIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) { + this.coreContext.context.logger.logDebug('收到新人被邀请进群消息', grayTipElement); + const xmlElement = grayTipElement.xmlElement; + if (xmlElement?.content) { + const regex = /jp="(\d+)"/g; + + const matches = []; + let match = null; + + while ((match = regex.exec(xmlElement.content)) !== null) { + matches.push(match[1]); + } + // log("新人进群匹配到的QQ号", matches) + if (matches.length === 2) { + const [inviter, invitee] = matches; + return new OB11GroupIncreaseEvent( + this.coreContext, + parseInt(GroupCode), + parseInt(invitee), + parseInt(inviter), + 'invite' + ); + } + } + return undefined; + } async parseGroupMemberIncreaseEvent(GroupCode: string, grayTipElement: GrayTipElement) { const NTQQGroupApi = this.coreContext.apis.GroupApi; let groupElement = grayTipElement?.groupElement; diff --git a/src/onebot/helper/converter.ts b/src/onebot/helper/converter.ts index e69de29b..350ae812 100644 --- a/src/onebot/helper/converter.ts +++ b/src/onebot/helper/converter.ts @@ -0,0 +1,268 @@ +import { UUIDConverter, calcQQLevel } from '@/common/utils/helper'; +import { MessageUnique } from '@/common/utils/MessageUnique'; +import { NapCatCore, RawMessage, ChatType, AtType, VideoElement, FaceIndex, NTGrayTipElementSubTypeV2, TipGroupElementType, Peer, SelfInfo, FriendV2, Friend, Sex, GroupMember, User, Group } from '@/core'; +import { NapCatOneBot11Adapter } from '..'; +import { OB11BaseNoticeEvent } from '../event/notice/OB11BaseNoticeEvent'; +import { OB11FriendAddNoticeEvent } from '../event/notice/OB11FriendAddNoticeEvent'; +import { OB11GroupCardEvent } from '../event/notice/OB11GroupCardEvent'; +import { OB11GroupDecreaseEvent } from '../event/notice/OB11GroupDecreaseEvent'; +import { OB11GroupEssenceEvent } from '../event/notice/OB11GroupEssenceEvent'; +import { OB11GroupNoticeEvent } from '../event/notice/OB11GroupNoticeEvent'; +import { OB11GroupTitleEvent } from '../event/notice/OB11GroupTitleEvent'; +import { OB11GroupUploadNoticeEvent } from '../event/notice/OB11GroupUploadNoticeEvent'; +import { OB11GroupPokeEvent } from '../event/notice/OB11PokeEvent'; +import { EventType } from '../event/OB11BaseEvent'; +import { OB11Message, OB11MessageData, OB11MessageDataType, OB11User, OB11GroupMemberRole, OB11UserSex, OB11GroupMember, OB11Group } from '../types'; +import { encodeCQCode } from './cqcode'; + + +export class OB11Constructor { + static async PrivateEvent(core: NapCatCore, obContext: NapCatOneBot11Adapter, msg: RawMessage): Promise { + if (msg.chatType !== ChatType.friend) { + return; + } + for (const element of msg.elements) { + if (element.grayTipElement) { + if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) { + if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) { + let PokeEvent = await obContext.apiContext.FriendApi.parsePrivatePokeEvent(element.grayTipElement); + if (PokeEvent) return PokeEvent; + } + } + if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) { + //好友添加成功事件 + if (element.grayTipElement.xmlElement.templId === '10229' && msg.peerUin !== '') { + return new OB11FriendAddNoticeEvent(core, parseInt(msg.peerUin)); + } + } + } + } + } + + static async GroupEvent(core: NapCatCore, obContext: NapCatOneBot11Adapter, msg: RawMessage): Promise { + const NTQQGroupApi = core.apis.GroupApi; + const NTQQUserApi = core.apis.UserApi; + const NTQQMsgApi = core.apis.MsgApi; + const logger = core.context.logger; + if (msg.chatType !== ChatType.group) { + return; + } + //log("group msg", msg); + if (msg.senderUin && msg.senderUin !== '0') { + const member = await NTQQGroupApi.getGroupMember(msg.peerUid, msg.senderUin); + if (member && member.cardName !== msg.sendMemberName) { + const newCardName = msg.sendMemberName || ''; + const event = new OB11GroupCardEvent(core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName); + member.cardName = newCardName; + return event; + } + } + + for (const element of msg.elements) { + if (element.grayTipElement && element.grayTipElement.groupElement) { + const groupElement = element.grayTipElement.groupElement; + if (groupElement.type == TipGroupElementType.memberIncrease) { + let MemberIncreaseEvent = await obContext.apiContext.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement); + if (MemberIncreaseEvent) return MemberIncreaseEvent; + } else if (groupElement.type === TipGroupElementType.ban) { + let BanEvent = await obContext.apiContext.GroupApi.parseGroupBanEvent(msg.peerUid, element.grayTipElement); + if (BanEvent) return BanEvent; + } else if (groupElement.type == TipGroupElementType.kicked) { + NTQQGroupApi.quitGroup(msg.peerUid).then(); + try { + let KickEvent = await obContext.apiContext.GroupApi.parseGroupKickEvent(msg.peerUid, element.grayTipElement); + if (KickEvent) return KickEvent; + } catch (e) { + return new OB11GroupDecreaseEvent( + core, + parseInt(msg.peerUid), + parseInt(core.selfInfo.uin), + 0, + 'leave' + ); + } + } + } else if (element.fileElement) { + return new OB11GroupUploadNoticeEvent( + core, + parseInt(msg.peerUid), parseInt(msg.senderUin || ''), + { + id: element.fileElement.fileUuid!, + name: element.fileElement.fileName, + size: parseInt(element.fileElement.fileSize), + busid: element.fileElement.fileBizId || 0, + } + ); + } + if (element.grayTipElement) { + if (element.grayTipElement.xmlElement?.templId === '10382') { + let emojiLikeEvent = await obContext.apiContext.GroupApi.parseGroupEmjioLikeEvent(msg.peerUid, element.grayTipElement); + if (emojiLikeEvent) return emojiLikeEvent; + } + if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) { + let GroupIncreaseEvent = await obContext.apiContext.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement); + if (GroupIncreaseEvent) return GroupIncreaseEvent; + } + + //代码歧义 GrayTipElementSubType.MEMBER_NEW_TITLE + 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( + core, + parseInt(msg.peerUid), + parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[0].uid))!), + parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[1].uid))!), + pokedetail + ); + } + } + if (element.grayTipElement.jsonGrayTipElement.busiId == 2401) { + const searchParams = new URL(json.items[0].jp).searchParams; + const msgSeq = searchParams.get('msgSeq')!; + const Group = searchParams.get('groupCode'); + // const businessId = searchParams.get('businessid'); + const Peer: Peer = { + guildId: '', + chatType: ChatType.group, + peerUid: Group!, + }; + const msgData = await NTQQMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true); + return new OB11GroupEssenceEvent( + core, + parseInt(msg.peerUid), + MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!, + parseInt(msgData.msgList[0].senderUin) + ); + // 获取MsgSeq+Peer可获取具体消息 + } + if (element.grayTipElement.jsonGrayTipElement.busiId == 2407) { + //下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE + const memberUin = json.items[1].param[0]; + const title = json.items[3].txt; + logger.logDebug('收到群成员新头衔消息', json); + return new OB11GroupTitleEvent( + core, + parseInt(msg.peerUid), + parseInt(memberUin), + title + ); + } + } + } + } + } + + static selfInfo(selfInfo: SelfInfo): OB11User { + return { + user_id: parseInt(selfInfo.uin), + nickname: selfInfo.nick, + }; + } + + static friendsV2(friends: FriendV2[]): OB11User[] { + const data: OB11User[] = []; + friends.forEach(friend => { + const sexValue = this.sex(friend.baseInfo.sex!); + data.push({ + ...friend.baseInfo, + ...friend.coreInfo, + user_id: parseInt(friend.coreInfo.uin), + nickname: friend.coreInfo.nick, + remark: friend.coreInfo.nick, + sex: sexValue, + level: 0, + }); + }); + return data; + } + + static friends(friends: Friend[]): OB11User[] { + const data: OB11User[] = []; + friends.forEach(friend => { + const sexValue = this.sex(friend.sex!); + data.push({ + user_id: parseInt(friend.uin), + nickname: friend.nick, + remark: friend.remark, + sex: sexValue, + level: 0, + }); + }); + return data; + } + + static groupMemberRole(role: number): OB11GroupMemberRole | undefined { + return { + 4: OB11GroupMemberRole.owner, + 3: OB11GroupMemberRole.admin, + 2: OB11GroupMemberRole.member, + }[role]; + } + + static sex(sex: Sex): OB11UserSex { + const sexMap = { + [Sex.male]: OB11UserSex.male, + [Sex.female]: OB11UserSex.female, + [Sex.unknown]: OB11UserSex.unknown, + }; + return sexMap[sex] || OB11UserSex.unknown; + } + + static groupMember(group_id: string, member: GroupMember): OB11GroupMember { + return { + group_id: parseInt(group_id), + user_id: parseInt(member.uin), + nickname: member.nick, + card: member.cardName, + sex: OB11Constructor.sex(member.sex!), + age: member.age ?? 0, + area: '', + level: '0', + qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0, + join_time: 0, // 暂时没法获取 + last_sent_time: 0, // 暂时没法获取 + title_expire_time: 0, + unfriendly: false, + card_changeable: true, + is_robot: member.isRobot, + shut_up_timestamp: member.shutUpTime, + role: OB11Constructor.groupMemberRole(member.role), + title: member.memberSpecialTitle || '', + }; + } + + static stranger(user: User): OB11User { + //logDebug('construct ob11 stranger', user); + return { + ...user, + user_id: parseInt(user.uin), + nickname: user.nick, + sex: OB11Constructor.sex(user.sex!), + age: 0, + qid: user.qid, + login_days: 0, + level: user.qqLevel && calcQQLevel(user.qqLevel) || 0, + }; + } + + + static group(group: Group): OB11Group { + return { + group_id: parseInt(group.groupCode), + group_name: group.groupName, + member_count: group.memberCount, + max_member_count: group.maxMember, + }; + } + + static groups(groups: Group[]): OB11Group[] { + return groups.map(OB11Constructor.group); + } +} diff --git a/src/onebot/helper/data.ts b/src/onebot/helper/data.ts deleted file mode 100644 index c7ca308c..00000000 --- a/src/onebot/helper/data.ts +++ /dev/null @@ -1,646 +0,0 @@ -import fastXmlParser from 'fast-xml-parser'; -import { - OB11Group, - OB11GroupMember, - OB11GroupMemberRole, - OB11Message, - OB11MessageData, - OB11MessageDataType, - OB11User, - OB11UserSex, -} from '../types'; -import { - AtType, - ChatType, - FaceIndex, - Friend, - FriendV2, - Group, - GroupMember, - NTGrayTipElementSubTypeV2, - Peer, - RawMessage, - SelfInfo, - Sex, - TipGroupElementType, - User, - VideoElement, -} from '@/core/entities'; -import { EventType } from '../event/OB11BaseEvent'; -import { encodeCQCode } from './cqcode'; -import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent'; -import { OB11GroupBanEvent } from '../event/notice/OB11GroupBanEvent'; -import { OB11GroupCardEvent } from '../event/notice/OB11GroupCardEvent'; -import { OB11GroupUploadNoticeEvent } from '../event/notice/OB11GroupUploadNoticeEvent'; -import { OB11GroupNoticeEvent } from '../event/notice/OB11GroupNoticeEvent'; -import { calcQQLevel, sleep, UUIDConverter } from '@/common/utils/helper'; -import { OB11GroupTitleEvent } from '../event/notice/OB11GroupTitleEvent'; -import { OB11GroupDecreaseEvent } from '../event/notice/OB11GroupDecreaseEvent'; -import { OB11GroupMsgEmojiLikeEvent } from '@/onebot/event/notice/OB11MsgEmojiLikeEvent'; -import { OB11FriendPokeEvent, OB11GroupPokeEvent } from '../event/notice/OB11PokeEvent'; -import { OB11FriendAddNoticeEvent } from '../event/notice/OB11FriendAddNoticeEvent'; -import { OB11BaseNoticeEvent } from '../event/notice/OB11BaseNoticeEvent'; -import { OB11GroupEssenceEvent } from '../event/notice/OB11GroupEssenceEvent'; -import { MessageUnique } from '@/common/utils/MessageUnique'; -import { NapCatCore } from '@/core'; -import { NapCatOneBot11Adapter } from '..'; - -export class OB11Constructor { - static async message( - core: NapCatCore, - obcore: NapCatOneBot11Adapter, - msg: RawMessage, - messagePostFormat: string = obcore.configLoader.configData.messagePostFormat - ): Promise { - if (msg.senderUin == "0" || msg.senderUin == "") return; - if (msg.peerUin == "0" || msg.peerUin == "") return; - //跳过空消息 - const NTQQGroupApi = core.apis.GroupApi; - const NTQQUserApi = core.apis.UserApi; - const NTQQFileApi = core.apis.FileApi; - const NTQQMsgApi = core.apis.MsgApi; - const logger = core.context.logger; - const resMsg: OB11Message = { - self_id: parseInt(core.selfInfo.uin), - user_id: parseInt(msg.senderUin!), - time: parseInt(msg.msgTime) || Date.now(), - message_id: msg.id!, - message_seq: msg.id!, - real_id: msg.id!, - message_type: msg.chatType == ChatType.group ? 'group' : 'private', - sender: { - user_id: parseInt(msg.senderUin || '0'), - nickname: msg.sendNickName, - card: msg.sendMemberName || '', - }, - raw_message: '', - font: 14, - sub_type: 'friend', - message: messagePostFormat === 'string' ? '' : [], - message_format: messagePostFormat === 'string' ? 'string' : 'array', - post_type: core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE, - }; - if (msg.chatType == ChatType.group) { - resMsg.sub_type = 'normal'; // 这里go-cqhttp是group,而onebot11标准是normal, 蛋疼 - resMsg.group_id = parseInt(msg.peerUin); - //直接去QQNative取 - let member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin); - if (!member) member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin); - if (member) { - resMsg.sender.role = OB11Constructor.groupMemberRole(member.role); - resMsg.sender.nickname = member.nick; - } - } else if (msg.chatType == ChatType.friend) { - resMsg.sub_type = 'friend'; - resMsg.sender.nickname = (await NTQQUserApi.getUserDetailInfo(msg.senderUid)).nick; - //const user = await NTQQUserApi.getUserDetailInfoByUin(msg.senderUin!); - //resMsg.sender.nickname = user.info.nick; - } else if (msg.chatType == ChatType.temp) { - resMsg.sub_type = 'group'; - const ret = await NTQQMsgApi.getTempChatInfo(ChatType.temp, msg.senderUid); - if (ret.result === 0) { - resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode); - resMsg.sender.nickname = ret.tmpChatInfo!.fromNick; - } else { - resMsg.group_id = 284840486;//兜底数据 - resMsg.sender.nickname = "临时会话"; - } - } - for (const element of msg.elements) { - let message_data: OB11MessageData = { - data: {} as any, - type: 'unknown' as any, - }; - if (element.textElement && element.textElement?.atType !== AtType.notAt) { - let qq: `${number}` | 'all'; - let name: string | undefined; - if (element.textElement.atType == AtType.atAll) { - qq = 'all'; - } else { - const { atNtUid, content } = element.textElement; - let atQQ = element.textElement.atUid; - if (!atQQ || atQQ === '0') { - atQQ = await NTQQUserApi.getUinByUidV2(atNtUid); - } - if (atQQ) { - qq = atQQ as `${number}`; - name = content.replace('@', ''); - } - } - message_data = { - type: OB11MessageDataType.at, - data: { - qq: qq!, - name, - }, - }; - } else if (element.textElement) { - message_data['type'] = OB11MessageDataType.text; - - let text = element.textElement.content; - if (!text.trim()) { - continue; - } - // 兼容 9.7.x 换行符 - if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) { - text = text.replace(/\r/g, '\n'); - } - message_data['data']['text'] = text; - } else if (element.replyElement) { - message_data['type'] = OB11MessageDataType.reply; - //log("收到回复消息", element.replyElement); - try { - const records = msg.records.find(msgRecord => msgRecord.msgId === element?.replyElement?.sourceMsgIdInRecords); - const peer = { - chatType: msg.chatType, - peerUid: msg.peerUid, - guildId: '', - }; - let replyMsg: RawMessage | undefined; - if (!records) throw new Error('找不到回复消息'); - replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({ - peerUid: msg.peerUid, - guildId: '', - chatType: msg.chatType, - }, element.replyElement.replayMsgSeq, 1, true, true)).msgList.find(msg => msg.msgRandom === records.msgRandom); - if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) { - replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0]; - } - if (msg.peerUin == '284840486') { - //合并消息内侧 消息具体定位不到 - } - if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') { - const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList; - if (replyMsgList.length < 1) { - throw new Error('回复消息消息验证失败'); - } - replyMsg = replyMsgList.filter(e => e.msgSeq == records.msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0]; - } - message_data['data']['id'] = MessageUnique.createMsg({ - peerUid: msg.peerUid, - guildId: '', - chatType: msg.chatType, - }, replyMsg.msgId)?.toString(); - //log("找到回复消息", message_data['data']['id'], replyMsg.msgList[0].msgId) - } catch (e: any) { - message_data['type'] = 'unknown' as any; - message_data['data'] = undefined; - logger.logError('获取不到引用的消息', e.stack, element.replyElement.replayMsgSeq); - } - - } else if (element.picElement) { - message_data['type'] = OB11MessageDataType.image; - // message_data["data"]["file"] = element.picElement.sourcePath - message_data['data']['file'] = element.picElement.fileName; - message_data['data']['subType'] = element.picElement.picSubType; - message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId); - // message_data["data"]["path"] = element.picElement.sourcePath - - try { - message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement); - } catch (e: any) { - logger.logError('获取图片url失败', e.stack); - } - //console.log(message_data['data']['url']) - // message_data["data"]["file_id"] = element.picElement.fileUuid - message_data['data']['file_size'] = element.picElement.fileSize; - } else if (element.fileElement) { - const FileElement = element.fileElement; - message_data['type'] = OB11MessageDataType.file; - message_data['data']['file'] = FileElement.fileName; - message_data['data']['path'] = FileElement.filePath; - message_data['data']['url'] = FileElement.filePath; - message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId); - message_data['data']['file_size'] = FileElement.fileSize; - await NTQQFileApi.addFileCache( - { - peerUid: msg.peerUid, - chatType: msg.chatType, - guildId: '', - }, - msg.msgId, - msg.msgSeq, - msg.senderUid, - element.elementId, - element.elementType.toString(), - FileElement.fileSize, - FileElement.fileName, - ); - } else if (element.videoElement) { - const videoElement: VideoElement = element.videoElement; - //读取视频链接并兜底 - let videoUrl;//Array - if (msg.peerUin === '284840486') { - //合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Taged TODO - } - try { - - videoUrl = await NTQQFileApi.getVideoUrl({ - chatType: msg.chatType, - peerUid: msg.peerUid, - guildId: '0', - }, msg.msgId, element.elementId); - } catch (error) { - videoUrl = undefined; - } - //读取在线URL - let videoDownUrl = undefined; - - if (videoUrl) { - const videoDownUrlTemp = videoUrl.find((url) => { - return !!url.url; - }); - if (videoDownUrlTemp) { - videoDownUrl = videoDownUrlTemp.url; - } - } - //开始兜底 - if (!videoDownUrl) { - videoDownUrl = videoElement.filePath; - } - message_data['type'] = OB11MessageDataType.video; - message_data['data']['file'] = videoElement.fileName; - message_data['data']['path'] = videoDownUrl; - message_data['data']['url'] = videoDownUrl; - message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId); - message_data['data']['file_size'] = videoElement.fileSize; - - await NTQQFileApi.addFileCache( - { - peerUid: msg.peerUid, - chatType: msg.chatType, - guildId: '', - }, - msg.msgId, - msg.msgSeq, - msg.senderUid, - element.elementId, - element.elementType.toString(), - videoElement.fileSize || '0', - videoElement.fileName, - ); - } else if (element.pttElement) { - message_data['type'] = OB11MessageDataType.voice; - message_data['data']['file'] = element.pttElement.fileName; - message_data['data']['path'] = element.pttElement.filePath; - //message_data['data']['file_id'] = element.pttElement.fileUuid; - message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId); - message_data['data']['file_size'] = element.pttElement.fileSize; - await NTQQFileApi.addFileCache({ - peerUid: msg.peerUid, - chatType: msg.chatType, - guildId: '', - }, - msg.msgId, - msg.msgSeq, - msg.senderUid, - element.elementId, - element.elementType.toString(), - element.pttElement.fileSize || '0', - element.pttElement.fileUuid || '', - ); - //以uuid作为文件名 - } else if (element.arkElement) { - message_data['type'] = OB11MessageDataType.json; - message_data['data']['data'] = element.arkElement.bytesData; - } else if (element.faceElement) { - const faceId = element.faceElement.faceIndex; - if (faceId === FaceIndex.dice) { - message_data['type'] = OB11MessageDataType.dice; - message_data['data']['result'] = element.faceElement.resultId; - } else if (faceId === FaceIndex.RPS) { - message_data['type'] = OB11MessageDataType.RPS; - message_data['data']['result'] = element.faceElement.resultId; - } else { - message_data['type'] = OB11MessageDataType.face; - message_data['data']['id'] = element.faceElement.faceIndex.toString(); - } - } else if (element.marketFaceElement) { - message_data['type'] = OB11MessageDataType.mface; - message_data['data']['summary'] = element.marketFaceElement.faceName; - const md5 = element.marketFaceElement.emojiId; - // 取md5的前两位 - const dir = md5.substring(0, 2); - // 获取组装url - // const url = `https://p.qpic.cn/CDN_STATIC/0/data/imgcache/htdocs/club/item/parcel/item/${dir}/${md5}/300x300.gif?max_age=31536000`; - message_data['data']['url'] = `https://gxh.vip.qq.com/club/item/parcel/item/${dir}/${md5}/raw300.gif`; - message_data['data']['emoji_id'] = element.marketFaceElement.emojiId; - message_data['data']['emoji_package_id'] = String(element.marketFaceElement.emojiPackageId); - message_data['data']['key'] = element.marketFaceElement.key; - //mFaceCache.set(md5, element.marketFaceElement.faceName); - } else if (element.markdownElement) { - message_data['type'] = OB11MessageDataType.markdown; - message_data['data']['data'] = element.markdownElement.content; - } else if (element.multiForwardMsgElement) { - message_data['type'] = OB11MessageDataType.forward; - message_data['data']['id'] = msg.msgId; - const ParentMsgPeer = msg.parentMsgPeer ?? { - chatType: msg.chatType, - guildId: '', - peerUid: msg.peerUid, - }; - //判断是否在合并消息内 - msg.parentMsgIdList = msg.parentMsgIdList ?? []; - //首次列表不存在则开始创建 - msg.parentMsgIdList.push(msg.msgId); - //let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2]; - //加入自身MsgId - const MultiMsgs = (await NTQQMsgApi.getMultiMsg(ParentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList; - //拉取下级消息 - if (!MultiMsgs) continue; - //拉取失败则跳过 - message_data['data']['content'] = []; - for (const MultiMsg of MultiMsgs) { - //对每条拉取的消息传递ParentMsgPeer修正Peer - MultiMsg.parentMsgPeer = ParentMsgPeer; - MultiMsg.parentMsgIdList = msg.parentMsgIdList; - MultiMsg.id = MessageUnique.createMsg(ParentMsgPeer, MultiMsg.msgId);//该ID仅用查看 无法调用 - const msgList = await OB11Constructor.message(core, obcore, MultiMsg, messagePostFormat); - if (!msgList) continue; - message_data['data']['content'].push(msgList); - //console.log("合并消息", msgList); - } - } - if ((message_data.type as string) !== 'unknown' && message_data.data) { - const cqCode = encodeCQCode(message_data); - - if (messagePostFormat === 'string') { - (resMsg.message as string) += cqCode; - } else (resMsg.message as OB11MessageData[]).push(message_data); - resMsg.raw_message += cqCode; - } - - } - resMsg.raw_message = resMsg.raw_message.trim(); - return resMsg; - } - - static async PrivateEvent(core: NapCatCore, obContext: NapCatOneBot11Adapter, msg: RawMessage): Promise { - if (msg.chatType !== ChatType.friend) { - return; - } - for (const element of msg.elements) { - if (element.grayTipElement) { - if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_JSON) { - if (element.grayTipElement.jsonGrayTipElement.busiId == 1061) { - let PokeEvent = await obContext.apiContext.FriendApi.parsePrivatePokeEvent(element.grayTipElement); - if (PokeEvent) return PokeEvent; - } - } - if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) { - //好友添加成功事件 - if (element.grayTipElement.xmlElement.templId === '10229' && msg.peerUin !== '') { - return new OB11FriendAddNoticeEvent(core, parseInt(msg.peerUin)); - } - } - } - } - } - - static async GroupEvent(core: NapCatCore, obContext: NapCatOneBot11Adapter, msg: RawMessage): Promise { - const NTQQGroupApi = core.apis.GroupApi; - const NTQQUserApi = core.apis.UserApi; - const NTQQMsgApi = core.apis.MsgApi; - const logger = core.context.logger; - if (msg.chatType !== ChatType.group) { - return; - } - //log("group msg", msg); - if (msg.senderUin && msg.senderUin !== '0') { - const member = await NTQQGroupApi.getGroupMember(msg.peerUid, msg.senderUin); - if (member && member.cardName !== msg.sendMemberName) { - const newCardName = msg.sendMemberName || ''; - const event = new OB11GroupCardEvent(core, parseInt(msg.peerUid), parseInt(msg.senderUin), newCardName, member.cardName); - member.cardName = newCardName; - return event; - } - } - - for (const element of msg.elements) { - if (element.grayTipElement && element.grayTipElement.groupElement) { - const groupElement = element.grayTipElement.groupElement; - if (groupElement.type == TipGroupElementType.memberIncrease) { - let MemberIncreaseEvent = await obContext.apiContext.GroupApi.parseGroupMemberIncreaseEvent(msg.peerUid, element.grayTipElement); - if (MemberIncreaseEvent) return MemberIncreaseEvent; - } else if (groupElement.type === TipGroupElementType.ban) { - let BanEvent = await obContext.apiContext.GroupApi.parseGroupBanEvent(msg.peerUid, element.grayTipElement); - if (BanEvent) return BanEvent; - } else if (groupElement.type == TipGroupElementType.kicked) { - NTQQGroupApi.quitGroup(msg.peerUid).then(); - try { - let KickEvent = await obContext.apiContext.GroupApi.parseGroupKickEvent(msg.peerUid, element.grayTipElement); - if (KickEvent) return KickEvent; - } catch (e) { - return new OB11GroupDecreaseEvent( - core, - parseInt(msg.peerUid), - parseInt(core.selfInfo.uin), - 0, - 'leave' - ); - } - } - } else if (element.fileElement) { - return new OB11GroupUploadNoticeEvent( - core, - parseInt(msg.peerUid), parseInt(msg.senderUin || ''), - { - id: element.fileElement.fileUuid!, - name: element.fileElement.fileName, - size: parseInt(element.fileElement.fileSize), - busid: element.fileElement.fileBizId || 0, - } - ); - } - if (element.grayTipElement) { - if (element.grayTipElement.xmlElement?.templId === '10382') { - let emojiLikeEvent = await obContext.apiContext.GroupApi.parseGroupEmjioLikeEvent(msg.peerUid, element.grayTipElement); - if (emojiLikeEvent) return emojiLikeEvent; - } - if (element.grayTipElement.subElementType == NTGrayTipElementSubTypeV2.GRAYTIP_ELEMENT_SUBTYPE_XMLMSG) { - logger.logDebug('收到新人被邀请进群消息', element.grayTipElement); - const xmlElement = element.grayTipElement.xmlElement; - if (xmlElement?.content) { - const regex = /jp="(\d+)"/g; - - const matches = []; - let match = null; - - while ((match = regex.exec(xmlElement.content)) !== null) { - matches.push(match[1]); - } - // log("新人进群匹配到的QQ号", matches) - if (matches.length === 2) { - const [inviter, invitee] = matches; - return new OB11GroupIncreaseEvent( - core, - parseInt(msg.peerUid), - parseInt(invitee), - parseInt(inviter), - 'invite' - ); - } - } - } - //代码歧义 GrayTipElementSubType.MEMBER_NEW_TITLE - 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( - core, - parseInt(msg.peerUid), - parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[0].uid))!), - parseInt((await NTQQUserApi.getUinByUidV2(poke_uid[1].uid))!), - pokedetail - ); - } - } - if (element.grayTipElement.jsonGrayTipElement.busiId == 2401) { - const searchParams = new URL(json.items[0].jp).searchParams; - const msgSeq = searchParams.get('msgSeq')!; - const Group = searchParams.get('groupCode'); - // const businessId = searchParams.get('businessid'); - const Peer: Peer = { - guildId: '', - chatType: ChatType.group, - peerUid: Group!, - }; - const msgData = await NTQQMsgApi.getMsgsBySeqAndCount(Peer, msgSeq.toString(), 1, true, true); - return new OB11GroupEssenceEvent( - core, - parseInt(msg.peerUid), - MessageUnique.getShortIdByMsgId(msgData.msgList[0].msgId)!, - parseInt(msgData.msgList[0].senderUin) - ); - // 获取MsgSeq+Peer可获取具体消息 - } - if (element.grayTipElement.jsonGrayTipElement.busiId == 2407) { - //下面得改 上面也是错的grayTipElement.subElementType == GrayTipElementSubType.MEMBER_NEW_TITLE - const memberUin = json.items[1].param[0]; - const title = json.items[3].txt; - logger.logDebug('收到群成员新头衔消息', json); - return new OB11GroupTitleEvent( - core, - parseInt(msg.peerUid), - parseInt(memberUin), - title - ); - } - } - } - } - } - - static selfInfo(selfInfo: SelfInfo): OB11User { - return { - user_id: parseInt(selfInfo.uin), - nickname: selfInfo.nick, - }; - } - - static friendsV2(friends: FriendV2[]): OB11User[] { - const data: OB11User[] = []; - friends.forEach(friend => { - const sexValue = this.sex(friend.baseInfo.sex!); - data.push({ - ...friend.baseInfo, - ...friend.coreInfo, - user_id: parseInt(friend.coreInfo.uin), - nickname: friend.coreInfo.nick, - remark: friend.coreInfo.nick, - sex: sexValue, - level: 0, - }); - }); - return data; - } - - static friends(friends: Friend[]): OB11User[] { - const data: OB11User[] = []; - friends.forEach(friend => { - const sexValue = this.sex(friend.sex!); - data.push({ - user_id: parseInt(friend.uin), - nickname: friend.nick, - remark: friend.remark, - sex: sexValue, - level: 0, - }); - }); - return data; - } - - static groupMemberRole(role: number): OB11GroupMemberRole | undefined { - return { - 4: OB11GroupMemberRole.owner, - 3: OB11GroupMemberRole.admin, - 2: OB11GroupMemberRole.member, - }[role]; - } - - static sex(sex: Sex): OB11UserSex { - const sexMap = { - [Sex.male]: OB11UserSex.male, - [Sex.female]: OB11UserSex.female, - [Sex.unknown]: OB11UserSex.unknown, - }; - return sexMap[sex] || OB11UserSex.unknown; - } - - static groupMember(group_id: string, member: GroupMember): OB11GroupMember { - return { - group_id: parseInt(group_id), - user_id: parseInt(member.uin), - nickname: member.nick, - card: member.cardName, - sex: OB11Constructor.sex(member.sex!), - age: member.age ?? 0, - area: '', - level: '0', - qq_level: member.qqLevel && calcQQLevel(member.qqLevel) || 0, - join_time: 0, // 暂时没法获取 - last_sent_time: 0, // 暂时没法获取 - title_expire_time: 0, - unfriendly: false, - card_changeable: true, - is_robot: member.isRobot, - shut_up_timestamp: member.shutUpTime, - role: OB11Constructor.groupMemberRole(member.role), - title: member.memberSpecialTitle || '', - }; - } - - static stranger(user: User): OB11User { - //logDebug('construct ob11 stranger', user); - return { - ...user, - user_id: parseInt(user.uin), - nickname: user.nick, - sex: OB11Constructor.sex(user.sex!), - age: 0, - qid: user.qid, - login_days: 0, - level: user.qqLevel && calcQQLevel(user.qqLevel) || 0, - }; - } - - - static group(group: Group): OB11Group { - return { - group_id: parseInt(group.groupCode), - group_name: group.groupName, - member_count: group.memberCount, - max_member_count: group.maxMember, - }; - } - - static groups(groups: Group[]): OB11Group[] { - return groups.map(OB11Constructor.group); - } -} diff --git a/src/onebot/helper/index.ts b/src/onebot/helper/index.ts new file mode 100644 index 00000000..6847ff05 --- /dev/null +++ b/src/onebot/helper/index.ts @@ -0,0 +1,5 @@ +export * from './config'; +export * from './message'; +export * from './converter'; +export * from './quick'; +export * from './msg'; \ No newline at end of file diff --git a/src/onebot/helper/message.ts b/src/onebot/helper/message.ts new file mode 100644 index 00000000..e15d0257 --- /dev/null +++ b/src/onebot/helper/message.ts @@ -0,0 +1,341 @@ +import fastXmlParser from 'fast-xml-parser'; +import { OB11GroupIncreaseEvent } from '../event/notice/OB11GroupIncreaseEvent'; +import { OB11GroupBanEvent } from '../event/notice/OB11GroupBanEvent'; +import { sleep, UUIDConverter } from '@/common/utils/helper'; +import { OB11GroupMsgEmojiLikeEvent } from '@/onebot/event/notice/OB11MsgEmojiLikeEvent'; +import { OB11FriendPokeEvent } from '../event/notice/OB11PokeEvent'; +import { NapCatOneBot11Adapter, OB11Message, OB11MessageData, OB11MessageDataType } from '..'; +import { AtType, ChatType, FaceIndex, NapCatCore, RawMessage, VideoElement } from '@/core'; +import { EventType } from '../event/OB11BaseEvent'; +import { MessageUnique } from '@/common/utils/MessageUnique'; +import { OB11Constructor } from './converter'; +import { encodeCQCode } from './cqcode'; + + + export async function RawNTMsg2Onebot( + core: NapCatCore, + obcore: NapCatOneBot11Adapter, + msg: RawMessage, + messagePostFormat: string = obcore.configLoader.configData.messagePostFormat +): Promise { + if (msg.senderUin == "0" || msg.senderUin == "") return; + if (msg.peerUin == "0" || msg.peerUin == "") return; + //跳过空消息 + const NTQQGroupApi = core.apis.GroupApi; + const NTQQUserApi = core.apis.UserApi; + const NTQQFileApi = core.apis.FileApi; + const NTQQMsgApi = core.apis.MsgApi; + const logger = core.context.logger; + const resMsg: OB11Message = { + self_id: parseInt(core.selfInfo.uin), + user_id: parseInt(msg.senderUin!), + time: parseInt(msg.msgTime) || Date.now(), + message_id: msg.id!, + message_seq: msg.id!, + real_id: msg.id!, + message_type: msg.chatType == ChatType.group ? 'group' : 'private', + sender: { + user_id: parseInt(msg.senderUin || '0'), + nickname: msg.sendNickName, + card: msg.sendMemberName || '', + }, + raw_message: '', + font: 14, + sub_type: 'friend', + message: messagePostFormat === 'string' ? '' : [], + message_format: messagePostFormat === 'string' ? 'string' : 'array', + post_type: core.selfInfo.uin == msg.senderUin ? EventType.MESSAGE_SENT : EventType.MESSAGE, + }; + if (msg.chatType == ChatType.group) { + resMsg.sub_type = 'normal'; // 这里go-cqhttp是group,而onebot11标准是normal, 蛋疼 + resMsg.group_id = parseInt(msg.peerUin); + //直接去QQNative取 + let member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin); + if (!member) member = await NTQQGroupApi.getGroupMember(msg.peerUin, msg.senderUin); + if (member) { + resMsg.sender.role = OB11Constructor.groupMemberRole(member.role); + resMsg.sender.nickname = member.nick; + } + } else if (msg.chatType == ChatType.friend) { + resMsg.sub_type = 'friend'; + resMsg.sender.nickname = (await NTQQUserApi.getUserDetailInfo(msg.senderUid)).nick; + //const user = await NTQQUserApi.getUserDetailInfoByUin(msg.senderUin!); + //resMsg.sender.nickname = user.info.nick; + } else if (msg.chatType == ChatType.temp) { + resMsg.sub_type = 'group'; + const ret = await NTQQMsgApi.getTempChatInfo(ChatType.temp, msg.senderUid); + if (ret.result === 0) { + resMsg.group_id = parseInt(ret.tmpChatInfo!.groupCode); + resMsg.sender.nickname = ret.tmpChatInfo!.fromNick; + } else { + resMsg.group_id = 284840486; //兜底数据 + resMsg.sender.nickname = "临时会话"; + } + } + for (const element of msg.elements) { + let message_data: OB11MessageData = { + data: {} as any, + type: 'unknown' as any, + }; + if (element.textElement && element.textElement?.atType !== AtType.notAt) { + let qq: `${number}` | 'all'; + let name: string | undefined; + if (element.textElement.atType == AtType.atAll) { + qq = 'all'; + } else { + const { atNtUid, content } = element.textElement; + let atQQ = element.textElement.atUid; + if (!atQQ || atQQ === '0') { + atQQ = await NTQQUserApi.getUinByUidV2(atNtUid); + } + if (atQQ) { + qq = atQQ as `${number}`; + name = content.replace('@', ''); + } + } + message_data = { + type: OB11MessageDataType.at, + data: { + qq: qq!, + name, + }, + }; + } else if (element.textElement) { + message_data['type'] = OB11MessageDataType.text; + + let text = element.textElement.content; + if (!text.trim()) { + continue; + } + // 兼容 9.7.x 换行符 + if (text.indexOf('\n') === -1 && text.indexOf('\r\n') === -1) { + text = text.replace(/\r/g, '\n'); + } + message_data['data']['text'] = text; + } else if (element.replyElement) { + message_data['type'] = OB11MessageDataType.reply; + //log("收到回复消息", element.replyElement); + try { + const records = msg.records.find(msgRecord => msgRecord.msgId === element?.replyElement?.sourceMsgIdInRecords); + const peer = { + chatType: msg.chatType, + peerUid: msg.peerUid, + guildId: '', + }; + let replyMsg: RawMessage | undefined; + if (!records) throw new Error('找不到回复消息'); + replyMsg = (await NTQQMsgApi.getMsgsBySeqAndCount({ + peerUid: msg.peerUid, + guildId: '', + chatType: msg.chatType, + }, element.replyElement.replayMsgSeq, 1, true, true)).msgList.find(msg => msg.msgRandom === records.msgRandom); + if (!replyMsg || records.msgRandom !== replyMsg.msgRandom) { + replyMsg = (await NTQQMsgApi.getSingleMsg(peer, element.replyElement.replayMsgSeq)).msgList[0]; + } + if (msg.peerUin == '284840486') { + //合并消息内侧 消息具体定位不到 + } + if ((!replyMsg || records.msgRandom !== replyMsg.msgRandom) && msg.peerUin !== '284840486') { + const replyMsgList = (await NTQQMsgApi.getMsgExBySeq(peer, records.msgSeq)).msgList; + if (replyMsgList.length < 1) { + throw new Error('回复消息消息验证失败'); + } + replyMsg = replyMsgList.filter(e => e.msgSeq == records.msgSeq).sort((a, b) => parseInt(a.msgTime) - parseInt(b.msgTime))[0]; + } + message_data['data']['id'] = MessageUnique.createMsg({ + peerUid: msg.peerUid, + guildId: '', + chatType: msg.chatType, + }, replyMsg.msgId)?.toString(); + //log("找到回复消息", message_data['data']['id'], replyMsg.msgList[0].msgId) + } catch (e: any) { + message_data['type'] = 'unknown' as any; + message_data['data'] = undefined; + logger.logError('获取不到引用的消息', e.stack, element.replyElement.replayMsgSeq); + } + + } else if (element.picElement) { + message_data['type'] = OB11MessageDataType.image; + // message_data["data"]["file"] = element.picElement.sourcePath + message_data['data']['file'] = element.picElement.fileName; + message_data['data']['subType'] = element.picElement.picSubType; + message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId); + // message_data["data"]["path"] = element.picElement.sourcePath + try { + message_data['data']['url'] = await NTQQFileApi.getImageUrl(element.picElement); + } catch (e: any) { + logger.logError('获取图片url失败', e.stack); + } + //console.log(message_data['data']['url']) + // message_data["data"]["file_id"] = element.picElement.fileUuid + message_data['data']['file_size'] = element.picElement.fileSize; + } else if (element.fileElement) { + const FileElement = element.fileElement; + message_data['type'] = OB11MessageDataType.file; + message_data['data']['file'] = FileElement.fileName; + message_data['data']['path'] = FileElement.filePath; + message_data['data']['url'] = FileElement.filePath; + message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId); + message_data['data']['file_size'] = FileElement.fileSize; + await NTQQFileApi.addFileCache( + { + peerUid: msg.peerUid, + chatType: msg.chatType, + guildId: '', + }, + msg.msgId, + msg.msgSeq, + msg.senderUid, + element.elementId, + element.elementType.toString(), + FileElement.fileSize, + FileElement.fileName + ); + } else if (element.videoElement) { + const videoElement: VideoElement = element.videoElement; + //读取视频链接并兜底 + let videoUrl; //Array + if (msg.peerUin === '284840486') { + //合并消息内部 应该进行特殊处理 可能需要重写peer 待测试与研究 Mlikiowa Taged TODO + } + try { + + videoUrl = await NTQQFileApi.getVideoUrl({ + chatType: msg.chatType, + peerUid: msg.peerUid, + guildId: '0', + }, msg.msgId, element.elementId); + } catch (error) { + videoUrl = undefined; + } + //读取在线URL + let videoDownUrl = undefined; + + if (videoUrl) { + const videoDownUrlTemp = videoUrl.find((url) => { + return !!url.url; + }); + if (videoDownUrlTemp) { + videoDownUrl = videoDownUrlTemp.url; + } + } + //开始兜底 + if (!videoDownUrl) { + videoDownUrl = videoElement.filePath; + } + message_data['type'] = OB11MessageDataType.video; + message_data['data']['file'] = videoElement.fileName; + message_data['data']['path'] = videoDownUrl; + message_data['data']['url'] = videoDownUrl; + message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId); + message_data['data']['file_size'] = videoElement.fileSize; + + await NTQQFileApi.addFileCache( + { + peerUid: msg.peerUid, + chatType: msg.chatType, + guildId: '', + }, + msg.msgId, + msg.msgSeq, + msg.senderUid, + element.elementId, + element.elementType.toString(), + videoElement.fileSize || '0', + videoElement.fileName + ); + } else if (element.pttElement) { + message_data['type'] = OB11MessageDataType.voice; + message_data['data']['file'] = element.pttElement.fileName; + message_data['data']['path'] = element.pttElement.filePath; + //message_data['data']['file_id'] = element.pttElement.fileUuid; + message_data['data']['file_id'] = UUIDConverter.encode(msg.peerUin, msg.msgId); + message_data['data']['file_size'] = element.pttElement.fileSize; + await NTQQFileApi.addFileCache({ + peerUid: msg.peerUid, + chatType: msg.chatType, + guildId: '', + }, + msg.msgId, + msg.msgSeq, + msg.senderUid, + element.elementId, + element.elementType.toString(), + element.pttElement.fileSize || '0', + element.pttElement.fileUuid || '' + ); + //以uuid作为文件名 + } else if (element.arkElement) { + message_data['type'] = OB11MessageDataType.json; + message_data['data']['data'] = element.arkElement.bytesData; + } else if (element.faceElement) { + const faceId = element.faceElement.faceIndex; + if (faceId === FaceIndex.dice) { + message_data['type'] = OB11MessageDataType.dice; + message_data['data']['result'] = element.faceElement.resultId; + } else if (faceId === FaceIndex.RPS) { + message_data['type'] = OB11MessageDataType.RPS; + message_data['data']['result'] = element.faceElement.resultId; + } else { + message_data['type'] = OB11MessageDataType.face; + message_data['data']['id'] = element.faceElement.faceIndex.toString(); + } + } else if (element.marketFaceElement) { + message_data['type'] = OB11MessageDataType.mface; + message_data['data']['summary'] = element.marketFaceElement.faceName; + const md5 = element.marketFaceElement.emojiId; + // 取md5的前两位 + const dir = md5.substring(0, 2); + // 获取组装url + // const url = `https://p.qpic.cn/CDN_STATIC/0/data/imgcache/htdocs/club/item/parcel/item/${dir}/${md5}/300x300.gif?max_age=31536000`; + message_data['data']['url'] = `https://gxh.vip.qq.com/club/item/parcel/item/${dir}/${md5}/raw300.gif`; + message_data['data']['emoji_id'] = element.marketFaceElement.emojiId; + message_data['data']['emoji_package_id'] = String(element.marketFaceElement.emojiPackageId); + message_data['data']['key'] = element.marketFaceElement.key; + //mFaceCache.set(md5, element.marketFaceElement.faceName); + } else if (element.markdownElement) { + message_data['type'] = OB11MessageDataType.markdown; + message_data['data']['data'] = element.markdownElement.content; + } else if (element.multiForwardMsgElement) { + message_data['type'] = OB11MessageDataType.forward; + message_data['data']['id'] = msg.msgId; + const ParentMsgPeer = msg.parentMsgPeer ?? { + chatType: msg.chatType, + guildId: '', + peerUid: msg.peerUid, + }; + //判断是否在合并消息内 + msg.parentMsgIdList = msg.parentMsgIdList ?? []; + //首次列表不存在则开始创建 + msg.parentMsgIdList.push(msg.msgId); + //let parentMsgId = msg.parentMsgIdList[msg.parentMsgIdList.length - 2 < 0 ? 0 : msg.parentMsgIdList.length - 2]; + //加入自身MsgId + const MultiMsgs = (await NTQQMsgApi.getMultiMsg(ParentMsgPeer, msg.parentMsgIdList[0], msg.msgId))?.msgList; + //拉取下级消息 + if (!MultiMsgs) continue; + //拉取失败则跳过 + message_data['data']['content'] = []; + for (const MultiMsg of MultiMsgs) { + //对每条拉取的消息传递ParentMsgPeer修正Peer + MultiMsg.parentMsgPeer = ParentMsgPeer; + MultiMsg.parentMsgIdList = msg.parentMsgIdList; + MultiMsg.id = MessageUnique.createMsg(ParentMsgPeer, MultiMsg.msgId); //该ID仅用查看 无法调用 + const msgList = await RawNTMsg2Onebot(core, obcore, MultiMsg, messagePostFormat); + if (!msgList) continue; + message_data['data']['content'].push(msgList); + //console.log("合并消息", msgList); + } + } + if ((message_data.type as string) !== 'unknown' && message_data.data) { + const cqCode = encodeCQCode(message_data); + + if (messagePostFormat === 'string') { + (resMsg.message as string) += cqCode; + } else (resMsg.message as OB11MessageData[]).push(message_data); + resMsg.raw_message += cqCode; + } + + } + resMsg.raw_message = resMsg.raw_message.trim(); + return resMsg; +} diff --git a/src/onebot/index.ts b/src/onebot/index.ts index c99c41bc..bb48d169 100644 --- a/src/onebot/index.ts +++ b/src/onebot/index.ts @@ -26,7 +26,7 @@ import { ActionMap, createActionMap } from '@/onebot/action'; import { WebUiDataRuntime } from '@/webui/src/helper/Data'; import { OB11InputStatusEvent } from '@/onebot/event/notice/OB11InputStatusEvent'; import { MessageUnique } from '@/common/utils/MessageUnique'; -import { OB11Constructor } from '@/onebot/helper/data'; +import { OB11Constructor } from './helper/converter'; import { proxiedListenerOf } from '@/common/utils/proxy-handler'; import { OB11FriendRequestEvent } from '@/onebot/event/request/OB11FriendRequest'; import { OB11GroupAdminNoticeEvent } from '@/onebot/event/notice/OB11GroupAdminNoticeEvent'; @@ -35,6 +35,7 @@ import { OB11GroupRequestEvent } from '@/onebot/event/request/OB11GroupRequest'; import { OB11FriendRecallNoticeEvent } from '@/onebot/event/notice/OB11FriendRecallNoticeEvent'; import { OB11GroupRecallNoticeEvent } from '@/onebot/event/notice/OB11GroupRecallNoticeEvent'; import { LRUCache } from '@/common/utils/LRU'; +import { RawNTMsg2Onebot } from './helper'; //OneBot实现类 export class NapCatOneBot11Adapter { @@ -255,7 +256,7 @@ export class NapCatOneBot11Adapter { if (msg.sendStatus == SendStatusType.KSEND_STATUS_SUCCESS && !msgIdSend.get(msg.msgId)) { msgIdSend.put(msg.msgId, true); // 完成后再post - OB11Constructor.message(this.core, this, msg) + RawNTMsg2Onebot(this.core, this, msg) .then((ob11Msg) => { if (!ob11Msg) return; ob11Msg.target_id = parseInt(msg.peerUin); @@ -445,7 +446,7 @@ export class NapCatOneBot11Adapter { private async emitMsg(message: RawMessage) { const { debug, reportSelfMessage, messagePostFormat } = this.configLoader.configData; this.context.logger.logDebug('收到新消息 RawMessage', message); - OB11Constructor.message(this.core, this, message, messagePostFormat).then((ob11Msg) => { + RawNTMsg2Onebot(this.core, this, message, messagePostFormat).then((ob11Msg) => { if (!ob11Msg) return; this.context.logger.logDebug('转化为 OB11Message', ob11Msg); if (debug) {