From af6a1404985554038a63a9b07d856f2efbe4a139 Mon Sep 17 00:00:00 2001 From: Il Harper Date: Mon, 15 Jul 2024 03:21:03 +0800 Subject: [PATCH] feat(event): support receive `chronocat:poke` --- .../src/parser/index.ts | 41 ++++- .../fixtures/f-5-12-3-8-17-1061/data.json | 145 ++++++++++++++++++ .../fixtures/f-5-12-3-8-17-1061/index.test.ts | 19 +++ packages/red/src/redEntity.ts | 58 +++++++ 4 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 packages/engine-chronocat-event/tests/parser/fixtures/f-5-12-3-8-17-1061/data.json create mode 100644 packages/engine-chronocat-event/tests/parser/fixtures/f-5-12-3-8-17-1061/index.test.ts diff --git a/packages/engine-chronocat-event/src/parser/index.ts b/packages/engine-chronocat-event/src/parser/index.ts index 8e4583b..a61c4ed 100644 --- a/packages/engine-chronocat-event/src/parser/index.ts +++ b/packages/engine-chronocat-event/src/parser/index.ts @@ -1,4 +1,10 @@ -import type { MarketFaceAssetRequest, Media, RedMessage } from '@chronocat/red' +import type { + JsonGrayTipBusi1061, + JsonGrayTipBusi1061ItemQq, + MarketFaceAssetRequest, + Media, + RedMessage, +} from '@chronocat/red' import { AtType, ChatType, FaceType, MsgType, SendType } from '@chronocat/red' import type { Channel, @@ -190,6 +196,15 @@ export const parseMessage = async ( // 群主禁止群内临时通话 // 群主禁止群内发起新的群聊 return undefined + else if ( + ntMsgTypes.msgType === MsgType.System && // 5 + message.subMsgType === 12 && // 12 + ntMsgTypes.sendType === SendType.System && // 3 + message.elements[0]!.elementType === 8 && // 8 + message.elements[0]!.grayTipElement!.subElementType === 17 && // 17 + message.elements[0]!.grayTipElement!.jsonGrayTipElement!.busiId === '1061' // 1061 + ) + return await parsePokeMessage(ctx, config, event, message) return undefined } @@ -355,6 +370,30 @@ async function parseGuildMemberAddedLegacyInviteMessage( return [event2, ...extraEvents] } +async function parsePokeMessage( + ctx: ChronocatContext, + _config: O.Intersect, + event: Event, + message: RedMessage, +) { + const json = JSON.parse( + message.elements[0]!.grayTipElement!.jsonGrayTipElement!.jsonStr, + ) as JsonGrayTipBusi1061 + + const [operator, user] = json.items.filter((x) => x.type == 'qq') as [ + JsonGrayTipBusi1061ItemQq, + JsonGrayTipBusi1061ItemQq, + ] + + event.type = 'message-created' + event.message = { + id: message.msgId, + content: `<${ctx.chronocat.platform}:poke user-id="${ctx.chronocat.uix.getUin(user.uid)}" operator-id="${ctx.chronocat.uix.getUin(operator.uid)}"/>`, + } + + return [event] +} + /** * 解析消息元素。 */ diff --git a/packages/engine-chronocat-event/tests/parser/fixtures/f-5-12-3-8-17-1061/data.json b/packages/engine-chronocat-event/tests/parser/fixtures/f-5-12-3-8-17-1061/data.json new file mode 100644 index 0000000..b85b863 --- /dev/null +++ b/packages/engine-chronocat-event/tests/parser/fixtures/f-5-12-3-8-17-1061/data.json @@ -0,0 +1,145 @@ +{ + "msgId": "1000000000000000000", + "msgRandom": "1000000000000000000", + "msgSeq": "100000", + "cntSeq": "0", + "chatType": 2, + "msgType": 5, + "subMsgType": 12, + "sendType": 3, + "senderUid": "", + "peerUid": "100000000", + "channelId": "", + "guildId": "", + "guildCode": "0", + "fromUid": "0", + "fromAppid": "0", + "msgTime": "1000000000", + "msgMeta": "0x", + "sendStatus": 2, + "sendRemarkName": "", + "sendMemberName": "", + "sendNickName": "", + "guildName": "", + "channelName": "", + "elements": [ + { + "elementType": 8, + "elementId": "1000000000000000000", + "extBufForUI": "0x", + "textElement": null, + "faceElement": null, + "marketFaceElement": null, + "replyElement": null, + "picElement": null, + "pttElement": null, + "videoElement": null, + "grayTipElement": { + "subElementType": 17, + "revokeElement": null, + "proclamationElement": null, + "emojiReplyElement": null, + "groupElement": null, + "buddyElement": null, + "feedMsgElement": null, + "essenceElement": null, + "groupNotifyElement": null, + "buddyNotifyElement": null, + "xmlElement": null, + "fileReceiptElement": null, + "localGrayTipElement": null, + "blockGrayTipElement": null, + "aioOpGrayTipElement": null, + "jsonGrayTipElement": { + "busiId": "1061", + "jsonStr": "{\"align\":\"center\",\"items\":[{\"col\":\"1\",\"nm\":\"\",\"type\":\"qq\",\"uid\":\"u_0000000000000000000000\"},{\"jp\":\"https://zb.vip.qq.com/v2/pages/nudgeMall?_wv=2&actionId=0\",\"src\":\"http://tianquan.gtimg.cn/nudgeaction/item/0/expression.jpg\",\"type\":\"img\"},{\"txt\":\"戳了戳\",\"type\":\"nor\"},{\"col\":\"1\",\"nm\":\"\",\"tp\":\"0\",\"type\":\"qq\",\"uid\":\"u_0000000000000000000001\"},{\"txt\":\"\",\"type\":\"nor\"}]}\n", + "recentAbstract": "", + "isServer": true, + "xmlToJsonParam": { + "busiType": "12", + "busiId": "1061", + "c2cType": 0, + "serviceType": 0, + "ctrlFlag": 7, + "content": " ", + "templId": "1132", + "seqId": "1000000000", + "templParam": {}, + "pbReserv": null, + "members": {} + } + }, + "walletGrayTipElement": null + }, + "arkElement": null, + "fileElement": null, + "liveGiftElement": null, + "markdownElement": null, + "structLongMsgElement": null, + "multiForwardMsgElement": null, + "giphyElement": null, + "walletElement": null, + "inlineKeyboardElement": null, + "textGiftElement": null, + "calendarElement": null, + "yoloGameResultElement": null, + "avRecordElement": null, + "structMsgElement": null, + "faceBubbleElement": null, + "shareLocationElement": null, + "tofuRecordElement": null, + "taskTopMsgElement": null, + "recommendedMsgElement": null, + "actionBarElement": null + } + ], + "records": [], + "emojiLikesList": [], + "commentCnt": "0", + "directMsgFlag": 0, + "directMsgMembers": [], + "peerName": "", + "freqLimitInfo": null, + "editable": false, + "avatarMeta": "", + "avatarPendant": "", + "feedId": "", + "roleId": "0", + "timeStamp": "0", + "clientIdentityInfo": null, + "isImportMsg": false, + "atType": 0, + "roleType": 0, + "fromChannelRoleInfo": { + "roleId": "0", + "name": "", + "color": 0 + }, + "fromGuildRoleInfo": { + "roleId": "0", + "name": "", + "color": 0 + }, + "levelRoleInfo": { + "roleId": "0", + "name": "", + "color": 0 + }, + "recallTime": "0", + "isOnlineMsg": false, + "generalFlags": "0x", + "clientSeq": "0", + "fileGroupSize": null, + "foldingInfo": null, + "multiTransInfo": null, + "senderUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED", + "peerUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED", + "msgAttrs": {}, + "anonymousExtInfo": null, + "nameType": 0, + "avatarFlag": 0, + "extInfoForUI": null, + "personalMedal": null, + "categoryManage": 0, + "msgEventInfo": null +} diff --git a/packages/engine-chronocat-event/tests/parser/fixtures/f-5-12-3-8-17-1061/index.test.ts b/packages/engine-chronocat-event/tests/parser/fixtures/f-5-12-3-8-17-1061/index.test.ts new file mode 100644 index 0000000..ae08358 --- /dev/null +++ b/packages/engine-chronocat-event/tests/parser/fixtures/f-5-12-3-8-17-1061/index.test.ts @@ -0,0 +1,19 @@ +import type { RedMessage } from '@chronocat/red' +import { readFile } from 'node:fs/promises' +import { join } from 'node:path' +import { buildParser } from '../../../../src/parser' +import { ctx, satoriConfig } from '../../../mocks' + +test('Red 解析器应当正确解析 戳一戳消息', async () => { + const message = JSON.parse( + (await readFile(join(__dirname, 'data.json'))).toString('utf-8'), + ) as RedMessage + + const events = await buildParser(ctx, satoriConfig)(message) + + expect(events).toBeTruthy() // 解析到的消息应该存在 + expect(events).toHaveLength(1) // 应当解析到 1 条消息 + + const [event] = events! + expect(event).toMatchSnapshot() +}) diff --git a/packages/red/src/redEntity.ts b/packages/red/src/redEntity.ts index 943e5b9..6b9ee41 100644 --- a/packages/red/src/redEntity.ts +++ b/packages/red/src/redEntity.ts @@ -254,6 +254,17 @@ export interface RedMessage { clientSeq: string nameType: number avatarFlag: number + freqLimitInfo: unknown + clientIdentityInfo: unknown + fileGroupSize: unknown + foldingInfo: unknown + multiTransInfo: unknown + msgAttrs: Record + anonymousExtInfo: unknown + extInfoForUI: unknown + personalMedal: unknown + categoryManage: number + msgEventInfo: unknown senderUin: string peerUin: string @@ -289,6 +300,8 @@ export interface Element { shareLocationElement?: unknown tofuRecordElement?: unknown taskTopMsgElement?: unknown + recommendedMsgElement?: unknown + actionBarElement?: unknown } export interface PicElement { @@ -476,12 +489,57 @@ export interface GrayTipElement { blockGrayTipElement?: unknown aioOpGrayTipElement?: unknown jsonGrayTipElement?: JsonGrayTipElement + walletGrayTipElement?: unknown } export interface JsonGrayTipElement { busiId: string jsonStr: string + recentAbstract: string isServer: boolean + xmlToJsonParam: JsonGrayTipElementXmlToJsonParam +} + +export interface JsonGrayTipElementXmlToJsonParam { + busiType: string + busiId: string + c2cType: number + serviceType: number + ctrlFlag: number + content: string + templId: string + seqId: string + templParam: Record + pbReserv: unknown + members: Record +} + +export interface JsonGrayTipBusi1061 { + align: 'center' + items: JsonGrayTipBusi1061Item[] +} + +export type JsonGrayTipBusi1061Item = + | JsonGrayTipBusi1061ItemNormal + | JsonGrayTipBusi1061ItemQq + | JsonGrayTipBusi1061ItemImage + +export interface JsonGrayTipBusi1061ItemNormal { + type: 'nor' + txt: string +} + +export interface JsonGrayTipBusi1061ItemQq { + type: 'qq' + col: string // '1' + nm: string // '' + uid: string // 'u_0000000000000000000000' +} + +export interface JsonGrayTipBusi1061ItemImage { + type: 'img' + jp: string // 'https://zb.vip.qq.com/v2/pages/nudgeMall?_wv=2&actionId=0' + src: string // 'http://tianquan.gtimg.cn/nudgeaction/item/0/expression.jpg' } export interface PttElement {