feat(channels): restore WhatsApp config compatibility

This commit is contained in:
晴天
2026-05-23 07:51:16 +08:00
parent 8d745e7543
commit d933177ec3
6 changed files with 473 additions and 16 deletions

View File

@@ -49,6 +49,108 @@ test('渠道保存不会向不支持顶层 requireMention 的平台写入非法
assert.equal(Object.hasOwn(form, 'requireMention'), false)
})
test('WhatsApp 渠道保存会写入扫码运行和访问策略字段并启用插件', () => {
const cfg = { channels: {} }
mergeOpenClawMessagingPlatformConfig(cfg, {
platform: 'whatsapp',
accountId: 'phone-a',
form: {
enabled: 'true',
configWrites: 'true',
sendReadReceipts: 'false',
selfChatMode: 'true',
dmPolicy: 'allowlist',
allowFrom: '+15551234567, +15557654321',
defaultTo: '+15550001111',
groupPolicy: 'allowlist',
groupAllowFrom: '120363@g.us, 120364@g.us',
contextVisibility: 'allowlist_quote',
historyLimit: '80',
dmHistoryLimit: '20',
mediaMaxMb: '50',
debounceMs: '800',
textChunkLimit: '1800',
chunkMode: 'newline',
blockStreaming: 'true',
reactionLevel: 'ack',
replyToMode: 'first',
messagePrefix: '[WA]',
responsePrefix: '[AI]',
ackEmoji: '✅',
ackDirect: 'true',
ackGroup: 'mentions',
},
})
const root = cfg.channels.whatsapp
const account = root.accounts['phone-a']
assert.equal(root.defaultAccount, 'phone-a')
assert.equal(account.enabled, true)
assert.equal(account.configWrites, true)
assert.equal(account.sendReadReceipts, false)
assert.equal(account.selfChatMode, true)
assert.equal(account.dmPolicy, 'allowlist')
assert.deepEqual(account.allowFrom, ['+15551234567', '+15557654321'])
assert.equal(account.defaultTo, '+15550001111')
assert.equal(account.groupPolicy, 'allowlist')
assert.deepEqual(account.groupAllowFrom, ['120363@g.us', '120364@g.us'])
assert.equal(account.contextVisibility, 'allowlist_quote')
assert.equal(account.historyLimit, 80)
assert.equal(account.dmHistoryLimit, 20)
assert.equal(account.mediaMaxMb, 50)
assert.equal(account.debounceMs, 800)
assert.equal(account.textChunkLimit, 1800)
assert.equal(account.chunkMode, 'newline')
assert.equal(account.blockStreaming, true)
assert.equal(account.reactionLevel, 'ack')
assert.equal(account.replyToMode, 'first')
assert.equal(account.messagePrefix, '[WA]')
assert.equal(account.responsePrefix, '[AI]')
assert.deepEqual(account.ackReaction, { emoji: '✅', direct: true, group: 'mentions' })
assert.equal(cfg.plugins.entries.whatsapp.enabled, true)
})
test('WhatsApp 读取会回显扫码运行字段且诊断不要求 Bot Token', () => {
const values = buildMessagingPlatformFormValues('whatsapp', {
enabled: true,
configWrites: true,
sendReadReceipts: false,
selfChatMode: true,
dmPolicy: 'open',
allowFrom: ['*'],
groupPolicy: 'allowlist',
groupAllowFrom: ['120363@g.us'],
historyLimit: 50,
debounceMs: 800,
mediaMaxMb: 50,
blockStreaming: true,
ackReaction: { emoji: '✅', direct: true, group: 'mentions' },
})
const diagnosis = buildOpenClawChannelDiagnosis({
platform: 'whatsapp',
configExists: true,
channelEnabled: true,
form: values,
})
assert.equal(values.enabled, 'true')
assert.equal(values.configWrites, 'true')
assert.equal(values.sendReadReceipts, 'false')
assert.equal(values.selfChatMode, 'true')
assert.equal(values.allowFrom, '*')
assert.equal(values.groupAllowFrom, '120363@g.us')
assert.equal(values.historyLimit, '50')
assert.equal(values.debounceMs, '800')
assert.equal(values.mediaMaxMb, '50')
assert.equal(values.blockStreaming, 'true')
assert.equal(values.ackEmoji, '✅')
assert.equal(values.ackDirect, 'true')
assert.equal(values.ackGroup, 'mentions')
assert.equal(diagnosis.checks.find(item => item.id === 'credentials')?.ok, true)
assert.match(diagnosis.checks.find(item => item.id === 'credentials')?.title || '', /扫码|会话/)
})
test('Signal 渠道保存会保留多账号和上游运行字段', () => {
const cfg = { channels: {} }

View File

@@ -7,8 +7,15 @@ const channelsPageSource = readFileSync(new URL('../src/pages/channels.js', impo
function getRegistryBlock(platformId) {
const start = channelsPageSource.indexOf(` ${platformId}: {`)
assert.notEqual(start, -1, `未找到 ${platformId} 渠道注册表`)
const next = channelsPageSource.indexOf('\n slack: {', start + 1)
return channelsPageSource.slice(start, next === -1 ? undefined : next)
const braceStart = channelsPageSource.indexOf('{', start)
let depth = 0
for (let index = braceStart; index < channelsPageSource.length; index += 1) {
const char = channelsPageSource[index]
if (char === '{') depth += 1
if (char === '}') depth -= 1
if (depth === 0) return channelsPageSource.slice(start, index + 1)
}
assert.fail(`未找到 ${platformId} 渠道注册表结束位置`)
}
test('Discord 渠道 UI 会暴露服务器频道 allowlist 配置字段', () => {
@@ -39,3 +46,23 @@ test('iMessage 渠道 UI 会暴露桥接运行配置字段', () => {
assert.match(imessageBlock, /pluginRequired:\s*'@openclaw\/imessage@latest'/)
assert.match(imessageBlock, /pluginId:\s*'imessage'/)
})
test('WhatsApp 渠道 UI 会恢复扫码登录和运行配置入口', () => {
const whatsappBlock = getRegistryBlock('whatsapp')
for (const field of [
'selfChatMode',
'allowFrom',
'groupAllowFrom',
'debounceMs',
'mediaMaxMb',
'sendReadReceipts',
'ackEmoji',
'ackGroup',
]) {
assert.match(whatsappBlock, new RegExp(`key:\\s*'${field}'`))
}
assert.match(whatsappBlock, /id:\s*'login'/)
assert.match(whatsappBlock, /pluginRequired:\s*'@openclaw\/whatsapp@latest'/)
assert.match(whatsappBlock, /pluginId:\s*'whatsapp'/)
})