feat: add QQ Bot platform adapter (Official API v2)#7616
Conversation
There was a problem hiding this comment.
Pull request overview
Adds a new QQ Bot messaging platform adapter (Official QQ Bot API v2) to the Hermes Gateway, wiring it into gateway runtime, CLI setup/status/tooling, toolsets, cron delivery, and documentation so Hermes can operate on QQ via WebSocket events + REST sends and support voice STT.
Changes:
- Introduces
QQAdapterwith WebSocket gateway handling, REST messaging/media upload, and voice transcription support. - Registers QQ across gateway config/run/cron and CLI setup/status/tools so it can be configured and used end-to-end.
- Adds QQ documentation and a dedicated unit test suite for the adapter.
Reviewed changes
Copilot reviewed 20 out of 20 changed files in this pull request and generated 10 comments.
Show a summary per file
| File | Description |
|---|---|
gateway/platforms/qq.py |
New QQ adapter implementing gateway connection, message handling, media, and STT. |
gateway/platforms/__init__.py |
Exports QQAdapter from the platforms package. |
gateway/config.py |
Adds Platform.QQ + env overrides/home channel support + connected-platform detection. |
gateway/run.py |
Registers QQ adapter creation and plugs QQ into allowlist/allow-all auth env maps. |
tools/send_message_tool.py |
Adds QQ routing and a direct-send implementation via the adapter. |
toolsets.py |
Defines hermes-qq toolset and includes it in hermes-gateway. |
cron/scheduler.py |
Adds QQ to known delivery platforms and delivery platform map. |
hermes_cli/config.py |
Adds QQ-related env keys to CLI config management. |
hermes_cli/gateway.py |
Adds QQ to the gateway setup menu/options. |
hermes_cli/setup.py |
Adds an interactive _setup_qq() wizard and registers QQ in gateway setup flow. |
hermes_cli/status.py |
Adds QQ to platform status display. |
hermes_cli/tools_config.py |
Adds QQ toolset label/default and auto-enablement detection. |
tests/gateway/test_qq.py |
Adds unit tests covering key QQ adapter helpers and dispatch behavior. |
website/docs/user-guide/messaging/qq.md |
New QQ setup/config/STT/troubleshooting documentation page. |
website/docs/user-guide/messaging/index.md |
Adds QQ to platform comparison table, architecture diagram, toolsets table, and nav links. |
website/docs/reference/environment-variables.md |
Documents QQ-related environment variables. |
cli-config.yaml.example |
Adds qq to supported platform keys and default toolset mapping example. |
AGENTS.md |
Updates the directory tree platform list to include QQ. |
PR_DESCRIPTION.md |
Adds a PR description/template-like file describing the QQ integration. |
.ggcode/todos.json |
Tracks internal TODOs for the PR review/fixup process. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
7ed8943 to
32a8f91
Compare
Add full QQ Bot integration via the Official QQ Bot API (v2): - WebSocket gateway for inbound events (C2C, group, guild, DM) - REST API for outbound text/markdown/media messages - Voice transcription (Tencent ASR + configurable STT provider) - Attachment processing (images, voice, files) - User authorization (allowlist + allow-all + DM pairing) Integration points: - gateway: Platform.QQ enum, adapter factory, allowlist maps - CLI: setup wizard, gateway config, status display, tools config - tools: send_message cross-platform routing, toolsets - cron: delivery platform support - docs: QQ Bot setup guide
…g, restore missing setup functions - Rename platform from 'qq' to 'qqbot' across all integration points (Platform enum, toolset, config keys, import paths, file rename qq.py → qqbot.py) - Add PLATFORM_HINTS for QQBot in prompt_builder (QQ supports markdown) - Set SUPPORTS_MESSAGE_EDITING = False to skip streaming on QQ (prevents duplicate messages from non-editable partial + final sends) - Add _send_qqbot() standalone send function for cron/send_message tool - Add interactive _setup_qq() wizard in hermes_cli/setup.py - Restore missing _setup_signal/email/sms/dingtalk/feishu/wecom/wecom_callback functions that were lost during the original merge
… shared strip_markdown Improvements from our earlier #8269 salvage work applied to #7616: - Platform token lock: acquire_scoped_lock/release_scoped_lock prevents two profiles from double-connecting the same QQ bot simultaneously - Send retry with exponential backoff (3 attempts, 1s/2s/4s) with permanent vs transient error classification (matches Telegram pattern) - Proper long-message splitting via truncate_message() instead of hard-truncating at MAX_MESSAGE_LENGTH (preserves code blocks, adds 1/N) - REST-based one-shot send in send_message_tool — uses QQ Bot REST API directly with httpx instead of creating a full WebSocket adapter per message (fixes the connect→send race condition) - Use shared strip_markdown() from helpers.py instead of 15 lines of inline regex with import-inside-method (DRY, same as BlueBubbles/SMS) - format_message() now wired into send() pipeline
… shared strip_markdown Improvements from our earlier #8269 salvage work applied to #7616: - Platform token lock: acquire_scoped_lock/release_scoped_lock prevents two profiles from double-connecting the same QQ bot simultaneously - Send retry with exponential backoff (3 attempts, 1s/2s/4s) with permanent vs transient error classification (matches Telegram pattern) - Proper long-message splitting via truncate_message() instead of hard-truncating at MAX_MESSAGE_LENGTH (preserves code blocks, adds 1/N) - REST-based one-shot send in send_message_tool — uses QQ Bot REST API directly with httpx instead of creating a full WebSocket adapter per message (fixes the connect→send race condition) - Use shared strip_markdown() from helpers.py instead of 15 lines of inline regex with import-inside-method (DRY, same as BlueBubbles/SMS) - format_message() now wired into send() pipeline
|
Merged via PR #9364. Your implementation was cherry-picked as the primary adapter — raw Official API v2, voice STT, WebSocket lifecycle, ACL policies, 65 tests, and full docs. We added platform token lock, send retry with backoff, proper message splitting, REST one-shot send, and 11 additional docs pages on top. Thank you @topcheer for the excellent work — this is the QQ Bot adapter shipping in Hermes. 🎉 |
… shared strip_markdown Improvements from our earlier NousResearch#8269 salvage work applied to NousResearch#7616: - Platform token lock: acquire_scoped_lock/release_scoped_lock prevents two profiles from double-connecting the same QQ bot simultaneously - Send retry with exponential backoff (3 attempts, 1s/2s/4s) with permanent vs transient error classification (matches Telegram pattern) - Proper long-message splitting via truncate_message() instead of hard-truncating at MAX_MESSAGE_LENGTH (preserves code blocks, adds 1/N) - REST-based one-shot send in send_message_tool — uses QQ Bot REST API directly with httpx instead of creating a full WebSocket adapter per message (fixes the connect→send race condition) - Use shared strip_markdown() from helpers.py instead of 15 lines of inline regex with import-inside-method (DRY, same as BlueBubbles/SMS) - format_message() now wired into send() pipeline
… shared strip_markdown Improvements from our earlier NousResearch#8269 salvage work applied to NousResearch#7616: - Platform token lock: acquire_scoped_lock/release_scoped_lock prevents two profiles from double-connecting the same QQ bot simultaneously - Send retry with exponential backoff (3 attempts, 1s/2s/4s) with permanent vs transient error classification (matches Telegram pattern) - Proper long-message splitting via truncate_message() instead of hard-truncating at MAX_MESSAGE_LENGTH (preserves code blocks, adds 1/N) - REST-based one-shot send in send_message_tool — uses QQ Bot REST API directly with httpx instead of creating a full WebSocket adapter per message (fixes the connect→send race condition) - Use shared strip_markdown() from helpers.py instead of 15 lines of inline regex with import-inside-method (DRY, same as BlueBubbles/SMS) - format_message() now wired into send() pipeline
… shared strip_markdown Improvements from our earlier NousResearch#8269 salvage work applied to NousResearch#7616: - Platform token lock: acquire_scoped_lock/release_scoped_lock prevents two profiles from double-connecting the same QQ bot simultaneously - Send retry with exponential backoff (3 attempts, 1s/2s/4s) with permanent vs transient error classification (matches Telegram pattern) - Proper long-message splitting via truncate_message() instead of hard-truncating at MAX_MESSAGE_LENGTH (preserves code blocks, adds 1/N) - REST-based one-shot send in send_message_tool — uses QQ Bot REST API directly with httpx instead of creating a full WebSocket adapter per message (fixes the connect→send race condition) - Use shared strip_markdown() from helpers.py instead of 15 lines of inline regex with import-inside-method (DRY, same as BlueBubbles/SMS) - format_message() now wired into send() pipeline
… shared strip_markdown Improvements from our earlier NousResearch#8269 salvage work applied to NousResearch#7616: - Platform token lock: acquire_scoped_lock/release_scoped_lock prevents two profiles from double-connecting the same QQ bot simultaneously - Send retry with exponential backoff (3 attempts, 1s/2s/4s) with permanent vs transient error classification (matches Telegram pattern) - Proper long-message splitting via truncate_message() instead of hard-truncating at MAX_MESSAGE_LENGTH (preserves code blocks, adds 1/N) - REST-based one-shot send in send_message_tool — uses QQ Bot REST API directly with httpx instead of creating a full WebSocket adapter per message (fixes the connect→send race condition) - Use shared strip_markdown() from helpers.py instead of 15 lines of inline regex with import-inside-method (DRY, same as BlueBubbles/SMS) - format_message() now wired into send() pipeline
… shared strip_markdown Improvements from our earlier NousResearch#8269 salvage work applied to NousResearch#7616: - Platform token lock: acquire_scoped_lock/release_scoped_lock prevents two profiles from double-connecting the same QQ bot simultaneously - Send retry with exponential backoff (3 attempts, 1s/2s/4s) with permanent vs transient error classification (matches Telegram pattern) - Proper long-message splitting via truncate_message() instead of hard-truncating at MAX_MESSAGE_LENGTH (preserves code blocks, adds 1/N) - REST-based one-shot send in send_message_tool — uses QQ Bot REST API directly with httpx instead of creating a full WebSocket adapter per message (fixes the connect→send race condition) - Use shared strip_markdown() from helpers.py instead of 15 lines of inline regex with import-inside-method (DRY, same as BlueBubbles/SMS) - format_message() now wired into send() pipeline
What does this PR do?
Adds a QQ Bot platform adapter using the Official QQ Bot API v2 (
api.sgroup.qq.com). The adapter connects to QQ's WebSocket Gateway for inbound events and uses the REST API for outbound messages, media uploads, and voice STT.This enables Hermes users to interact with the AI assistant via QQ — China's largest messaging platform with 500M+ monthly active users.
Related Issue
N/A (new platform integration)
Type of Change
Changes Made
New files
gateway/platforms/qq.py— QQAdapter (~1898 lines): WebSocket gateway, REST API, voice STT, close code handling, ACLtests/gateway/test_qq.py— 65 unit testswebsite/docs/user-guide/messaging/qq.md— Setup guide with overview, configuration, env vars, STT, troubleshootingIntegration (append-only, zero modifications to existing adapter logic)
gateway/platforms/__init__.py— QQAdapter import/exportgateway/config.py—Platform.QQenum + env var override + connected platform detectiongateway/run.py— Adapter registration + allowlist/allow-all mapshermes_cli/config.py— QQ env vars in_EXTRA_ENV_KEYShermes_cli/gateway.py— QQ entry in_PLATFORMSsetup menuhermes_cli/setup.py—_setup_qq()+_GATEWAY_PLATFORMSregistration +any_messaging+missing_homehermes_cli/status.py— QQ in platform status displayhermes_cli/tools_config.py— QQ toolset label + auto-enablement detectiontools/send_message_tool.py— QQ in platform map +_send_qq()functiontoolsets.py—hermes-qqtoolset + added tohermes-gatewayincludescron/scheduler.py— QQ in_KNOWN_DELIVERY_PLATFORMS+ platform mapDocumentation updates
AGENTS.md— QQ added to platforms list in directory treecli-config.yaml.example— QQ added to platform keys comment andplatform_toolsetsdefaultswebsite/docs/reference/environment-variables.md— QQ env vars documentedwebsite/docs/user-guide/messaging/index.md— QQ added to comparison table, architecture diagram, toolsets table, nav linksHow to Test
hermes setup gateway— select QQ, enter App ID + Secret from q.qq.comhermes gateway run— verify WebSocket connects and READY event is receivedpython -m pytest tests/gateway/test_qq.py -q— 65 tests passpython -m pytest tests/ -q— no regressions (all failures are pre-existing on upstream/main)Checklist
Code
feat: add QQ Bot platform adapter (Official API v2))pytest tests/ -qand all tests pass (65 QQ-specific pass; full suite: no new failures)Documentation & Housekeeping
website/docs/user-guide/messaging/qq.md— new setup guidewebsite/docs/user-guide/messaging/index.md— comparison table, architecture, toolsets, navwebsite/docs/reference/environment-variables.md— QQ env varscli-config.yaml.example— QQ added to platform keys and defaultsAGENTS.md— QQ added to platforms list~/.hermes, optional deps via try/exceptsend_message)For New Skills
N/A (platform adapter, not a skill)