mirror of
https://github.com/NanmiCoder/cc-haha
synced 2026-07-16 13:03:31 +08:00
WeChat and DingTalk were using different pairing, attachment, and response state paths, which made the new IM channels behave differently from Feishu and Telegram. Align the shared pairing model, wire inbound media into the existing attachment bridge, and map platform response capabilities to their real APIs: WeChat block streaming plus typing, DingTalk AI Card streaming. Constraint: WeChat iLink exposes typing and block streaming, but no editable message/card streaming API Constraint: DingTalk streaming depends on the AI Card create/deliver/stream/finalize lifecycle Rejected: Fake DingTalk typing with standalone markdown | it would add chat noise instead of platform state Rejected: Auto-pair WeChat after QR login | it bypasses the shared IM pairing model Confidence: high Scope-risk: moderate Directive: Keep WeChat streaming as block-send unless iLink adds editable messages; keep DingTalk streaming on AI Card APIs Tested: bun run check:adapters; bun run check:server; cd desktop && bun run test src/stores/adapterStore.test.ts; cd desktop && bun run build; bunx tsc -p adapters/tsconfig.json --noEmit; git diff --check Not-tested: Live WeChat and DingTalk platform smoke with real production credentials
75 lines
2.1 KiB
TypeScript
75 lines
2.1 KiB
TypeScript
import { describe, expect, it } from 'bun:test'
|
|
import { buildClientVersion, extractWechatText } from '../protocol.js'
|
|
import { collectWechatMediaCandidates } from '../media.js'
|
|
|
|
describe('WeChat protocol helpers', () => {
|
|
it('encodes iLink client versions like the OpenClaw Weixin plugin', () => {
|
|
expect(buildClientVersion('2.1.7')).toBe((2 << 16) | (1 << 8) | 7)
|
|
expect(buildClientVersion('1.0.11')).toBe(65547)
|
|
})
|
|
|
|
it('extracts plain text from WeChat message items', () => {
|
|
expect(extractWechatText([
|
|
{ type: 1, text_item: { text: 'hello' } },
|
|
])).toBe('hello')
|
|
})
|
|
|
|
it('extracts voice transcription when text items are absent', () => {
|
|
expect(extractWechatText([
|
|
{ type: 3, voice_item: { text: 'voice text' } },
|
|
])).toBe('voice text')
|
|
})
|
|
|
|
it('preserves quoted text context', () => {
|
|
expect(extractWechatText([
|
|
{
|
|
type: 1,
|
|
text_item: { text: 'reply' },
|
|
ref_msg: {
|
|
title: 'quote title',
|
|
message_item: { type: 1, text_item: { text: 'quoted body' } },
|
|
},
|
|
},
|
|
])).toBe('[引用: quote title | quoted body]\nreply')
|
|
})
|
|
|
|
it('collects image and file media candidates from message items', () => {
|
|
expect(collectWechatMediaCandidates([
|
|
{
|
|
type: 2,
|
|
msg_id: 'img-1',
|
|
image_item: {
|
|
aeskey: '00112233445566778899aabbccddeeff',
|
|
media: {
|
|
full_url: 'https://cdn.example.com/image',
|
|
encrypt_query_param: 'enc=1',
|
|
},
|
|
},
|
|
},
|
|
{
|
|
type: 4,
|
|
msg_id: 'file-1',
|
|
file_item: {
|
|
file_name: 'report.pdf',
|
|
media: {
|
|
full_url: 'https://cdn.example.com/file',
|
|
aes_key: Buffer.from('00112233445566778899aabbccddeeff').toString('base64'),
|
|
},
|
|
},
|
|
},
|
|
])).toMatchObject([
|
|
{
|
|
kind: 'image',
|
|
name: 'wechat-image-img-1.jpg',
|
|
url: 'https://cdn.example.com/image',
|
|
},
|
|
{
|
|
kind: 'file',
|
|
name: 'report.pdf',
|
|
url: 'https://cdn.example.com/file',
|
|
mimeType: 'application/pdf',
|
|
},
|
|
])
|
|
})
|
|
})
|