Skip to content

fix(qqbot): add .md and other doc extensions to extract_media() regex#22492

Open
Allonz wants to merge 12 commits into
NousResearch:mainfrom
Allonz:feat/qqbot-media-md-support
Open

fix(qqbot): add .md and other doc extensions to extract_media() regex#22492
Allonz wants to merge 12 commits into
NousResearch:mainfrom
Allonz:feat/qqbot-media-md-support

Conversation

@Allonz

@Allonz Allonz commented May 9, 2026

Copy link
Copy Markdown

Summary

MEDIA: tag extraction silently ignored .md, .json, .xml, .html, .log, .yaml, .yml, and .toml files because the extension allowlist in BasePlatformAdapter.extract_media() didn't include these formats.

This prevented the gateway's _deliver_media_tags_streaming() and the send_message tool from delivering document files produced by agents (e.g. markdown reports, logs, config files) via QQBot and other platforms that use send_document().

Root Cause

The regex in gateway/platforms/base.py:1928 had a hardcoded extension alternation that only matched:

png|jpe?g|gif|webp|mp4|mov|avi|mkv|webm|ogg|opus|mp3|wav|m4a|flac|
epub|pdf|zip|rar|7z|docx?|xlsx?|pptx?|txt|csv|apk|ipa

Common document/code/text formats were missing - most notably .md (markdown), which Hermes agents frequently generate.

Fix

Added md|json|xml|html?|log|yaml|yml|toml to the regex extension alternation (1 line change in gateway/platforms/base.py).

Test Results

Existing tests (no regression)

  • test_send_image_file.py: 23 passed
  • test_qqbot.py + test_qqbot_zombie_fix.py + test_media_extraction.py: 160 passed
  • Total: 183 existing tests pass

Manual verification

All new extensions extracted correctly:

  • .md, .json, .xml, .html, .log, .yaml, .yml, .toml

Existing extensions still work:

  • .pdf, .docx, .png, .jpg no regression

Impact

Low risk - purely additive: no existing extensions removed, no logic changed.

Allonz added 12 commits May 1, 2026 16:51
- Add media file upload support to _send_qqbot function
- Support chat_type detection from target format (c2c:/group:/guild:)
- Upload media via QQ Bot v2 API (/v2/users/{openid}/files, /v2/groups/{group_openid}/files)
- Map file extensions to QQ Bot file_type (1=image, 2=video, 3=voice, 4=file)
- Include media in message payload via 'file_info' field
- Update error messages to include qqbot in supported platforms
- Update schema description with qqbot target format examples
… attempts

Fix 4 issues in QQBot adapter reconnect logic:

1. [P0] _listen_loop exit now calls _set_fatal_error() to notify Gateway
   When reconnect attempts are exhausted, the adapter now sets a retryable
   fatal error so _platform_reconnect_watcher can take over. Previously
   the listen loop would silently die, leaving QQBot in a zombie state
   where the process is alive but no messages are received.

2. [P1] CLOSED/ERROR WS events now raise QQCloseError instead of plain
   RuntimeError, preserving close code/reason for proper error classification.

3. [P1] Added 15s cooldown after reconnect failure to give QQ server time
   to clean up the old session before the next attempt.

4. [P2] Moved _heartbeat_interval reset inside _reconnect() try block
   so it only resets after a successful connection, not on failure.
…a support)

Keep both QQBot and Feishu native media delivery blocks. Preserve
QQBot's full chat_type routing and base64 file upload logic from
HEAD, while incorporating Feishu's media support and thread_id
parameter from upstream/main. Update error/warning strings to
list both platforms.
…ping/pong

Bug 5: _reconnect() opens a new WebSocket but never recreates _heartbeat_task.
The old heartbeat task is orphaned on the dead connection — no heartbeat is
sent on the new one. QQ server closes the connection after 60s with code=None
(no close code because the server drops it cleanly). Reconnect succeeds,
but the death loop repeats every 60s: connect → 60s silence → disconnect
→ reconnect → repeat.

Changes:
- _open_ws(): add heartbeat=20 to aiohttp ClientWebSocketResponse for
  WS-level ping/pong, preventing idle disconnects at the transport layer
- _reconnect(): cancel old _heartbeat_task before opening new WebSocket
- _reconnect(): create_task(_heartbeat_loop()) after successful reconnect
MEDIA: tag extraction silently ignored .md, .json, .xml, .html, .log,
.yaml, .yml, and .toml files because the extension allowlist in
BasePlatformAdapter.extract_media() didn't include these formats.

This prevented the gateway's _deliver_media_tags_streaming() and the
send_message tool from delivering document files produce d by agents
(e.g. markdown reports, logs, config files) via QQBot and other
platforms that use send_document().

Fix: add md|json|xml|html?|log|yaml|yml|toml to the regex extension
alternation.  All 183 existing tests pass with no regressions.

Verified:
  - MEDIA:/path/to/file.md  → extracted correctly
  - MEDIA:/path/to/file.json → extracted correctly
  - Existing formats (pdf, docx, png, etc.) → no regression
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery platform/qqbot QQ Bot adapter labels May 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/qqbot QQ Bot adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants