test(events): init tests

This commit is contained in:
Il Harper 2024-03-13 22:20:55 +08:00
parent 105ee6f0f9
commit 495ede6b53
No known key found for this signature in database
GPG Key ID: 4B71FCA698E7E8EC
30 changed files with 1630 additions and 0 deletions

34
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,34 @@
name: test
on:
push:
pull_request:
workflow_dispatch:
jobs:
test:
name: test
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
submodules: 'false'
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '20.3.1'
- name: Test
run: |
yarn
yarn build
yarn test
shell: bash
- name: Upload Coverage Reports to Codecov
uses: codecov/codecov-action@v3

View File

@ -38,6 +38,7 @@
"jest": "^29.6.3",
"prettier": "^3.0.2",
"ts-jest": "^29.1.1",
"ts-node": "^10.9.1",
"typescript": "~5.1.0"
}
}

View File

@ -0,0 +1,24 @@
import type { ChronocatContext } from '@chronocat/shell'
import h from '@satorijs/element'
export const ctx: ChronocatContext = {
//@ts-expect-error aaa
chronocat: {
l: {
info: jest.fn(),
warn: jest.fn(),
error: jest.fn(),
debug: jest.fn(),
} as unknown as ChronocatContext['chronocat']['l'],
h,
},
}
export const satoriConfig = {
type: 'satori',
listen: '0.0.0.0',
port: 5500,
self_url: 'https://chronocat.vercel.app',
token: 'DEFINE_CHRONO_TOKEN',
enable: true,
} as const

View File

@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Red 解析器应当正确解析 群聊 纯文本消息 1`] = `
{
"channel": {
"id": "100000000",
"name": "001",
"type": 0,
},
"guild": {
"avatar": "https://p.qlogo.cn/gh/100000000/100000000/640",
"id": "100000000",
"name": "001",
},
"id": undefined,
"member": {
"nick": "000",
},
"message": {
"content": "xxx",
"id": "1000000000000000000",
},
"platform": undefined,
"self_id": undefined,
"timestamp": 1000000000000,
"type": "message-created",
"user": {
"avatar": "http://thirdqq.qlogo.cn/headimg_dl?dst_uin=10000&spec=640",
"id": "10000",
"name": undefined,
},
}
`;

View File

@ -0,0 +1,109 @@
{
"msgId": "1000000000000000000",
"msgRandom": "100000000",
"msgSeq": "100000",
"cntSeq": "0",
"chatType": 2,
"msgType": 2,
"subMsgType": 1,
"sendType": 0,
"senderUid": "u_0000000000000000000000",
"peerUid": "100000000",
"channelId": "",
"guildId": "",
"guildCode": "0",
"fromUid": "0",
"fromAppid": "0",
"msgTime": "1000000000",
"msgMeta": "0x",
"sendStatus": 2,
"sendMemberName": "000",
"sendNickName": "",
"guildName": "",
"channelName": "",
"elements": [
{
"elementType": 1,
"elementId": "1000000000000000000",
"extBufForUI": "0x",
"textElement": {
"content": "xxx",
"atType": 0,
"atUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"atTinyId": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"atNtUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"subElementType": 0,
"atChannelId": "0",
"atRoleId": "0",
"atRoleColor": 0,
"atRoleName": "",
"needNotify": 0,
"atNtUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED"
},
"faceElement": null,
"marketFaceElement": null,
"replyElement": null,
"picElement": null,
"pttElement": null,
"videoElement": null,
"grayTipElement": 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
}
],
"records": [],
"emojiLikesList": [],
"commentCnt": "0",
"directMsgFlag": 0,
"directMsgMembers": [],
"peerName": "001",
"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": true,
"generalFlags": "0x",
"clientSeq": "0",
"fileGroupSize": null,
"foldingInfo": null,
"nameType": 0,
"avatarFlag": 0,
"anonymousExtInfo": null,
"personalMedal": null,
"roleManagementTag": null,
"senderUin": "10000"
}

View File

@ -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()
})

View File

@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Red 解析器应当正确解析 群聊 文件消息 1`] = `
{
"channel": {
"id": "100000000",
"name": "001",
"type": 0,
},
"guild": {
"avatar": "https://p.qlogo.cn/gh/100000000/100000000/640",
"id": "100000000",
"name": "001",
},
"id": undefined,
"member": {},
"message": {
"content": "<file src="https://chronocat.vercel.app/v1/assets/eyJtc2dJZCI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJjaGF0VHlwZSI6MiwicGVlclVpZCI6IjEwMDAwMDAwMCIsImVsZW1lbnRJZCI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJ0aHVtYlNpemUiOjB9"/>",
"id": "1000000000000000000",
},
"platform": undefined,
"self_id": undefined,
"timestamp": 1000000000000,
"type": "message-created",
"user": {
"avatar": "http://thirdqq.qlogo.cn/headimg_dl?dst_uin=10000&spec=640",
"id": "10000",
"name": "000",
},
}
`;

View File

@ -0,0 +1,120 @@
{
"msgId": "1000000000000000000",
"msgRandom": "1000000000",
"msgSeq": "100000",
"cntSeq": "0",
"chatType": 2,
"msgType": 3,
"subMsgType": 512,
"sendType": 0,
"senderUid": "u_0000000000000000000000",
"peerUid": "100000000",
"channelId": "",
"guildId": "",
"guildCode": "0",
"fromUid": "0",
"fromAppid": "0",
"msgTime": "1000000000",
"msgMeta": "0x",
"sendStatus": 2,
"sendMemberName": "",
"sendNickName": "000",
"guildName": "",
"channelName": "",
"elements": [
{
"elementType": 3,
"elementId": "1000000000000000000",
"extBufForUI": "0x",
"textElement": null,
"faceElement": null,
"marketFaceElement": null,
"replyElement": null,
"picElement": null,
"pttElement": null,
"videoElement": null,
"grayTipElement": null,
"arkElement": null,
"fileElement": {
"fileMd5": "00000000000000000000000000000000",
"fileName": "file.zip",
"filePath": "",
"fileSize": "100000",
"picHeight": 0,
"picWidth": 0,
"picThumbPath": {},
"expireTime": "0",
"file10MMd5": "",
"fileSha": "",
"fileSha3": "",
"videoDuration": 0,
"transferStatus": 1,
"progress": 0,
"invalidState": 0,
"fileUuid": "/00000000-0000-0000-0000-000000000000",
"fileSubId": "",
"thumbFileSize": 0,
"fileBizId": null,
"thumbMd5": null,
"folderId": null,
"fileGroupIndex": 0,
"fileTransType": null
},
"liveGiftElement": null,
"markdownElement": null,
"structLongMsgElement": null,
"multiForwardMsgElement": null,
"giphyElement": null,
"walletElement": null,
"inlineKeyboardElement": null,
"textGiftElement": null,
"calendarElement": null,
"yoloGameResultElement": null,
"avRecordElement": null
}
],
"records": [],
"emojiLikesList": [],
"commentCnt": "0",
"directMsgFlag": 0,
"directMsgMembers": [],
"peerName": "001",
"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": true,
"generalFlags": "0x",
"clientSeq": "0",
"fileGroupSize": null,
"foldingInfo": null,
"nameType": 0,
"avatarFlag": 0,
"anonymousExtInfo": null,
"personalMedal": null,
"roleManagementTag": null,
"senderUin": "10000"
}

View File

@ -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()
})

View File

@ -0,0 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Red 解析器应当正确解析 群聊 旧版群成员邀请新人入群消息 1`] = `
{
"channel": {
"id": "100000000",
"name": "",
"type": 0,
},
"guild": {
"avatar": "https://p.qlogo.cn/gh/100000000/100000000/640",
"id": "100000000",
"name": "",
},
"id": undefined,
"member": {},
"message": {
"content": "",
"id": "1000000000000000000",
},
"operator": {
"id": "10001",
"name": undefined,
},
"platform": undefined,
"self_id": undefined,
"timestamp": 1000000000000,
"type": "guild-member-added",
"user": {
"avatar": "http://thirdqq.qlogo.cn/headimg_dl?dst_uin=10000&spec=640",
"id": "10000",
"name": undefined,
},
}
`;

View File

@ -0,0 +1,125 @@
{
"msgId": "1000000000000000000",
"msgRandom": "1000000000000000000",
"msgSeq": "100000",
"cntSeq": "0",
"chatType": 2,
"msgType": 5,
"subMsgType": 12,
"sendType": 3,
"senderUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"peerUid": "100000000",
"channelId": "",
"guildId": "",
"guildCode": "0",
"fromUid": "0",
"fromAppid": "0",
"msgTime": "1000000000",
"msgMeta": "0x",
"sendStatus": 2,
"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": 12,
"revokeElement": null,
"proclamationElement": null,
"emojiReplyElement": null,
"groupElement": null,
"buddyElement": null,
"feedMsgElement": null,
"essenceElement": null,
"groupNotifyElement": null,
"buddyNotifyElement": null,
"xmlElement": {
"busiType": "1",
"busiId": "10145",
"c2cType": 0,
"serviceType": 0,
"ctrlFlag": 7,
"content": "<gtip align=\"center\"><qq uin=\"u_0000000000000000000001\" col=\"3\" jp=\"10001\" /><nor txt=\"邀请\"/><qq uin=\"u_0000000000000000000000\" col=\"3\" jp=\"10000\" /> <nor txt=\"加入了群聊。\"/> </gtip>",
"templId": "10179",
"seqId": "1000000000",
"templParam": {},
"pbReserv": "0x",
"members": {}
},
"fileReceiptElement": null,
"localGrayTipElement": null,
"blockGrayTipElement": null,
"aioOpGrayTipElement": null,
"jsonGrayTipElement": 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
}
],
"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,
"nameType": 0,
"avatarFlag": 0,
"anonymousExtInfo": null,
"personalMedal": null,
"roleManagementTag": null,
"senderUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED"
}

View File

@ -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()
})

View File

@ -0,0 +1,35 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Red 解析器应当正确解析 群聊 新人自行入群消息 1`] = `
{
"channel": {
"id": "100000000",
"name": "",
"type": 0,
},
"guild": {
"avatar": "https://p.qlogo.cn/gh/100000000/100000000/640",
"id": "100000000",
"name": "",
},
"id": undefined,
"member": {},
"message": {
"content": "",
"id": "1000000000000000000",
},
"operator": {
"id": "10001",
"name": undefined,
},
"platform": undefined,
"self_id": undefined,
"timestamp": 1000000000000,
"type": "guild-member-added",
"user": {
"avatar": "http://thirdqq.qlogo.cn/headimg_dl?dst_uin=10000&spec=640",
"id": "10000",
"name": "000",
},
}
`;

View File

@ -0,0 +1,141 @@
{
"msgId": "1000000000000000000",
"msgRandom": "1000000000000000000",
"msgSeq": "100000",
"cntSeq": "0",
"chatType": 2,
"msgType": 5,
"subMsgType": 8,
"sendType": 3,
"senderUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"peerUid": "100000000",
"channelId": "",
"guildId": "",
"guildCode": "0",
"fromUid": "0",
"fromAppid": "0",
"msgTime": "1000000000",
"msgMeta": "0x",
"sendStatus": 2,
"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": 4,
"revokeElement": null,
"proclamationElement": null,
"emojiReplyElement": null,
"groupElement": {
"type": 1,
"role": 0,
"groupName": "",
"memberUid": "u_0000000000000000000000",
"memberNick": "000",
"memberRemark": "",
"adminUid": "u_0000000000000000000001",
"adminNick": "001",
"adminRemark": "001",
"createGroup": null,
"memberAdd": {
"showType": 0,
"otherAdd": {
"uid": "u_0000000000000000000000",
"name": "000",
"uin": "10000"
},
"otherAddByOtherQRCode": null,
"otherAddByYourQRCode": null,
"youAddByOtherQRCode": null,
"otherInviteOther": null,
"otherInviteYou": null,
"youInviteOther": null
},
"shutUp": null,
"memberUin": "10000",
"adminUin": "10001"
},
"buddyElement": null,
"feedMsgElement": null,
"essenceElement": null,
"groupNotifyElement": null,
"buddyNotifyElement": null,
"xmlElement": null,
"fileReceiptElement": null,
"localGrayTipElement": null,
"blockGrayTipElement": null,
"aioOpGrayTipElement": null,
"jsonGrayTipElement": 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
}
],
"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,
"nameType": 0,
"avatarFlag": 0,
"anonymousExtInfo": null,
"personalMedal": null,
"roleManagementTag": null,
"senderUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED"
}

View File

@ -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()
})

View File

@ -0,0 +1,34 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Red 解析器应当正确解析 群聊 群成员被禁言消息 1`] = `
{
"channel": {
"id": "100000000",
"name": "",
"type": 0,
},
"guild": {
"avatar": "https://p.qlogo.cn/gh/100000000/100000000/640",
"id": "100000000",
"name": "",
},
"id": undefined,
"message": {
"content": "",
"id": "1000000000000000000",
},
"operator": {
"id": "10001",
"name": undefined,
},
"platform": undefined,
"self_id": undefined,
"timestamp": 1000000000000,
"type": "unsafe-guild-mute",
"user": {
"avatar": "http://thirdqq.qlogo.cn/headimg_dl?dst_uin=10000&spec=640",
"id": "10000",
"name": "000",
},
}
`;

View File

@ -0,0 +1,145 @@
{
"msgId": "1000000000000000000",
"msgRandom": "1000000000000000000",
"msgSeq": "100000",
"cntSeq": "0",
"chatType": 2,
"msgType": 5,
"subMsgType": 8,
"sendType": 3,
"senderUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"peerUid": "100000000",
"channelId": "",
"guildId": "",
"guildCode": "0",
"fromUid": "0",
"fromAppid": "0",
"msgTime": "1000000000",
"msgMeta": "0x",
"sendStatus": 2,
"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": 4,
"revokeElement": null,
"proclamationElement": null,
"emojiReplyElement": null,
"groupElement": {
"type": 8,
"role": 0,
"groupName": "",
"memberUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"memberNick": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"memberRemark": "",
"adminUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"adminNick": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"adminRemark": "",
"createGroup": null,
"memberAdd": null,
"shutUp": {
"curTime": "1000000000",
"duration": "1296000",
"admin": {
"uid": "u_0000000000000000000001",
"card": "",
"name": "",
"role": 3,
"uin": "10001"
},
"member": {
"uid": "u_0000000000000000000000",
"card": "",
"name": "000",
"role": 0,
"uin": "10000"
}
},
"memberUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"adminUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED"
},
"buddyElement": null,
"feedMsgElement": null,
"essenceElement": null,
"groupNotifyElement": null,
"buddyNotifyElement": null,
"xmlElement": null,
"fileReceiptElement": null,
"localGrayTipElement": null,
"blockGrayTipElement": null,
"aioOpGrayTipElement": null,
"jsonGrayTipElement": 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
}
],
"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,
"nameType": 0,
"avatarFlag": 0,
"anonymousExtInfo": null,
"personalMedal": null,
"roleManagementTag": null,
"senderUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED"
}

View File

@ -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()
})

View File

@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Red 解析器应当正确解析 群聊 语音消息 1`] = `
{
"channel": {
"id": "100000000",
"name": "001",
"type": 0,
},
"guild": {
"avatar": "https://p.qlogo.cn/gh/100000000/100000000/640",
"id": "100000000",
"name": "001",
},
"id": undefined,
"member": {},
"message": {
"content": "<audio src="https://chronocat.vercel.app/v1/assets/eyJtc2dJZCI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJjaGF0VHlwZSI6MiwicGVlclVpZCI6IjEwMDAwMDAwMCIsImVsZW1lbnRJZCI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJ0aHVtYlNpemUiOjB9"/>",
"id": "1000000000000000000",
},
"platform": undefined,
"self_id": undefined,
"timestamp": 1000000000000,
"type": "message-created",
"user": {
"avatar": "http://thirdqq.qlogo.cn/headimg_dl?dst_uin=10000&spec=640",
"id": "10000",
"name": "000",
},
}
`;

View File

@ -0,0 +1,120 @@
{
"msgId": "1000000000000000000",
"msgRandom": "1000000000",
"msgSeq": "10000",
"cntSeq": "0",
"chatType": 2,
"msgType": 6,
"subMsgType": 0,
"sendType": 0,
"senderUid": "u_0000000000000000000000",
"peerUid": "100000000",
"channelId": "",
"guildId": "",
"guildCode": "0",
"fromUid": "0",
"fromAppid": "0",
"msgTime": "1000000000",
"msgMeta": "0x",
"sendStatus": 2,
"sendMemberName": "",
"sendNickName": "000",
"guildName": "",
"channelName": "",
"elements": [
{
"elementType": 4,
"elementId": "1000000000000000000",
"extBufForUI": "0x",
"textElement": null,
"faceElement": null,
"marketFaceElement": null,
"replyElement": null,
"picElement": null,
"pttElement": {
"fileName": "00000000000000000000000000000000.amr",
"filePath": "C:\\00000000000000000000000000000000.amr",
"md5HexStr": "00000000000000000000000000000000",
"fileSize": "10000",
"duration": 15,
"formatType": 1,
"voiceType": 0,
"voiceChangeType": 0,
"canConvert2Text": false,
"fileId": 0,
"fileUuid": "000000000000000000000000000000000000",
"text": "",
"translateStatus": 0,
"transferStatus": 0,
"progress": 0,
"playState": 0,
"waveAmplitudes": [
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99,
99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99, 99
],
"invalidState": 0,
"fileSubId": "1000000000",
"fileBizId": null
},
"videoElement": null,
"grayTipElement": 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
}
],
"records": [],
"emojiLikesList": [],
"commentCnt": "0",
"directMsgFlag": 0,
"directMsgMembers": [],
"peerName": "001",
"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": true,
"generalFlags": "0x",
"clientSeq": "0",
"fileGroupSize": null,
"foldingInfo": null,
"nameType": 0,
"avatarFlag": 0,
"anonymousExtInfo": null,
"personalMedal": null,
"roleManagementTag": null,
"senderUin": "10000"
}

View File

@ -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()
})

View File

@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Red 解析器应当正确解析 群聊 视频消息 1`] = `
{
"channel": {
"id": "100000000",
"name": "001",
"type": 0,
},
"guild": {
"avatar": "https://p.qlogo.cn/gh/100000000/100000000/640",
"id": "100000000",
"name": "001",
},
"id": undefined,
"member": {},
"message": {
"content": "<video src="https://chronocat.vercel.app/v1/assets/eyJtc2dJZCI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJjaGF0VHlwZSI6MiwicGVlclVpZCI6IjEwMDAwMDAwMCIsImVsZW1lbnRJZCI6IjEwMDAwMDAwMDAwMDAwMDAwMDAiLCJ0aHVtYlNpemUiOjEwMDAwfQ"/>",
"id": "1000000000000000000",
},
"platform": undefined,
"self_id": undefined,
"timestamp": 1000000000000,
"type": "message-created",
"user": {
"avatar": "http://thirdqq.qlogo.cn/headimg_dl?dst_uin=10000&spec=640",
"id": "10000",
"name": "000",
},
}
`;

View File

@ -0,0 +1,116 @@
{
"msgId": "1000000000000000000",
"msgRandom": "1000000000",
"msgSeq": "100000",
"cntSeq": "0",
"chatType": 2,
"msgType": 7,
"subMsgType": 0,
"sendType": 0,
"senderUid": "u_0000000000000000000000",
"peerUid": "100000000",
"channelId": "",
"guildId": "",
"guildCode": "0",
"fromUid": "0",
"fromAppid": "0",
"msgTime": "1000000000",
"msgMeta": "0x",
"sendStatus": 2,
"sendMemberName": "",
"sendNickName": "000",
"guildName": "",
"channelName": "",
"elements": [
{
"elementType": 5,
"elementId": "1000000000000000000",
"extBufForUI": "0x",
"textElement": null,
"faceElement": null,
"marketFaceElement": null,
"replyElement": null,
"picElement": null,
"pttElement": null,
"videoElement": {
"filePath": "C:\\00000000000000000000000000000000.mp4",
"fileName": "00000000000000000000000000000000.mp4",
"videoMd5": "00000000000000000000000000000000",
"thumbMd5": "00000000000000000000000000000001",
"fileTime": 90,
"thumbSize": 10000,
"fileFormat": 2,
"fileSize": "20000000",
"thumbWidth": 640,
"thumbHeight": 480,
"busiType": 0,
"subBusiType": 0,
"thumbPath": {},
"transferStatus": 0,
"progress": 0,
"invalidState": 0,
"fileUuid": "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
"fileSubId": "",
"fileBizId": null
},
"grayTipElement": 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
}
],
"records": [],
"emojiLikesList": [],
"commentCnt": "0",
"directMsgFlag": 0,
"directMsgMembers": [],
"peerName": "001",
"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": true,
"generalFlags": "0x",
"clientSeq": "0",
"fileGroupSize": null,
"foldingInfo": null,
"nameType": 0,
"avatarFlag": 0,
"anonymousExtInfo": null,
"personalMedal": null,
"roleManagementTag": null,
"senderUin": "10000"
}

View File

@ -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()
})

View File

@ -0,0 +1,31 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP
exports[`Red 解析器应当正确解析 群聊 引用消息 1`] = `
{
"channel": {
"id": "100000000",
"name": "002",
"type": 0,
},
"guild": {
"avatar": "https://p.qlogo.cn/gh/100000000/100000000/640",
"id": "100000000",
"name": "002",
},
"id": undefined,
"member": {},
"message": {
"content": "<quote chronocat:seq="100000"><author id="10001" name="001" avatar="http://thirdqq.qlogo.cn/headimg_dl?dst_uin=10001&amp;spec=640"/>yyy</quote>xxx",
"id": "1000000000000000000",
},
"platform": undefined,
"self_id": undefined,
"timestamp": 1000000000000,
"type": "message-created",
"user": {
"avatar": "http://thirdqq.qlogo.cn/headimg_dl?dst_uin=10000&spec=640",
"id": "10000",
"name": "000",
},
}
`;

View File

@ -0,0 +1,270 @@
{
"msgId": "1000000000000000000",
"msgRandom": "1000000000",
"msgSeq": "100000",
"cntSeq": "0",
"chatType": 2,
"msgType": 9,
"subMsgType": 33,
"sendType": 0,
"senderUid": "u_0000000000000000000000",
"peerUid": "100000000",
"channelId": "",
"guildId": "",
"guildCode": "0",
"fromUid": "0",
"fromAppid": "0",
"msgTime": "1000000000",
"msgMeta": "0x",
"sendStatus": 2,
"sendMemberName": "",
"sendNickName": "000",
"guildName": "",
"channelName": "",
"elements": [
{
"elementType": 7,
"elementId": "1000000000000000000",
"extBufForUI": "0x",
"textElement": null,
"faceElement": null,
"marketFaceElement": null,
"replyElement": {
"replayMsgId": "0",
"replayMsgSeq": "100000",
"replayMsgRootSeq": "0",
"replayMsgRootMsgId": "0",
"replayMsgRootCommentCnt": "0",
"sourceMsgIdInRecords": "1000000000000000001",
"sourceMsgText": "",
"sourceMsgTextElems": [
{
"replyAbsElemType": 1,
"textElemContent": "yyy",
"faceElem": null
}
],
"senderUid": "10001",
"senderUidStr": "u_0000000000000000000001",
"replyMsgClientSeq": "0",
"replyMsgTime": "1000000000",
"replyMsgRevokeType": 0,
"sourceMsgIsIncPic": false,
"sourceMsgExpired": false,
"anonymousNickName": null,
"originalMsgState": null,
"senderUin": "u_0000000000000000000001"
},
"picElement": null,
"pttElement": null,
"videoElement": null,
"grayTipElement": 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
},
{
"elementType": 1,
"elementId": "1000000000000000000",
"extBufForUI": "0x",
"textElement": {
"content": "xxx",
"atType": 0,
"atUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"atTinyId": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"atNtUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"subElementType": 0,
"atChannelId": "0",
"atRoleId": "0",
"atRoleColor": 0,
"atRoleName": "",
"needNotify": 0,
"atNtUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED"
},
"faceElement": null,
"marketFaceElement": null,
"replyElement": null,
"picElement": null,
"pttElement": null,
"videoElement": null,
"grayTipElement": 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
}
],
"records": [
{
"msgId": "1000000000000000001",
"msgRandom": "1000000000",
"msgSeq": "100000",
"cntSeq": "0",
"chatType": 2,
"msgType": 2,
"subMsgType": 1,
"sendType": 0,
"senderUid": "u_0000000000000000000001",
"peerUid": "100000000",
"channelId": "",
"guildId": "",
"guildCode": "0",
"fromUid": "0",
"fromAppid": "0",
"msgTime": "1000000000",
"msgMeta": "0x",
"sendStatus": 2,
"sendMemberName": "",
"sendNickName": "001",
"guildName": "",
"channelName": "",
"elements": [
{
"elementType": 1,
"elementId": "1000000000000000000",
"extBufForUI": "0x",
"textElement": {
"content": "yyy",
"atType": 0,
"atUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"atTinyId": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"atNtUid": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED",
"subElementType": 0,
"atChannelId": "0",
"atRoleId": "0",
"atRoleColor": 0,
"atRoleName": "",
"needNotify": 0,
"atNtUin": "DO_NOT_USE_THIS_OR_YOU_WILL_BE_FIRED"
},
"faceElement": null,
"marketFaceElement": null,
"replyElement": null,
"picElement": null,
"pttElement": null,
"videoElement": null,
"grayTipElement": 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
}
],
"records": [],
"emojiLikesList": [],
"commentCnt": "0",
"directMsgFlag": 0,
"directMsgMembers": [],
"peerName": "002",
"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,
"nameType": 0,
"avatarFlag": 0,
"anonymousExtInfo": null,
"personalMedal": null,
"roleManagementTag": null,
"senderUin": "10001"
}
],
"emojiLikesList": [],
"commentCnt": "0",
"directMsgFlag": 0,
"directMsgMembers": [],
"peerName": "002",
"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": true,
"generalFlags": "0x",
"clientSeq": "0",
"fileGroupSize": null,
"foldingInfo": null,
"nameType": 0,
"avatarFlag": 0,
"anonymousExtInfo": null,
"personalMedal": null,
"roleManagementTag": null,
"senderUin": "10000"
}

View File

@ -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()
})

View File

@ -0,0 +1,10 @@
{
"extends": "../../../tsconfig.base",
"compilerOptions": {
"rootDir": ".."
},
"include": [
".",
"../src"
]
}

View File

@ -2,6 +2,7 @@
"extends": "./tsconfig.base",
"include": [
"jest.config.ts",
"external",
"packages",
"scripts",
],

View File

@ -6407,6 +6407,7 @@ __metadata:
jest: "npm:^29.6.3"
prettier: "npm:^3.0.2"
ts-jest: "npm:^29.1.1"
ts-node: "npm:^10.9.1"
typescript: "npm:~5.1.0"
languageName: unknown
linkType: soft