fix(weixin): extract and deliver MEDIA: attachments in normal send() path

The Weixin adapter's send() method previously split and delivered the
raw response text without first extracting MEDIA: tags or bare local
file paths. This meant images, documents, and voice files referenced
by the agent were silently dropped in normal (non-streaming,
non-background) conversations.

Changes:
- In WeixinAdapter.send(), call extract_media() and
  extract_local_files() before formatting/splitting text.
- Deliver extracted files via send_image_file(), send_document(),
  send_voice(), or send_video() prior to sending text chunks.
- Also fix two minor typing issues in gateway/run.py where
  extract_media() tuples were not unpacked correctly in background
  and /btw task handlers.

Fixes missing media delivery on Weixin personal accounts.
This commit is contained in:
Hedgeho9 2026-04-14 01:25:14 +08:00 committed by Teknium
parent 4ed6e4c1a5
commit 498fc6780e
2 changed files with 39 additions and 3 deletions

View file

@ -1460,8 +1460,44 @@ class WeixinAdapter(BasePlatformAdapter):
return SendResult(success=False, error="Not connected")
context_token = self._token_store.get(self._account_id, chat_id)
last_message_id: Optional[str] = None
# Extract MEDIA: tags and bare local file paths before text delivery.
media_files, cleaned_content = self.extract_media(content)
_, image_cleaned = self.extract_images(cleaned_content)
local_files, final_content = self.extract_local_files(image_cleaned)
_AUDIO_EXTS = {".ogg", ".opus", ".mp3", ".wav", ".m4a"}
_VIDEO_EXTS = {".mp4", ".mov", ".avi", ".mkv", ".webm", ".3gp"}
_IMAGE_EXTS = {".jpg", ".jpeg", ".png", ".webp", ".gif"}
async def _deliver_media(path: str, is_voice: bool = False) -> None:
ext = Path(path).suffix.lower()
if is_voice or ext in _AUDIO_EXTS:
await self.send_voice(chat_id=chat_id, audio_path=path, metadata=metadata)
elif ext in _VIDEO_EXTS:
await self.send_video(chat_id=chat_id, video_path=path, metadata=metadata)
elif ext in _IMAGE_EXTS:
await self.send_image_file(chat_id=chat_id, image_path=path, metadata=metadata)
else:
await self.send_document(chat_id=chat_id, file_path=path, metadata=metadata)
try:
chunks = [c for c in self._split_text(self.format_message(content)) if c and c.strip()]
# Deliver extracted MEDIA: attachments first.
for media_path, is_voice in media_files:
try:
await _deliver_media(media_path, is_voice)
except Exception as exc:
logger.warning("[%s] media delivery failed for %s: %s", self.name, media_path, exc)
# Deliver bare local file paths.
for file_path in local_files:
try:
await _deliver_media(file_path, is_voice=False)
except Exception as exc:
logger.warning("[%s] local file delivery failed for %s: %s", self.name, file_path, exc)
# Deliver text content.
chunks = [c for c in self._split_text(self.format_message(final_content)) if c and c.strip()]
for idx, chunk in enumerate(chunks):
client_id = f"hermes-weixin-{uuid.uuid4().hex}"
await self._send_text_chunk(

View file

@ -5811,7 +5811,7 @@ class GatewayRunner:
pass
# Send media files
for media_path in (media_files or []):
for media_path, _is_voice in (media_files or []):
try:
await adapter.send_document(
chat_id=source.chat_id,
@ -5989,7 +5989,7 @@ class GatewayRunner:
except Exception:
pass
for media_path in (media_files or []):
for media_path, _is_voice in (media_files or []):
try:
await adapter.send_file(chat_id=source.chat_id, file_path=media_path)
except Exception: