Skip to content

feat(qqbot): simplify setup with QR code scan-to-configure#11582

Closed
WideLee wants to merge 10 commits into
NousResearch:mainfrom
WideLee:feat/walli_20260415_qrcodeonboard
Closed

feat(qqbot): simplify setup with QR code scan-to-configure#11582
WideLee wants to merge 10 commits into
NousResearch:mainfrom
WideLee:feat/walli_20260415_qrcodeonboard

Conversation

@WideLee

@WideLee WideLee commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Refactors the QQBot platform adapter from a single monolithic file (gateway/platforms/qqbot.py) into a proper Python package (gateway/platforms/qqbot/), and adds a QR-code scan-to-configure onboard flow that lets new users set up their QQ Bot credentials by scanning a QR code in the terminal — no manual App ID / App Secret copy-pasting required.

Additionally:

  • Renames the QQ_HOME_CHANNEL / QQ_HOME_CHANNEL_NAME env vars to QQBOT_HOME_CHANNEL / QQBOT_HOME_CHANNEL_NAME for consistency with the platform key.
  • Replaces all hardcoded "QQBot" log tags with a self._log_tag property that includes the app_id, making multi-instance logs easier to distinguish.
  • Adjusts STT-related log levels to reduce noise during normal operation.

Related Issue

Fixes #

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

Package restructure

  • gateway/platforms/qqbot.pygateway/platforms/qqbot/adapter.py — original adapter moved into a package; all existing import paths preserved via __init__.py re-exports.
  • gateway/platforms/qqbot/__init__.py — re-exports QQAdapter, check_qq_requirements, _coerce_list and new onboard symbols.
  • gateway/platforms/qqbot/constants.py — shared constants extracted from the adapter (API URLs, timeouts, message types, media types, reconnect backoff, etc.).
  • gateway/platforms/qqbot/utils.pybuild_user_agent(), get_api_headers(), coerce_list() — shared HTTP and config helpers.
  • gateway/platforms/qqbot/crypto.py — AES-256-GCM key generation (generate_bind_key()) and decryption (decrypt_secret()) for scan-to-configure credential exchange.
  • gateway/platforms/qqbot/onboard.pycreate_bind_task() / poll_bind_result() / build_connect_url() — QR-code onboard API client calling q.qq.com.

QR-code onboard flow (new interactive setup)

  • hermes_cli/gateway.py — new _setup_qqbot() function with two setup paths:
    1. QR scan — generates a bind task, renders a QR code in the terminal (via qrcode library), polls for scan completion, decrypts returned credentials, and optionally prompts to add the scanner to the allow list and set as home channel.
    2. Manual — traditional App ID + App Secret prompt (fallback).
  • hermes_cli/setup.py — old _setup_qqbot() removed; now delegates to the gateway's _setup_qqbot().

Env var rename (QQ_HOME_CHANNELQQBOT_HOME_CHANNEL)

  • hermes_cli/config.py — updated _EXTRA_ENV_KEYS and OPTIONAL_ENV_VARS.
  • gateway/config.py — updated env override reads.
  • hermes_cli/status.py — updated status display.
  • hermes_cli/gateway.py — updated platform definition.

Logging improvements

  • gateway/platforms/qqbot/adapter.py — all 79 logger calls now use self._log_tag (format QQBot:<app_id>) instead of hardcoded "QQBot".
  • STT-related log messages adjusted to DEBUG level.

Dependency & docs

  • pyproject.toml / requirements.txt — added qrcode>=7.4,<9 dependency.
  • cron/scheduler.py — added "qqbot" to the cron delivery fallback platform list.
  • website/docs/reference/environment-variables.md — updated QQ_HOME_CHANNELQQBOT_HOME_CHANNEL.
  • website/docs/user-guide/messaging/qqbot.md — updated env var references.

How to Test

  1. Run hermes gateway setup, select QQ Bot, and choose "Scan QR code" — verify a QR code renders in the terminal and scanning it completes the credential exchange.
  2. Run hermes gateway setup, select QQ Bot, and choose "Enter manually" — verify the traditional manual flow still works.
  3. Confirm that QQBOT_HOME_CHANNEL is respected for cron delivery by setting it and triggering a scheduled job.
  4. Start the QQ Bot gateway and verify logs show [QQBot:<app_id>] prefixes instead of bare [QQBot].
  5. Run hermes status and verify the QQBot section reads QQBOT_HOME_CHANNEL.

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: macOS (darwin)

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

image

WideLee added 7 commits April 17, 2026 20:26
…re onboard flow

- Refactor gateway/platforms/qqbot.py into gateway/platforms/qqbot/ package:
  - adapter.py: core QQAdapter (unchanged logic, constants from shared module)
  - constants.py: shared constants (API URLs, timeouts, message types)
  - crypto.py: AES-256-GCM key generation and secret decryption
  - onboard.py: QR-code scan-to-configure API (create_bind_task, poll_bind_result)
  - utils.py: User-Agent builder, HTTP headers, config helpers
  - __init__.py: re-exports all public symbols for backward compatibility

- Add interactive QR-code setup flow in hermes_cli/gateway.py:
  - Terminal QR rendering via qrcode package (graceful fallback to URL)
  - Auto-refresh on QR expiry (up to 3 times)
  - AES-256-GCM encrypted credential exchange
  - DM security policy selection (pairing/allowlist/open)

- Update hermes_cli/setup.py to delegate to gateway's _setup_qqbot()
- Add qrcode>=7.4 dependency to pyproject.toml and requirements.txt
…just STT log levels

- Remove @staticmethod from _detect_message_type, _convert_silk_to_wav,
  _convert_raw_to_wav, _convert_ffmpeg_to_wav so they can use self._log_tag
- Replace all remaining hardcoded "QQBot" log args with self._log_tag
- Downgrade STT routine flow logs (download, convert, success) from info to debug
- Keep warning level for actual failures (STT failed, ffmpeg error, empty transcript)
@WideLee WideLee force-pushed the feat/walli_20260415_qrcodeonboard branch from 9d62dec to f65357e Compare April 17, 2026 12:26
WideLee added 3 commits April 17, 2026 20:29
- Re-export _ssrf_redirect_guard from __init__.py
- Fix _parse_json @staticmethod using self._log_tag
- Update test_detect_message_type to call as instance method
- Fix mock.patch path for httpx.AsyncClient in adapter submodule
teknium1 added a commit that referenced this pull request Apr 17, 2026
Follow-up to WideLee's salvaged PR #11582.

Back-compat for QQ_HOME_CHANNEL → QQBOT_HOME_CHANNEL rename:
  - gateway/config.py reads QQBOT_HOME_CHANNEL, falls back to QQ_HOME_CHANNEL
    with a one-shot deprecation warning so users on the old name aren't
    silently broken.
  - cron/scheduler.py: _HOME_TARGET_ENV_VARS['qqbot'] now maps to the new
    name; _get_home_target_chat_id falls back to the legacy name via a
    _LEGACY_HOME_TARGET_ENV_VARS table.
  - hermes_cli/status.py + hermes_cli/setup.py: honor both names when
    displaying or checking for missing home channels.
  - hermes_cli/config.py: keep legacy QQ_HOME_CHANNEL[_NAME] in
    _EXTRA_ENV_KEYS so .env sanitization still recognizes them.

Scope cleanup:
  - Drop qrcode from core dependencies and requirements.txt (remains in
    messaging/dingtalk/feishu extras). _qqbot_render_qr already degrades
    gracefully when qrcode is missing, printing a 'pip install qrcode' tip
    and falling back to URL-only display.
  - Restore @staticmethod on QQAdapter._detect_message_type (it doesn't
    use self). Revert the test change that was only needed when it was
    converted to an instance method.
  - Reset uv.lock to origin/main; the PR's stale lock also included
    unrelated changes (atroposlib source URL, hermes-agent version bump,
    fastapi additions) that don't belong.

Verified E2E:
  - Existing user (QQ_HOME_CHANNEL set): gateway + cron both pick up the
    legacy name; deprecation warning logs once.
  - Fresh user (QQBOT_HOME_CHANNEL set): gateway + cron use new name,
    no warning.
  - Both set: new name wins on both surfaces.

Targeted tests: 296 passed, 4 skipped (qqbot + cron + hermes_cli).
teknium1 added a commit that referenced this pull request Apr 17, 2026
Follow-up to WideLee's salvaged PR #11582.

Back-compat for QQ_HOME_CHANNEL → QQBOT_HOME_CHANNEL rename:
  - gateway/config.py reads QQBOT_HOME_CHANNEL, falls back to QQ_HOME_CHANNEL
    with a one-shot deprecation warning so users on the old name aren't
    silently broken.
  - cron/scheduler.py: _HOME_TARGET_ENV_VARS['qqbot'] now maps to the new
    name; _get_home_target_chat_id falls back to the legacy name via a
    _LEGACY_HOME_TARGET_ENV_VARS table.
  - hermes_cli/status.py + hermes_cli/setup.py: honor both names when
    displaying or checking for missing home channels.
  - hermes_cli/config.py: keep legacy QQ_HOME_CHANNEL[_NAME] in
    _EXTRA_ENV_KEYS so .env sanitization still recognizes them.

Scope cleanup:
  - Drop qrcode from core dependencies and requirements.txt (remains in
    messaging/dingtalk/feishu extras). _qqbot_render_qr already degrades
    gracefully when qrcode is missing, printing a 'pip install qrcode' tip
    and falling back to URL-only display.
  - Restore @staticmethod on QQAdapter._detect_message_type (it doesn't
    use self). Revert the test change that was only needed when it was
    converted to an instance method.
  - Reset uv.lock to origin/main; the PR's stale lock also included
    unrelated changes (atroposlib source URL, hermes-agent version bump,
    fastapi additions) that don't belong.

Verified E2E:
  - Existing user (QQ_HOME_CHANNEL set): gateway + cron both pick up the
    legacy name; deprecation warning logs once.
  - Fresh user (QQBOT_HOME_CHANNEL set): gateway + cron use new name,
    no warning.
  - Both set: new name wins on both surfaces.

Targeted tests: 296 passed, 4 skipped (qqbot + cron + hermes_cli).
@teknium1

Copy link
Copy Markdown
Contributor

Merged via #11831#11831

Your 10 commits were cherry-picked onto current main with your authorship preserved in git log. One follow-up commit from us on top addresses a few issues surfaced during E2E testing:

  • Back-compat for the QQ_HOME_CHANNELQQBOT_HOME_CHANNEL rename so existing users with the old name in their .env don't silently lose their home channel. Gateway reads the new name, falls back to legacy with a one-shot deprecation warning. Cron delivery fallback picks up either. Applied across gateway/config.py, cron/scheduler.py, hermes_cli/status.py, hermes_cli/setup.py.
  • Kept qrcode out of core dependencies (it's already in the messaging/dingtalk/feishu extras). Your _qqbot_render_qr wrapper already degrades gracefully with a "pip install qrcode" tip when it's missing, so promoting to core wasn't needed.
  • Reverted _detect_message_type to @staticmethod (it doesn't use self); test reverted to match.

QR crypto roundtrip, onboard flow, and both setup paths were E2E-verified before merge. Thanks for this — the scan-to-configure UX is a real improvement, and splitting that 2k-line file into submodules was overdue.

ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
Follow-up to WideLee's salvaged PR NousResearch#11582.

Back-compat for QQ_HOME_CHANNEL → QQBOT_HOME_CHANNEL rename:
  - gateway/config.py reads QQBOT_HOME_CHANNEL, falls back to QQ_HOME_CHANNEL
    with a one-shot deprecation warning so users on the old name aren't
    silently broken.
  - cron/scheduler.py: _HOME_TARGET_ENV_VARS['qqbot'] now maps to the new
    name; _get_home_target_chat_id falls back to the legacy name via a
    _LEGACY_HOME_TARGET_ENV_VARS table.
  - hermes_cli/status.py + hermes_cli/setup.py: honor both names when
    displaying or checking for missing home channels.
  - hermes_cli/config.py: keep legacy QQ_HOME_CHANNEL[_NAME] in
    _EXTRA_ENV_KEYS so .env sanitization still recognizes them.

Scope cleanup:
  - Drop qrcode from core dependencies and requirements.txt (remains in
    messaging/dingtalk/feishu extras). _qqbot_render_qr already degrades
    gracefully when qrcode is missing, printing a 'pip install qrcode' tip
    and falling back to URL-only display.
  - Restore @staticmethod on QQAdapter._detect_message_type (it doesn't
    use self). Revert the test change that was only needed when it was
    converted to an instance method.
  - Reset uv.lock to origin/main; the PR's stale lock also included
    unrelated changes (atroposlib source URL, hermes-agent version bump,
    fastapi additions) that don't belong.

Verified E2E:
  - Existing user (QQ_HOME_CHANNEL set): gateway + cron both pick up the
    legacy name; deprecation warning logs once.
  - Fresh user (QQBOT_HOME_CHANNEL set): gateway + cron use new name,
    no warning.
  - Both set: new name wins on both surfaces.

Targeted tests: 296 passed, 4 skipped (qqbot + cron + hermes_cli).
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
Follow-up to WideLee's salvaged PR NousResearch#11582.

Back-compat for QQ_HOME_CHANNEL → QQBOT_HOME_CHANNEL rename:
  - gateway/config.py reads QQBOT_HOME_CHANNEL, falls back to QQ_HOME_CHANNEL
    with a one-shot deprecation warning so users on the old name aren't
    silently broken.
  - cron/scheduler.py: _HOME_TARGET_ENV_VARS['qqbot'] now maps to the new
    name; _get_home_target_chat_id falls back to the legacy name via a
    _LEGACY_HOME_TARGET_ENV_VARS table.
  - hermes_cli/status.py + hermes_cli/setup.py: honor both names when
    displaying or checking for missing home channels.
  - hermes_cli/config.py: keep legacy QQ_HOME_CHANNEL[_NAME] in
    _EXTRA_ENV_KEYS so .env sanitization still recognizes them.

Scope cleanup:
  - Drop qrcode from core dependencies and requirements.txt (remains in
    messaging/dingtalk/feishu extras). _qqbot_render_qr already degrades
    gracefully when qrcode is missing, printing a 'pip install qrcode' tip
    and falling back to URL-only display.
  - Restore @staticmethod on QQAdapter._detect_message_type (it doesn't
    use self). Revert the test change that was only needed when it was
    converted to an instance method.
  - Reset uv.lock to origin/main; the PR's stale lock also included
    unrelated changes (atroposlib source URL, hermes-agent version bump,
    fastapi additions) that don't belong.

Verified E2E:
  - Existing user (QQ_HOME_CHANNEL set): gateway + cron both pick up the
    legacy name; deprecation warning logs once.
  - Fresh user (QQBOT_HOME_CHANNEL set): gateway + cron use new name,
    no warning.
  - Both set: new name wins on both surfaces.

Targeted tests: 296 passed, 4 skipped (qqbot + cron + hermes_cli).
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
Follow-up to WideLee's salvaged PR NousResearch#11582.

Back-compat for QQ_HOME_CHANNEL → QQBOT_HOME_CHANNEL rename:
  - gateway/config.py reads QQBOT_HOME_CHANNEL, falls back to QQ_HOME_CHANNEL
    with a one-shot deprecation warning so users on the old name aren't
    silently broken.
  - cron/scheduler.py: _HOME_TARGET_ENV_VARS['qqbot'] now maps to the new
    name; _get_home_target_chat_id falls back to the legacy name via a
    _LEGACY_HOME_TARGET_ENV_VARS table.
  - hermes_cli/status.py + hermes_cli/setup.py: honor both names when
    displaying or checking for missing home channels.
  - hermes_cli/config.py: keep legacy QQ_HOME_CHANNEL[_NAME] in
    _EXTRA_ENV_KEYS so .env sanitization still recognizes them.

Scope cleanup:
  - Drop qrcode from core dependencies and requirements.txt (remains in
    messaging/dingtalk/feishu extras). _qqbot_render_qr already degrades
    gracefully when qrcode is missing, printing a 'pip install qrcode' tip
    and falling back to URL-only display.
  - Restore @staticmethod on QQAdapter._detect_message_type (it doesn't
    use self). Revert the test change that was only needed when it was
    converted to an instance method.
  - Reset uv.lock to origin/main; the PR's stale lock also included
    unrelated changes (atroposlib source URL, hermes-agent version bump,
    fastapi additions) that don't belong.

Verified E2E:
  - Existing user (QQ_HOME_CHANNEL set): gateway + cron both pick up the
    legacy name; deprecation warning logs once.
  - Fresh user (QQBOT_HOME_CHANNEL set): gateway + cron use new name,
    no warning.
  - Both set: new name wins on both surfaces.

Targeted tests: 296 passed, 4 skipped (qqbot + cron + hermes_cli).
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
Follow-up to WideLee's salvaged PR NousResearch#11582.

Back-compat for QQ_HOME_CHANNEL → QQBOT_HOME_CHANNEL rename:
  - gateway/config.py reads QQBOT_HOME_CHANNEL, falls back to QQ_HOME_CHANNEL
    with a one-shot deprecation warning so users on the old name aren't
    silently broken.
  - cron/scheduler.py: _HOME_TARGET_ENV_VARS['qqbot'] now maps to the new
    name; _get_home_target_chat_id falls back to the legacy name via a
    _LEGACY_HOME_TARGET_ENV_VARS table.
  - hermes_cli/status.py + hermes_cli/setup.py: honor both names when
    displaying or checking for missing home channels.
  - hermes_cli/config.py: keep legacy QQ_HOME_CHANNEL[_NAME] in
    _EXTRA_ENV_KEYS so .env sanitization still recognizes them.

Scope cleanup:
  - Drop qrcode from core dependencies and requirements.txt (remains in
    messaging/dingtalk/feishu extras). _qqbot_render_qr already degrades
    gracefully when qrcode is missing, printing a 'pip install qrcode' tip
    and falling back to URL-only display.
  - Restore @staticmethod on QQAdapter._detect_message_type (it doesn't
    use self). Revert the test change that was only needed when it was
    converted to an instance method.
  - Reset uv.lock to origin/main; the PR's stale lock also included
    unrelated changes (atroposlib source URL, hermes-agent version bump,
    fastapi additions) that don't belong.

Verified E2E:
  - Existing user (QQ_HOME_CHANNEL set): gateway + cron both pick up the
    legacy name; deprecation warning logs once.
  - Fresh user (QQBOT_HOME_CHANNEL set): gateway + cron use new name,
    no warning.
  - Both set: new name wins on both surfaces.

Targeted tests: 296 passed, 4 skipped (qqbot + cron + hermes_cli).
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
Follow-up to WideLee's salvaged PR NousResearch#11582.

Back-compat for QQ_HOME_CHANNEL → QQBOT_HOME_CHANNEL rename:
  - gateway/config.py reads QQBOT_HOME_CHANNEL, falls back to QQ_HOME_CHANNEL
    with a one-shot deprecation warning so users on the old name aren't
    silently broken.
  - cron/scheduler.py: _HOME_TARGET_ENV_VARS['qqbot'] now maps to the new
    name; _get_home_target_chat_id falls back to the legacy name via a
    _LEGACY_HOME_TARGET_ENV_VARS table.
  - hermes_cli/status.py + hermes_cli/setup.py: honor both names when
    displaying or checking for missing home channels.
  - hermes_cli/config.py: keep legacy QQ_HOME_CHANNEL[_NAME] in
    _EXTRA_ENV_KEYS so .env sanitization still recognizes them.

Scope cleanup:
  - Drop qrcode from core dependencies and requirements.txt (remains in
    messaging/dingtalk/feishu extras). _qqbot_render_qr already degrades
    gracefully when qrcode is missing, printing a 'pip install qrcode' tip
    and falling back to URL-only display.
  - Restore @staticmethod on QQAdapter._detect_message_type (it doesn't
    use self). Revert the test change that was only needed when it was
    converted to an instance method.
  - Reset uv.lock to origin/main; the PR's stale lock also included
    unrelated changes (atroposlib source URL, hermes-agent version bump,
    fastapi additions) that don't belong.

Verified E2E:
  - Existing user (QQ_HOME_CHANNEL set): gateway + cron both pick up the
    legacy name; deprecation warning logs once.
  - Fresh user (QQBOT_HOME_CHANNEL set): gateway + cron use new name,
    no warning.
  - Both set: new name wins on both surfaces.

Targeted tests: 296 passed, 4 skipped (qqbot + cron + hermes_cli).
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.

2 participants