Skip to content

[Feature]: Support MEDIA delivery for plugin platforms via adapter #18422

@wkeqin

Description

@wkeqin

Problem or Use Case

send_message tool supports native (images, voice, documents) for telegram, discord, matrix, weixin, signal, and yuanbao. However, plugin platforms that register via the gateway adapter system get MEDIA attachments silently dropped — text goes through _send_via_adapter() but media_files are completely ignored, falling through to the "Non-media platforms" path which either errors out (media-only) or emits a warning and omits media.

This means any plugin platform (e.g. QQ OneBot, custom adapters) that implements send_image, send_image_file, send_voice, or send_document on its adapter class cannot receive media from send_message at all, even though the base adapter class already defines these methods.

Proposed Solution

In _send_to_platform(), after the platform-specific MEDIA blocks (telegram, discord, matrix, weixin, signal, yuanbao), add a generic fallback for plugin platforms before the "Non-media platforms" error/warning path:

# --- Plugin platform media: route through live adapter ---
if media_files:
    try:
        from gateway.run import _gateway_runner_ref
        runner = _gateway_runner_ref()
        if runner:
            adapter = runner.adapters.get(platform)
            if adapter:
                # send text first
                if message.strip():
                    result = await adapter.send(chat_id=chat_id, content=message)
                    if not result.success:
                        return {"error": f"Plugin text send failed: {result.error}"}
                # send each media file via adapter's native methods
                for media_path, is_voice in media_files:
                    ext = os.path.splitext(media_path)[1].lower()
                    if ext in _IMAGE_EXTS:
                        result = await adapter.send_image_file(chat_id, media_path)
                    elif ext in _VOICE_EXTS and is_voice:
                        result = await adapter.send_voice(chat_id, media_path)
                    elif ext in _AUDIO_EXTS:
                        result = await adapter.send_voice(chat_id, media_path)
                    else:
                        result = await adapter.send_document(chat_id, media_path)
                    if not result.success:
                        return {"error": f"Plugin media send failed: {result.error}"}
                return {"success": True, "platform": platform.value, "chat_id": chat_id}
    except Exception as e:
        return {"error": f"Plugin media send failed: {e}"}

This reuses the existing base adapter interface (send_image_file, send_voice, send_document) — no new abstractions needed. Adapters that don't implement these methods get the default fallback behavior from the base class.

Alternatives Considered

  • Hardcoding each plugin platform's media handling individually (what I'm doing now as a local patch) — works but doesn't scale and requires modifying the tool for every new platform.
  • Adding a supports_media flag to plugin registration — over-engineered since the base adapter already has the methods.

Feature Type

Gateway / messaging improvement

Scope

Small (single file, < 50 lines)

Contribution

  • I'd like to implement this myself and submit a PR

Debug Report (optional)

Metadata

Metadata

Assignees

No one assigned

    Labels

    P3Low — cosmetic, nice to havecomp/gatewayGateway runner, session dispatch, deliverycomp/pluginsPlugin system and bundled pluginstype/featureNew feature or request

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions