文件最后提交记录最后更新时间
feat(qqbot): add inline-keyboard approvals and update prompts The QQ Bot v2 API supports inline keyboards on outbound messages. When a user taps a button, the platform dispatches an INTERACTION_CREATE gateway event; the bot ACKs it via PUT /interactions/{id} and decodes the button's data payload to route the click. This commit adds: New module gateway/platforms/qqbot/keyboards.py - Inline-keyboard dataclasses (InlineKeyboard, KeyboardRow, KeyboardButton, KeyboardButtonAction, KeyboardButtonRenderData, KeyboardButtonPermission) that serialize to the JSON shape the QQ API expects. - build_approval_keyboard(session_key) — 3-button layout: ✅ 允许一次 / ⭐ 始终允许 / ❌ 拒绝, all sharing group_id='approval' so clicking one greys out the rest. - build_update_prompt_keyboard() — Yes/No keyboard for update confirms. - parse_approval_button_data() / parse_update_prompt_button_data() — decode the button_data payload from INTERACTION_CREATE. approve:<session_key>:<decision> (decision = allow-once|allow-always|deny) update_prompt:<answer> (answer = y|n) - build_approval_text(ApprovalRequest) — markdown renderer for the surrounding message body (exec-approval and plugin-approval variants, with severity icons 🔴/🔵/🟡). - parse_interaction_event(raw) → InteractionEvent dataclass — normalizes the nested raw payload (id / scene / openids / button_data / etc.). Adapter changes (gateway/platforms/qqbot/adapter.py) - _dispatch_payload routes INTERACTION_CREATE → _on_interaction. - _on_interaction parses the event, ACKs via PUT /interactions/{id}, then invokes a user-registered interaction callback. Exceptions from the callback are caught and logged (never propagate into the WS loop). - set_interaction_callback(cb) lets gateway wiring register a routing handler that inspects button_data and resolves the corresponding pending approval / update prompt. - _send_c2c_text / _send_group_text now accept an optional keyboard kwarg and append it to the outbound body. - send_with_keyboard(chat_id, content, keyboard, reply_to=None) — public helper that sends a single short message with a keyboard attached. Does NOT chunk-split (a keyboard message has one interactive surface). Guild chats are rejected non-retryably — they don't support keyboards. - send_approval_request(chat_id, ApprovalRequest, reply_to=None) + send_update_prompt(chat_id, content, reply_to=None) — convenience wrappers over send_with_keyboard. Tests 27 new unit tests under TestApprovalButtonData, TestUpdatePromptButtonData, TestBuildApprovalKeyboard, TestBuildUpdatePromptKeyboard, TestBuildApprovalText, TestInteractionEventParsing, and TestAdapterInteractionDispatch. Cover: - Button-data round-trip (build → parse returns original session/decision) - Keyboard JSON shape + mutual-exclusion group_id - Exec vs plugin approval text templates + severity icons - Interaction event parsing (c2c / group / guild scene codes) - _on_interaction end-to-end: ACK invoked, callback receives parsed event, callback exceptions are swallowed, missing id skips ACK, no registered callback is harmless. Full qqbot suite: 118 passed (72 existing + 19 chunked + 27 keyboards). Co-authored-by: WideLee <limkuan24@gmail.com> 28 天前
fix(gateway): keep QQBot reconnect loop alive 21 天前
chore: ruff auto-fix PLR6201 — tuple → set in membership tests (#23937) Replace with for all literal-tuple membership tests. Set lookup is O(1) vs O(n) for tuple — consistent micro-optimization across the codebase. 608 instances fixed via ruff --fix --unsafe-fixes, 0 remaining. 133 files, +626/-626 (net zero).24 天前
refactor(qqbot): split qqbot.py into package & add QR scan-to-configure 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 1 个月前
refactor(qqbot): split qqbot.py into package & add QR scan-to-configure 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 1 个月前
feat(qqbot): add inline-keyboard approvals and update prompts The QQ Bot v2 API supports inline keyboards on outbound messages. When a user taps a button, the platform dispatches an INTERACTION_CREATE gateway event; the bot ACKs it via PUT /interactions/{id} and decodes the button's data payload to route the click. This commit adds: New module gateway/platforms/qqbot/keyboards.py - Inline-keyboard dataclasses (InlineKeyboard, KeyboardRow, KeyboardButton, KeyboardButtonAction, KeyboardButtonRenderData, KeyboardButtonPermission) that serialize to the JSON shape the QQ API expects. - build_approval_keyboard(session_key) — 3-button layout: ✅ 允许一次 / ⭐ 始终允许 / ❌ 拒绝, all sharing group_id='approval' so clicking one greys out the rest. - build_update_prompt_keyboard() — Yes/No keyboard for update confirms. - parse_approval_button_data() / parse_update_prompt_button_data() — decode the button_data payload from INTERACTION_CREATE. approve:<session_key>:<decision> (decision = allow-once|allow-always|deny) update_prompt:<answer> (answer = y|n) - build_approval_text(ApprovalRequest) — markdown renderer for the surrounding message body (exec-approval and plugin-approval variants, with severity icons 🔴/🔵/🟡). - parse_interaction_event(raw) → InteractionEvent dataclass — normalizes the nested raw payload (id / scene / openids / button_data / etc.). Adapter changes (gateway/platforms/qqbot/adapter.py) - _dispatch_payload routes INTERACTION_CREATE → _on_interaction. - _on_interaction parses the event, ACKs via PUT /interactions/{id}, then invokes a user-registered interaction callback. Exceptions from the callback are caught and logged (never propagate into the WS loop). - set_interaction_callback(cb) lets gateway wiring register a routing handler that inspects button_data and resolves the corresponding pending approval / update prompt. - _send_c2c_text / _send_group_text now accept an optional keyboard kwarg and append it to the outbound body. - send_with_keyboard(chat_id, content, keyboard, reply_to=None) — public helper that sends a single short message with a keyboard attached. Does NOT chunk-split (a keyboard message has one interactive surface). Guild chats are rejected non-retryably — they don't support keyboards. - send_approval_request(chat_id, ApprovalRequest, reply_to=None) + send_update_prompt(chat_id, content, reply_to=None) — convenience wrappers over send_with_keyboard. Tests 27 new unit tests under TestApprovalButtonData, TestUpdatePromptButtonData, TestBuildApprovalKeyboard, TestBuildUpdatePromptKeyboard, TestBuildApprovalText, TestInteractionEventParsing, and TestAdapterInteractionDispatch. Cover: - Button-data round-trip (build → parse returns original session/decision) - Keyboard JSON shape + mutual-exclusion group_id - Exec vs plugin approval text templates + severity icons - Interaction event parsing (c2c / group / guild scene codes) - _on_interaction end-to-end: ACK invoked, callback receives parsed event, callback exceptions are swallowed, missing id skips ACK, no registered callback is harmless. Full qqbot suite: 118 passed (72 existing + 19 chunked + 27 keyboards). Co-authored-by: WideLee <limkuan24@gmail.com> 28 天前
refactor(qqbot): migrate qr onboard flow to sync + consolidate into onboard.py - Replace async create_bind_task/poll_bind_result with synchronous httpx.Client equivalents, eliminating manual event loop management - Move _render_qr and full qr_register() entry-point into onboard.py, mirroring the Feishu onboarding pattern - Remove _qqbot_render_qr and _qqbot_qr_flow from gateway.py (~90 lines); call site becomes a single qr_register() import - Fix potential segfault: previous code called loop.close() in the EXPIRED branch and again in the finally block (double-close crashed under uvloop) 1 个月前
refactor(qqbot): split qqbot.py into package & add QR scan-to-configure 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 1 个月前