feat(weixin): add WeChat channel plugin with QR login and UI#1704
feat(weixin): add WeChat channel plugin with QR login and UI#1704kaizhou-lab merged 46 commits intomainfrom
Conversation
- WeixinLogin: rewrite API calls to use GET+query params matching iLink SDK (bot_type param, correct response fields qrcode/qrcode_img_content, bot_token/ilink_bot_id, long-poll timeout treated as wait) - WeixinLoginHandler: render QR page via hidden BrowserWindow to extract canvas data URL, since qrcode_img_content is a JS-rendered SPA - WeixinPlugin: write SDK credential file (~/.openclaw/.../accounts/<id>.json) on initialize so weixin-agent-sdk start() can authenticate - ChannelManager: add weixin to getPluginTypeFromId and builtinStartableTypes; add weixin credentials block in enablePlugin; always derive pluginType from pluginId instead of stale DB type field - channelBridge: add weixin to BUILTIN_TYPES and BUILTIN_NAMES so getPluginStatus correctly returns hasToken and status for the WeChat channel - database: include type in upsertChannelPlugin ON CONFLICT update so stale plugin type records are corrected on next enable
- Fix duplicate replies by writing WeChat credentials to AionUi's isolated data dir (getDataDir()) instead of ~/.openclaw, preventing the local openclaw gateway from auto-discovering the account - Temporarily set OPENCLAW_STATE_DIR before void start() so the SDK reads the isolated dir, then restore after the first async suspension - Add .then() auto-resolve on emitMessage for non-streaming responses (pairing codes, errors) that only call sendMessage without editMessage - Strip HTML from sendMessage text stored in accumulatedText - Add stripHtml to WeixinAdapter and wire it into ActionExecutor for outgoing text formatting on the weixin platform - Add 'weixin' to ConversationSource and isFromChannel yoloMode check - Fix TelegramConfigForm to filter pairings/events by platformType so Weixin pairing requests don't bleed into the Telegram UI - Add pairing request and authorized user management sections to WeixinConfigForm, matching the pattern from TelegramConfigForm - Update weixinPlugin unit tests to mock @/common/platform and use the isolated test data dir; fix pending-handler tests to block emitMessage so auto-resolve doesn't fire before rejection tests can observe it
- Add weixin.svg channel logo (green WeChat-style icon) - Register weixin logo in ChannelHeader channelLogoMap - Add weixin logo to CHANNEL_LOGOS in WebuiModalContent Channels tab - Add channels.weixinTitle / channels.weixinDesc i18n keys to all 6 locales (zh-CN: 微信, zh-TW: 微信, others: WeChat) - Update webui.featureChannelsDesc to include WeChat across all locales
- Move & decoding to first position to prevent partial entity artifacts in output (fixes double-escaping/unescaping, CWE-116) - Move tag stripping to last to catch tags formed from decoded entities like <script> (fixes incomplete sanitization, CWE-116) - Add stripHtml unit tests covering XSS vectors and entity decoding
- Update WeixinLogin to use qrcode_url/ticket from QR endpoint and botToken/userId/baseUrl from poll endpoint (matching test contracts) - Remove renderQRPage from WeixinLoginHandler: onQR now forwards the image URL directly to renderer instead of opening a hidden window - Add missing channel mock entries (getPendingPairings, getAuthorizedUsers, pairingRequested, userAuthorized) to WeixinConfigForm DOM tests - Fix oxfmt formatting issues in 5 files
- weixinLogin.test.ts: align mock responses with actual iLink API field names (qrcode/qrcode_img_content for QR endpoint, bot_token/ilink_bot_id/ baseurl for poll endpoint) - weixinLoginHandler.test.ts: use vi.spyOn on renderQRPage to avoid BrowserWindow mock complexity; mock electron at file level for import; test verifies onQR forwards canvas data URL to renderer IPC
- Add formatError helper to capture error.cause for better diagnostics - Split agent.chat() and callSendMessage() into separate try-catch blocks - Log 'agent error' vs 'send error' to distinguish failure points - No retry on send failure to avoid duplicate messages
…n stripHtml - Strip tags before decoding entities to prevent double-unescaping (&lt; now stays as < instead of becoming <) - Use single-pass replace callback for entity decoding (no chained replacements) - Two-pass strip: before decode handles raw tags, after decode handles entity-encoded tags (<script> -> <script>) - Loop strip until stable to handle nested/malformed markup Fixes CodeQL alerts: js/double-escaping (#99), js/incomplete-multi-character-sanitization (#100)
Code Review:feat(weixin): add WeChat channel plugin with QR login and UI (#1704)变更概述本 PR 为 AionUI 添加了完整的微信渠道插件,包含:main process 端的 QR 码登录流程( 方案评估结论:✅ 方案合理 自行实现 WeChat iLink Bot HTTP 客户端替代被移除的 SDK 依赖的思路正确,Promise bridge 模式( 问题清单🟠 HIGH —
|
| # | 严重级别 | 文件 | 问题 |
|---|---|---|---|
| 1 | 🟠 HIGH | WeixinPlugin.ts:84 |
console.log 直接透传到生产环境 main process |
| 2 | 🟡 MEDIUM | WeixinConfigForm.tsx:156,170,184,283 |
8 处 error: any 违反 strict mode |
| 3 | 🔵 LOW | WeixinMonitor.ts, WeixinTyping.ts, WeixinLogin.ts |
16 处 no-await-in-loop lint warning 需要 disable 注释 |
| 4 | 🔵 LOW | WeixinPlugin.ts:115 |
activeUsers 只增不减,长期运行内存增长 |
结论
console.log 在生产代码中),需要处理后合并。MEDIUM 和 LOW 问题不阻塞合并但建议一并修复。整体实现质量高,测试覆盖充分,i18n 完整,架构边界清晰。
本报告由本地 pr-review skill 生成,包含完整项目上下文,无截断限制。
- Fix 8 no-explicit-any violations in WeixinConfigForm.tsx: replace catch (error: any) with proper unknown narrowing, and replace (saved as any) with typed Record<string, unknown> cast - Add oxlint-disable-next-line comments for intentional no-await-in-loop patterns in WeixinMonitor.ts (10), WeixinTyping.ts (2), WeixinLogin.ts (3) - Add oxlint-disable-next-line for no-unmodified-loop-condition on the abort-signal while loop in WeixinMonitor.ts Review follow-up for #1704
PR Fix 验证报告原始 PR: #1704
总结: ✅ 已修复 2 个 | ⏭️ 跳过 2 个(用户确认) 验证: |
Summary
Test plan
bun run test