Skip to content

feat(qqbot): QR scan-to-configure setup + package split (salvage of #11582)#11831

Merged
teknium1 merged 11 commits into
mainfrom
hermes/hermes-ac9a40aa
Apr 17, 2026
Merged

feat(qqbot): QR scan-to-configure setup + package split (salvage of #11582)#11831
teknium1 merged 11 commits into
mainfrom
hermes/hermes-ac9a40aa

Conversation

@teknium1

Copy link
Copy Markdown
Contributor

Summary

QR-code scan-to-configure flow for QQBot setup, plus a clean package split of the 1960-line gateway/platforms/qqbot.py into submodules. Salvaged from @WideLee's #11582 onto current main, with back-compat added for the QQ_HOME_CHANNELQQBOT_HOME_CHANNEL rename so existing users don't silently lose their home channel.

Closes #11582.

Changes

Feature (WideLee's commits, cherry-picked with authorship preserved)

  • gateway/platforms/qqbot.pygateway/platforms/qqbot/ package: adapter.py, constants.py, utils.py, crypto.py, onboard.py, __init__.py (old import paths preserved via re-exports)
  • QR scan setup: user picks "Scan QR code" in _setup_qqbot, terminal renders a QR via qrcode.print_ascii, backend polls q.qq.com for scan completion, server-returned client_secret is AES-256-GCM decrypted locally with a key the CLI generated
  • Env var rename QQ_HOME_CHANNEL[_NAME]QQBOT_HOME_CHANNEL[_NAME] for consistency with the qqbot platform key
  • self._log_tag (format QQBot:<app_id>) replaces hardcoded "QQBot" in all 79 logger calls for multi-instance disambiguation
  • STT log levels lowered to reduce noise

Follow-up fixes (our commit on top)

  • Back-compat for the env var renamegateway/config.py reads the new name, falls back to the legacy one with a one-shot deprecation warning. cron/scheduler.py gains a _LEGACY_HOME_TARGET_ENV_VARS table so cron deliveries don't silently drop for existing users. hermes_cli/status.py and hermes_cli/setup.py honor both names. _EXTRA_ENV_KEYS keeps the legacy entries so .env sanitizer recognizes them.
  • qrcode stays out of core deps — removed from [project.dependencies] and requirements.txt. It remains in the messaging/dingtalk/feishu extras where it already lived. _qqbot_render_qr already handled the missing-dep case gracefully with a "pip install qrcode" tip.
  • Kept _detect_message_type as @staticmethod — it doesn't use self; the PR converted it to instance method purely to force a test change. Reverted both.
  • Reset uv.lock to current origin/main — the PR's stale lock also included unrelated atroposlib source-URL and version bumps.

Validation

Before (PR #11582 as-is) After (this salvage)
Existing user with QQ_HOME_CHANNEL set Gateway silently ignores it → home_channel=None Gateway reads it + logs deprecation warning once
Existing user, cron deliver=origin fallback Works (cron still mapped to old name) → but gateway broken Both gateway and cron pick up legacy name
Fresh user with QQBOT_HOME_CHANNEL set Gateway works, cron silently broken (cron mapped to old name) Both gateway and cron use new name
qrcode not installed Core dep ⇒ install fails Gracefully falls back to URL + "pip install qrcode" tip
Targeted tests (qqbot + cron + hermes_cli) Not all run under current main's structure 296 passed, 4 skipped

E2E scenarios exercised live against real gateway.config._apply_env_overrides and cron.scheduler._resolve_delivery_target:

  1. Existing user (legacy env var only) — gateway and cron both pick up the chat_id; deprecation warning logged
  2. Fresh user (new env var only) — gateway and cron use new name, no warning
  3. Both set — new name wins on both surfaces
  4. QR flow — full create_bind_task → poll-pending → poll-completed → decrypt loop verified against mocked q.qq.com; crypto roundtrip correct, wrong key / tampered tag rejected; QR code decodes back to exact URL
  5. QR-fail → manual fallback — user isn't trapped if QR setup is cancelled

Credits

Substantive work by @WideLee (commits 39ff62a5 through 552fc844, authorship preserved). Closes the original #11582.

WideLee and others added 11 commits April 17, 2026 14:39
…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)
- 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
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).
@github-actions

Copy link
Copy Markdown
Contributor

⚠️ Supply Chain Risk Detected

This PR contains patterns commonly associated with supply chain attacks. This does not mean the PR is malicious — but these patterns require careful human review before merging.

⚠️ WARNING: base64 encoding/decoding detected

Base64 has legitimate uses (images, JWT, etc.) but is also commonly used to obfuscate malicious payloads. Verify the usage is appropriate.

Matches (first 20):

2077:+    return base64.b64encode(os.urandom(32)).decode()
2098:+    key = base64.b64decode(key_base64)
2099:+    raw = base64.b64decode(encrypted_base64)

⚠️ WARNING: Install hook files modified

These files can execute code during package installation or interpreter startup.

Files:

hermes_cli/setup.py

Automated scan triggered by supply-chain-audit. If this is a false positive, a maintainer can approve after manual review.

@teknium1 teknium1 merged commit d2206c6 into main Apr 17, 2026
8 checks passed
@teknium1 teknium1 deleted the hermes/hermes-ac9a40aa branch April 17, 2026 22:31
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