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
- Set up a QQ Official (qq_official) platform adapter
- 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)
- Wait for the scheduled time
- 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:
- Import
MarkdownPayload from botpy.types.message
- Change initial payload from
content to markdown + msg_type: 2, matching _post_send() behavior
- 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.
What happened
When a scheduled cron job (active_agent) triggers the
send_message_to_usertool 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:Path B: Cron/tool proactive send (markdown broken ❌)
context.send_message()→platform.send_by_session()→QQOfficialPlatformAdapter._send_by_session_common()→ constructs payload:The
_send_by_session_commonmethod 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 viasend_message_to_usertool (cron jobs, future_task reminders, etc.).Reproduce
future_tasktool or WebUI that sends a markdown-formatted message (e.g. GitHub Trending report with tables, bold, links)Expected behavior
Cron-triggered messages via
send_message_to_usershould 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:Should be:
Fix details
The fix requires 3 changes in
_send_by_session_common:MarkdownPayloadfrombotpy.types.messagecontenttomarkdown+msg_type: 2, matching_post_send()behaviormsg_typeis overridden to7by image/file/media attachments, remove themarkdownfield and setcontentinstead (markdown payloads cannot carry media)Environment
Additional context
This bug has existed since the
send_by_sessionpath was introduced and has not been previously reported. The_post_send()method (normal chat path) correctly usesmsg_type: 2+MarkdownPayload, so this is a simple alignment fix.