Skip to content

[Bug] QQ Official: cron/scheduled messages sent via send_by_session lack markdown rendering (msg_type=2) #7848

@Alkapuce

Description

@Alkapuce

What happened

When a scheduled cron job (active_agent) triggers the send_message_to_user tool on the QQ Official (qq_official) platform, the message content is sent as plain text without native QQ markdown rendering. Normal chat replies render markdown correctly.

Root Cause

There are two distinct sending paths for QQ Official messages:

Path A: Normal chat (markdown works ✅)

event.send()QQOfficialMessageEvent._post_send() → constructs payload:

payload = {
    "markdown": MarkdownPayload(content=plain_text),
    "msg_type": 2,          # ← QQ SDK markdown message type
    "msg_id": self.message_obj.message_id,
}

Path B: Cron/tool proactive send (markdown broken ❌)

context.send_message()platform.send_by_session()QQOfficialPlatformAdapter._send_by_session_common() → constructs payload:

payload = {"content": plain_text, "msg_id": msg_id}
# ← missing "markdown" field and "msg_type": 2

The _send_by_session_common method uses a plain {"content": text} payload format, which causes the QQ API to render the content as raw text without markdown formatting. This affects all proactive messages sent via send_message_to_user tool (cron jobs, future_task reminders, etc.).

Reproduce

  1. Set up a QQ Official (qq_official) platform adapter
  2. Create a daily cron job via future_task tool or WebUI that sends a markdown-formatted message (e.g. GitHub Trending report with tables, bold, links)
  3. Wait for the scheduled time
  4. Observe: the QQ message arrives but markdown elements (tables, bold, headers, links) are not rendered — shown as raw text

Expected behavior

Cron-triggered messages via send_message_to_user should render markdown identically to normal chat responses.

Affected code

astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py_send_by_session_common() method, line 233:

payload: dict[str, Any] = {"content": plain_text, "msg_id": msg_id}

Should be:

payload: dict[str, Any] = {
    "markdown": MarkdownPayload(content=plain_text) if plain_text else None,
    "msg_type": 2,
    "msg_id": msg_id,
}

Fix details

The fix requires 3 changes in _send_by_session_common:

  1. Import MarkdownPayload from botpy.types.message
  2. Change initial payload from content to markdown + msg_type: 2, matching _post_send() behavior
  3. Add media fallback: when msg_type is overridden to 7 by image/file/media attachments, remove the markdown field and set content instead (markdown payloads cannot carry media)

Environment

  • AstrBot version: v4.23.6
  • Platform: QQ Official (qq_official)
  • Deployment: uv tool install, systemd service
  • OS: Linux

Additional context

This bug has existed since the send_by_session path was introduced and has not been previously reported. The _post_send() method (normal chat path) correctly uses msg_type: 2 + MarkdownPayload, so this is a simple alignment fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:platformThe bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions