Skip to content

feat(gateway): add QQBot platform adapter via qq-botpy SDK#8269

Closed
xing8star wants to merge 5 commits into
NousResearch:mainfrom
xing8star:main
Closed

feat(gateway): add QQBot platform adapter via qq-botpy SDK#8269
xing8star wants to merge 5 commits into
NousResearch:mainfrom
xing8star:main

Conversation

@xing8star

@xing8star xing8star commented Apr 12, 2026

Copy link
Copy Markdown

Integrate QQ (Tencent) as a supported messaging platform in the Hermes gateway, enabling bot interactions on guild channels, direct messages, C2C (single-user apps), and group @-messages.

Changes:

  • Add gateway/platforms/qqbot.py — full QQBotAdapter implementation built on the official qq-botpy SDK, supporting four message scenes (guild_at, direct_message, c2c, group_at) with WebSocket connection lifecycle, message deduplication, auto-reconnect backoff, and an embedded MessageHub for external tool / MCP polling access.

  • Register Platform.QQBOT enum in gateway/config.py with env-var overrides (QQBOT_APPID, QQBOT_SECRET, QQBOT_HOME_CHANNEL, QQBOT_ALLOWED_USERS, QQBOT_ALLOW_ALL_USERS).

  • Wire adapter factory in gateway/run.py with requirement checks and per-platform allowlist / allow-all user config maps.

  • Add interactive CLI setup flow (_setup_qqbot) in hermes_cli/gateway.py with platform definition (emoji: cat), AppID/BotSecret credential prompts, user allowlist, and open-access toggle.

  • Register "qqbot" platform info in hermes_cli/platforms.py and status check vars in hermes_cli/status.py.

  • Add "hermes-qqbot" toolset to toolsets.py and include it in the gateway union toolset.

  • Extend send_message_tool.py with QQBot target format support ('qqbot:channel_id'), max length constant (2000), and _send_qqbot() dispatch that instantiates the adapter for proactive outbound delivery.

  • Add QQ-specific prompt hint in agent/prompt_builder.py (plain-text, no markdown).

  • Wire QQBot delivery target in cron/scheduler.py.

  • Add qq-botpy>=1.2.1 to core dependencies and qq-botpy>=1.2.0,<2 to optional [qqbot] group in pyproject.toml.

What does this PR do?

Adds QQBot (QQ / Tencent QQ) as a fully supported messaging platform in the Hermes gateway, enabling AI agent interactions on China's most popular social platform. This brings Hermes to millions of Chinese users who communicate primarily via QQ.

The implementation follows the same adapter pattern established by existing platforms (Telegram, Discord, WhatsApp, BlueBubbles, etc.) and integrates with the official qq-botpy SDK for WebSocket-based real-time messaging.

Key capabilities:

  • Four message scenes: guild channel @-mentions, direct messages (DM), C2C (single-user app), and group @-messages
  • Full gateway integration: platform enum, config loading from env vars, CLI interactive setup (hermes gateway setup), allowlist/allow-all user authorization
  • Proactive outbound delivery: send_message tool supports qqbot:channel_id target format for cron jobs and notifications
  • Embedded MessageHub: coroutine-safe event bus enabling MCP / external tool polling and reply dispatch (mirrors the qq-bot-mcp hub interface)
  • Production-ready: message deduplication (5-min window), auto-reconnect with exponential backoff [2, 5, 10, 30, 60]s, C2C/group msg_seq monotonic counter, graceful disconnect

Why this approach?

The official qq-botpy SDK is the only blessed Python library for the QQ Bot Open Platform API. It handles WebSocket connection management, intent registration, and the four distinct message scenes — each with different reply APIs (post_message, post_dms, message.reply). Building on this SDK (rather than raw HTTP) ensures compatibility with future QQ platform updates.

Related Issue

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✨ New feature (non-breaking change that adds functionality)
  • 🔒 Security fix
  • 📝 Documentation update
  • ✅ Tests (adding or improving test coverage)
  • ♻️ Refactor (no behavior change)
  • 🎯 New skill (bundled or hub)

Changes Made

New files:

  • gateway/platforms/qqbot.py — Full QQBotAdapter (~580 lines): botpy.Client subclass, _MessageHub event bus, connect/disconnect lifecycle, send/send_image/get_chat_info, four scene handlers (guild_at, direct_message, c2c, group_at), dedup, msg_seq management, responder cache for group/C2C reply routing

Modified files:

  • gateway/config.py — Add Platform.QQBOT enum value; connected-platform detection (appid + token); env var overrides (QQBOT_APPID, QQBOT_SECRET/QQBOT_TOKEN fallback, QQBOT_HOME_CHANNEL)
  • gateway/run.py — Adapter factory branch for QQBot; allowlist/allow-all env var maps (QQBOT_ALLOWED_USERS, QQBOT_ALLOW_ALL_USERS)
  • hermes_cli/gateway.py — Platform definition entry (emoji: cat emoji); full _setup_qqbot() interactive wizard (~90 lines): dependency check, AppID/BotSecret prompts, user allowlist, open-access toggle, home channel
  • hermes_cli/platforms.py — Register "qqbot" -> PlatformInfo(label="QQBot", default_toolset="hermes-qqbot")
  • hermes_cli/status.py — Status check vars: ("QQBot", ("QQBOT_APPID", None))
  • toolsets.py — Add "hermes-qqbot" toolset definition; include it in "hermes-gateway" union includes list
  • tools/send_message_tool.py — Target format docstring add qqbot:channel_id; platform map add qqbot -> Platform.QQBOT; max length constant 2000; _send_qqbot() dispatch function (~25 lines)
  • agent/prompt_builder.pyPLATFORM_HINTS["qqbot"]: plain-text hint (no markdown on QQ)
  • cron/scheduler.py — Delivery platform map: "qqbot" -> Platform.QQBOT
  • pyproject.toml — Core dep: qq-botpy>=1.2.1; optional [qqbot] extra: qq-botpy>=1.2.0,<2

How to Test

  1. Prerequisites — create a QQ Bot application:

    • Go to https://q.qq.com/ → Bot Open Platform → Create a bot application
    • Copy the AppID and BotSecret from the credentials page
    • Enable intents: public_guild_messages, direct_message, public_messages
  2. Configure credentials:

    export QQBOT_APPID="your_appid"
    export QQBOT_SECRET="your_botsecret"
    # Or use CLI setup:
    hermes gateway setup
  3. Run gateway and verify connection:

    hermes gateway start
    # Expected log: "INFO gateway.platforms.qqbot" + "[QQBot] Bot ready"
  4. Test inbound messages (all four scenes):

    • Guild channel: @mention your bot in a guild channel → should receive and respond
    • DM: send a private message to the bot → should respond
    • C2C: if using single-user app mode, send a C2C message → should respond
    • Group: @mention bot in a group chat → should respond

Checklist

Code

  • I've read the Contributing Guide
  • My commit messages follow Conventional Commits (fix(scope):, feat(scope):, etc.)
  • I searched for existing PRs to make sure this isn't a duplicate
  • My PR contains only changes related to this fix/feature (no unrelated commits)
  • I've run pytest tests/ -q and all tests pass
  • I've added tests for my changes (required for bug fixes, strongly encouraged for features)
  • I've tested on my platform: WSL,Archlinux

Documentation & Housekeeping

  • I've updated relevant documentation (README, docs/, docstrings) — or N/A
  • I've updated cli-config.yaml.example if I added/changed config keys — or N/A
  • I've updated CONTRIBUTING.md or AGENTS.md if I changed architecture or workflows — or N/A
  • I've considered cross-platform impact (Windows, macOS) per the compatibility guide — or N/A
  • I've updated tool descriptions/schemas if I changed tool behavior — or N/A

Screenshots / Logs

屏幕截图 2026-04-12 164107

xing8star and others added 3 commits April 12, 2026 16:29
Integrate QQ (Tencent) as a supported messaging platform in the Hermes
gateway, enabling bot interactions on guild channels, direct messages,
C2C (single-user apps), and group @-messages.

Changes:

- Add gateway/platforms/qqbot.py — full QQBotAdapter implementation
  built on the official qq-botpy SDK, supporting four message scenes
  (guild_at, direct_message, c2c, group_at) with WebSocket connection
  lifecycle, message deduplication, auto-reconnect backoff, and an
  embedded MessageHub for external tool / MCP polling access.

- Register Platform.QQBOT enum in gateway/config.py with env-var
  overrides (QQBOT_APPID, QQBOT_SECRET, QQBOT_HOME_CHANNEL,
  QQBOT_ALLOWED_USERS, QQBOT_ALLOW_ALL_USERS).

- Wire adapter factory in gateway/run.py with requirement checks and
  per-platform allowlist / allow-all user config maps.

- Add interactive CLI setup flow (_setup_qqbot) in hermes_cli/gateway.py
  with platform definition (emoji: cat), AppID/BotSecret credential
  prompts, user allowlist, and open-access toggle.

- Register "qqbot" platform info in hermes_cli/platforms.py and status
  check vars in hermes_cli/status.py.

- Add "hermes-qqbot" toolset to toolsets.py and include it in the
  gateway union toolset.

- Extend send_message_tool.py with QQBot target format support
  ('qqbot:channel_id'), max length constant (2000), and _send_qqbot()
  dispatch that instantiates the adapter for proactive outbound delivery.

- Add QQ-specific prompt hint in agent/prompt_builder.py (plain-text,
  no markdown).

- Wire QQBot delivery target in cron/scheduler.py.

- Add qq-botpy>=1.2.1 to core dependencies and qq-botpy>=1.2.0,<2 to
  optional [qqbot] group in pyproject.toml.
@WideLee

WideLee commented Apr 13, 2026

Copy link
Copy Markdown
Contributor

Hi @xing8star 👋

I'm from the official QQ Bot team at Tencent. Really great to see this PR — a full-featured QQBot adapter following the existing platform patterns. Solid work!

We'd love to get involved and help push this PR to a mergeable state together. Could you leave your QQ number so we can connect directly? It'd be much easier to coordinate in real-time.

Looking forward to working together 🙏

teknium1 added a commit that referenced this pull request Apr 14, 2026
… 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
teknium1 added a commit that referenced this pull request Apr 14, 2026
… 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
@teknium1

Copy link
Copy Markdown
Contributor

Merged via PR #9364. We initially salvaged your qq-botpy SDK adapter, then rebuilt on top of #7616's raw API v2 approach (zero new deps). Your follow-up commit (branding unification, PLATFORM_HINTS, streaming fix) was cherry-picked and ships in the final adapter. Thank you @xing8star for the contribution and for the Tencent team's support.

ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
… 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
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
… 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
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
… 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
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
… 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
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
… 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
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