[Bug] QQ频道消息无法回复:markdown 回退顺序有误,误用主动消息导致 304049 限频
Los mantenedores suelen responder en 1 día
Evaluación
- Dificultad
- 2/5
- Tiempo estimado
- 1-3 horas
- Aptitud para principiantes
- 82/100
Línea de trabajo
Empieza en _send_with_markdown_fallback() en el archivo fuente de la plataforma QQ Official qqofficial_message_event.py; lee la rama except _QQOFFICIAL_SEND_API_ERRORS línea por línea para ver que msg_id se extrae (pop) antes de que se elimine el markdown. La corrección ramifica primero en el error 50056 'markdown not allowed' — descarta markdown, establece content en texto plano y ajusta msg_type, conservando msg_id — y solo recurre al envío proactivo (sin msg_id) ante otros errores. El resultado esperado es que una respuesta de canal se envíe con éxito como mensaje pasivo sin 304049, mientras que el comportamiento del markdown en chat individual permanece sin cambios.
Escrito por el modelo de indexación a partir del texto del issue.
Descripción
🐛 问题描述
QQ 频道场景下机器人无法回复消息,报错「主动消息限频」—— 但这只是普通被动回复。
🌍 环境
- AstrBot
4.28.2(4.29.0-beta.1/master/dev同样存在) - 平台:QQ 官方机器人(
qq_official) - 配置:
use_markdown = true
🔁 复现
use_markdown = true- 用户在 QQ 频道给机器人发消息
- 机器人回复失败
📜 日志
[QQOfficial] 回复消息失败: 不允许发送原生 markdown, 尝试使用主动发送接口。
botpy.errors.NotFoundError: 私信主动消息数限频
https://api.sgroup.qq.com/dms/{guild_id}/messages → 404
🔑 错误码
| 错误码 | 含义 |
|---|---|
50056 |
不允许发送 markdown content(触发点) |
304049 |
私信主动消息数限频(最终失败) |
🔍 根因
qqofficial_message_event.py 的 _send_with_markdown_fallback():
except _QQOFFICIAL_SEND_API_ERRORS as err:
if payload.get("msg_id"):
fallback_payload = payload.copy()
fallback_payload.pop("msg_id", None) # ❌ 先去 msg_id → 变主动消息
return await send_func(fallback_payload) # → 撞 304049
...
# 去掉 markdown、保留 msg_id(正确做法,但轮不到执行)
回退没按错误类型分流:markdown 被拒(50056)时应保留 msg_id、只去掉 markdown。
而官方规定「私信每个用户每天仅 2 条主动消息」,额度一空频道就彻底无法回复。
🛠 建议修复
在去掉 msg_id 之前先处理 markdown:
if (QQOfficialMessageEvent.MARKDOWN_NOT_ALLOWED_ERROR in str(err)
and payload.get("markdown") and plain_text):
md_payload = payload.copy()
md_payload.pop("markdown", None)
md_payload["content"] = plain_text
if md_payload.get("msg_type") == 2:
md_payload["msg_type"] = 0
try:
return await send_func(md_payload) # 保留 msg_id,走被动通道
except _QQOFFICIAL_SEND_API_ERRORS as e2:
err, payload = e2, md_payload
✔️ 验证情况(已实测通过 ✅)
实测方式:临时禁用所有插件层规避手段(即让 markdown 真正被发送),
完整触发 50056 → 回退 链路,观察补丁行为。
实测日志(20:01):
[20:01:34] 频道用户发送消息
[20:01:38] [QQOfficial] 回复消息失败: 不允许发送原生 markdown, 尝试使用主动发送接口。
[20:01:39] [QQOfficial] markdown 不被支持,已改用纯文本(保留被动回复)。 ← 补丁生效
[20:01:39] → 消息发送成功(无 Failed to send)
结果:
- ✅ 频道私信回复恢复正常
- ✅ 回退逻辑按预期执行:保留
msg_id、仅去掉markdown(走被动通道) - ✅ 不再出现
304049 私信主动消息数限频 - ✅ 单聊 markdown 不受影响(`)频道才降级)**
其他已验证项:
- ✅ 根因判断:逐行核对
_send_with_markdown_fallback控制流 - ✅ 补丁可应用性:
patch -p1成功,结果与修复后文件逐字节一致 - ✅ 补丁语法:
py_compile通过
- Lenguaje dominante
- Python
- Estrellas
- 41.4k
- Forks
- 3k
- Merge medio
- 1 d 5 h
- PR fusionados (30 d)
- 145
Preparar el entorno
- Incluye un Dockerfile o un archivo de Docker Compose
- Tiene una plantilla de pull request
- Leer la guía de contribución
Primeros pasos
- Lee el issue completo y luego la guía de contribución del proyecto.
- Comenta en el issue que vas a ocuparte — evita que dos personas hagan lo mismo.
- Haz un fork del repositorio y trabaja en una rama.
- Abre un pull request que haga referencia al número del issue.
Más de AstrBotDevs/AstrBot
-
Text-to-image rendering suppresses asyncio task cancellationPosiblemente ocupada @kilisamemarisaaa la tomó hace 4 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 80/100
AstrBotDevs/AstrBot#10389 ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 76/100
AstrBotDevs/AstrBot#10388 · 1 comentario ·
Los mantenedores suelen responder en 1 día
-
Boxlite boot treats any HTTP response as a healthy sandboxPosiblemente ocupada Un pull request vinculado a esta issue está abierto o ya se fusionó. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 82/100
AstrBotDevs/AstrBot#10385 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
[Feature] 移除「切换到旧版知识库」入口Posiblemente ocupada @silvaling la tomó hace 6 días. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
AstrBotDevs/AstrBot#10340 · 1 reacción ·
Los mantenedores suelen responder en 1 día
-
Dificultad 2/5 1-3 horas Aptitud para principiantes 78/100
AstrBotDevs/AstrBot#10322 · 1 comentario ·
Los mantenedores suelen responder en 1 día
Todos los issues de AstrBotDevs/AstrBot
Issues similares
-
Broken link in index.rstAbiertodocumentation
Dificultad 1/5 Menos de una hora Aptitud para principiantes 65/100
ansys/pydpf-core#3547 ·
Los mantenedores suelen responder en 1 día
-
core
Dificultad 2/5 1-3 horas Aptitud para principiantes 70/100
vectorize-io/hindsight#5457 ·
Los mantenedores suelen responder en 1 día
-
[Bug]: LangChain drops OpenAI Responses text blocks from session recordingPosiblemente ocupada @ktz03 la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
volcengine/OpenViking#5806 ·
Los mantenedores suelen responder en 1 día
-
HTML: <template> content is extracted as document textPosiblemente ocupada @ryanmeowy la tomó hoy. Abiertobug html
Dificultad 1/5 Menos de una hora Aptitud para principiantes 82/100
docling-project/docling#4714 · 2 comentarios ·
Los mantenedores suelen responder en 1 día
-
APIv2 event data accepts a non-string reply and a NaN upper_boundPosiblemente ocupada @awss1i la tomó hoy. Abierto
Dificultad 2/5 1-3 horas Aptitud para principiantes 72/100
freedomofpress/securedrop#7946 · 1 comentario ·
Los mantenedores suelen responder en 2 días