Skip to content

feat(gateway): Add WeChat platform adapter with long-poll architecture - #5230

Closed
Alex4Xu wants to merge 3 commits into
NousResearch:mainfrom
Alex4Xu:feat/wechat-adapter
Closed

feat(gateway): Add WeChat platform adapter with long-poll architecture#5230
Alex4Xu wants to merge 3 commits into
NousResearch:mainfrom
Alex4Xu:feat/wechat-adapter

Conversation

@Alex4Xu

@Alex4Xu Alex4Xu commented Apr 5, 2026

Copy link
Copy Markdown

Summary

Add a complete WeChat (personal) messaging platform adapter following the official Tencent openclaw-weixin plugin architecture.

What this adds

  • WeChatAdapter (gateway/platforms/wechat.py) - Full adapter lifecycle: QR login, continuous polling, inbound/outbound message handling for text, images, files, voice, and video
  • WeChatTransport (gateway/platforms/wechat_transport.py) - Raw HTTP wrappers, header building, base_info injection, long-poll API, CDN upload pipeline, media send/receive with AES decryption
  • WeChatStateStore (gateway/platforms/wechat_state.py) - Account store, sync cursor persistence, context-token caching
  • 73 tests (tests/gateway/test_wechat.py) - Covering login state transitions, polling, message conversion, media routing, session-expired pause behavior, typing tickets, and payload shapes

Key design decisions

  • Long-poll based (not webhook), matching the official Tencent plugin
  • context_token continuity for session management, cached per account+user
  • Media logic isolated in transport layer, not the adapter
  • AES decryption for inbound image/media downloads
  • errcode=-14 session expiry handled with account pause (not hammering retries)
  • base_info.channel_version injected on all API requests

Files changed

File Lines Description
gateway/platforms/wechat.py 493 Adapter: login, poll loop, send/receive
gateway/platforms/wechat_transport.py ~600 Transport: HTTP, upload, download, headers
gateway/platforms/wechat_state.py ~100 State: accounts, cursors, tokens
tests/gateway/test_wechat.py 76KB 73 tests, all passing

Registration points (already present in upstream)

  • gateway/config.py: Platform.WECHAT enum member
  • hermes_cli/tools_config.py: WeChat entry in PLATFORMS dict
  • hermes_cli/skills_config.py: WeChat entry in PLATFORMS dict
  • toolsets.py: hermes-wechat toolset definition
  • hermes_cli/main.py: hermes wechat CLI commands

Testing

uv run --extra dev python -m pytest tests/gateway/test_wechat.py -q
# 73 passed in 1.42s

Motivation

WeChat is the dominant messaging platform in China. This adapter enables Hermes users to interact with the agent via WeChat personal accounts using the official Tencent iLink bot protocol, expanding Hermes's multi-platform gateway to cover this critical market.

Notes

This PR mirrors the official Tencent openclaw-weixin plugin architecture rather than inventing a webhook/proxy design. The code has been tested with two live WeChat bot accounts.

@trevorgordon981

Copy link
Copy Markdown
Contributor

Substantial. 3080 lines of new code for a complete platform adapter is one of the biggest single PRs on this repo. Architectural choices look sound: long-poll matching the official Tencent plugin (vs. reinventing with webhooks), transport/adapter/state separation, isolated media logic with AES decryption, session-expiry account pause instead of retry hammering. 73 tests is solid coverage. Two questions worth flagging for long-running infra: how does the context_token cache invalidate when an account reconnects mid-session, and does the polling loop back off if the server repeatedly returns 429/503, or retry at constant cadence? Those are the two places long-poll adapters typically erode over time. Non-blocking. Proceed.

@Alex4Xu

Alex4Xu commented Apr 6, 2026

Copy link
Copy Markdown
Author

Thanks for the thorough review — both questions pointed at real issues.

Ran verification tests against both before patching (using GLM-5.1 + Claude Code), and confirmed:

context_token cache on reconnect: Cache was never cleared on WeChatSessionExpiredError. A stale token would persist across reconnects and get passed on subsequent sends. Fixed in _poll_account_loop: WeChatStateStore.clear_context_tokens(account_id) is now called before pausing the account, so any reconnect starts with a clean slate.

Polling backoff on 429/503: The loop was using min(consecutive_failures, 5) for all exceptions with no distinction — confirmed max backoff of only 3s after 3 consecutive rate-limit errors. Fixed by introducing WeChatRateLimitError in the transport layer (_api_get / _api_post now raise it explicitly on 429/503), caught separately in the loop with exponential backoff min(2 ** consecutive_failures, 60) capped at 60s.

Both fixes are covered by new regression tests added to test_wechat.py. Appreciate you catching these before they caused trouble in prod.

@Alex4Xu
Alex4Xu force-pushed the feat/wechat-adapter branch from 1abaae8 to 0fd249a Compare April 6, 2026 15:35
Alex4Xu and others added 3 commits April 6, 2026 23:39
Add full WeChat (personal) messaging platform adapter following the
official Tencent openclaw-weixin plugin architecture:

- WeChatAdapter: lifecycle management, QR login, poll loop, inbound/outbound
  message handling for text, image, file, voice, and video
- WeChatTransport: raw HTTP wrappers, header building, base_info injection,
  long-poll API, CDN upload pipeline, media send/receive
- WeChatStateStore: account store, sync cursor store, context-token cache
- 73 tests covering login state transitions, polling, message conversion,
  media routing, session-expired pause behavior, typing tickets, and payload
  shapes

Key design choices:
- Long-poll based (not webhook) matching official plugin
- context_token continuity for session management
- Media logic isolated in transport layer
- AES decryption support for inbound images
- errcode=-14 session expiry handling with account pause

Closes: WeChat integration for Hermes gateway
… rate-limit backoff

Two issues confirmed by regression tests before patching:

1. WeChatSessionExpiredError did not clear the context_token cache, leaving
   stale tokens that would be reused after an account reconnects mid-session.
   Fix: call clear_context_tokens(account_id) before pausing the account.

2. The poll loop used min(consecutive_failures, 5) for all errors with no
   distinction for 429/503, sustaining up to 1 req/3s under rate limiting.
   Fix: introduce WeChatRateLimitError in the transport layer (_api_get /
   _api_post raise it on 429/503); caught separately in _poll_account_loop
   with exponential backoff min(2**n, 60).

Both fixes covered by new tests in TestIssueVerification.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…orm registration

Progress messages:
- Remove WeChat from _no_edit_platforms — enable tool-call progress for WeChat
- WeChat uses "new-tool-only" dedup (no spam when same tool repeats)
- Cap at 3 progress messages then silently drop; typing indicator covers the rest
- Progress interval set to 5s (matches openclaw-weixin reference, reduces API pressure)

Typing indicator:
- Override _keep_typing in WeChatAdapter with 5s keepalive interval
  (base default is 2s, tuned for Telegram/Discord; WeChat reference uses 5s)

Platform registration:
- Register WeChatAdapter in gateway router
- Add WECHAT_ALLOWED_USERS / WECHAT_ALLOW_ALL_USERS access control
- Disable streaming and tool-progress editing for WeChat (no edit_message support)
- Add hermes-wechat toolset; include in hermes-gateway union

CLI:
- Add `hermes wechat login / status / accounts` subcommands
- Add WeChat to skills platform list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #8665 which synthesizes the best fixes from ~25 community PRs into a single consolidated change. Your contribution (WeChat long-poll adapter) was reviewed and informed the final implementation. Thank you @Alex4Xu for your work on this!

@teknium1 teknium1 closed this Apr 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants