Skip to content

fix(qqofficial): restore @ mentions in group messages - #9705

Open
IQ-Director wants to merge 2 commits into
AstrBotDevs:devfrom
IQ-Director:fix/qqofficial-mentions-v2
Open

fix(qqofficial): restore @ mentions in group messages#9705
IQ-Director wants to merge 2 commits into
AstrBotDevs:devfrom
IQ-Director:fix/qqofficial-mentions-v2

Conversation

@IQ-Director

@IQ-Director IQ-Director commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • serialize valid At components as <@openid> markup
  • send mention-bearing replies and proactive messages as Markdown
  • preserve payload compatibility for media and guild channel messages
  • support legacy and current incoming mention formats
  • restore regression tests for QQ Official @ mentions

This PR is a follow-up to #9285 and its subsequent revert in #9310.

#9285 originally fixed an issue where QQ Official outgoing messages discarded At components in _parse_to_qqofficial, causing user mentions to disappear from replies.

Messages sent through send_by_session, including proactive and scheduled group messages, were also sent through the plain content field. QQ does not resolve <@openid> mentions through that path, so the mention could appear as plain text instead of an actual user mention.

#9285 was later reverted by #9310 because it was reported that QQ Official did not support real mentions.

However, the QQ Bot official text-chain documentation explicitly lists mentioning users as available in group chats and text subchannels:

PixPin_2026-08-16_07-39-35

Official documentation:

https://bot.q.qq.com/wiki/develop/api-v2/server-inter/message/trans/text-chain.html

I reapplied the implementation to the current dev branch and tested it in a QQ Official group-message environment. In the tested scenario, the target user was rendered as an actual mention rather than displaying the raw <@openid> markup.

This PR therefore restores the behavior from #9285 while adapting it to the current codebase and preserving subsequent upstream changes.

Native mention rendering on QQ for Windows

In-client mention notification on QQ for Windows Native mention rendering on QQ for Android

Modifications / 改动点

  • Updated qqofficial_message_event.py to serialize valid At components as <@openid>.
  • Ignored empty mention IDs.
  • Skipped At(qq="all"), because QQ Official group bots do not support bot-generated @all through this path.
  • Ensured event replies containing At use Markdown even when Markdown was explicitly disabled on the message chain.
  • Updated send_by_session so proactive, scheduled, and directly sent group messages containing At use msg_type=2 with a Markdown payload.
  • Kept ordinary messages without mentions on the existing plain-text sending path to avoid changing unrelated behavior.
  • Preserved compatibility with media messages by removing the Markdown payload, restoring plain content, and switching to msg_type=7.
  • Removed the incompatible msg_type field when sending messages to guild text channels.
  • Added support for cleaning legacy and current incoming mention formats:
    • <@bot_id>
    • <@!bot_id>
    • <qqbot-at-user id="bot_id" />
  • Preserved current dev changes.
  • Restored regression tests for outgoing mention serialization, incoming mention cleanup, and proactive group messages containing At.

Compatibility / 兼容性

The mention-specific behavior is only activated when a message chain contains a valid At component, or when Markdown is explicitly requested.

Ordinary messages without mentions continue to use the existing plain-text sending path.

Media messages continue to use msg_type=7, and guild text-channel messages do not include the incompatible msg_type field.

Environment / 测试环境

  • AstrBot base branch: dev
  • AstrBot base commit: eede34c18360d12100f27fc2f24c8718777d237b
  • QQ clients tested:
    • QQ for Windows: 9.9.33-52230 (64-bit)
    • QQ for Android: 9.3.35.39800
  • Bot environment: Production
  • Message scene: QQ Official group message

QQ Official regression tests

Test command:

pytest tests/test_qqofficial_group_message_create.py -q

Test result:

24 passed, 2 warnings

The warnings are unrelated to the mention changes:

  • Python 3.12 reports that audioop is deprecated and scheduled for removal
    in Python 3.13.
  • An aiosqlite worker thread may report that the pytest event loop was
    already closed during test teardown.

Code-quality test

Test command:

pytest tests/test_code_quality_typing.py -q

Test result:

1 passed, 1 warning

Ruff check

Command:

ruff check \
  astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py \
  astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py

Result:

All checks passed!

Ruff format

Command:

ruff format --check \
  astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py \
  astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py \
  tests/test_qqofficial_group_message_create.py

Result:

3 files already formatted

Diff check

git diff --check upstream/dev..HEAD

Result:

No whitespace errors.

Changed Files / 修改文件

astrbot/core/platform/sources/qqofficial/qqofficial_message_event.py
astrbot/core/platform/sources/qqofficial/qqofficial_platform_adapter.py
tests/test_qqofficial_group_message_create.py
3 files changed, 188 insertions(+), 77 deletions(-)

Related / 相关 PR

Summary by Sourcery

Restore proper QQ Official group @ mention handling while keeping existing behaviors for non-mention and media/guild messages intact.

New Features:

  • Support outgoing QQ Official group messages with At components rendered as <@openid> mentions via Markdown payloads.
  • Handle proactive and session-based group messages with mentions using the Markdown msg_type path instead of plain-text content.

Bug Fixes:

  • Ensure At components in QQ Official outgoing messages are serialized into content instead of being silently dropped.
  • Normalize incoming QQ Official bot mentions by stripping legacy and current mention markup variants from plain text content.

Enhancements:

  • Centralize media payload construction to consistently use msg_type=7 and remove markdown when media is present.
  • Avoid sending incompatible msg_type values for guild text-channel messages to preserve compatibility with QQ APIs.

Tests:

  • Expand QQ Official group message tests to cover mention serialization ordering, empty At components, multiple mention markup formats, and Markdown usage in send_by_session.

- serialize valid At components as <@openid> markup
- send mention-bearing replies and proactive messages as Markdown
- preserve payload compatibility for media and guild channel messages
- support legacy and current incoming mention formats
- add regression tests for QQ Official @ mentions
@dosubot dosubot Bot added size:L This PR changes 100-499 lines, ignoring generated files. area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. labels Aug 16, 2026

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've left some high level feedback:

  • Consider adding a brief inline comment near _get_mention_id explaining why qq_id == 'all' is skipped, so future maintainers don’t have to rely on PR context to understand the @all behavior limitation on QQ Official.
  • The new _strip_bot_mention_markup helper uses simple str.replace, which will silently collapse multiple stacked mentions without preserving spacing; if multiple mentions or surrounding whitespace matter, you may want to normalize with a regex to avoid accidental concatenation of words.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- Consider adding a brief inline comment near `_get_mention_id` explaining why `qq_id == 'all'` is skipped, so future maintainers don’t have to rely on PR context to understand the `@all` behavior limitation on QQ Official.
- The new `_strip_bot_mention_markup` helper uses simple `str.replace`, which will silently collapse multiple stacked mentions without preserving spacing; if multiple mentions or surrounding whitespace matter, you may want to normalize with a regex to avoid accidental concatenation of words.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

- document why QQ Official skips bot-generated @ALL mentions
- normalize consecutive mention markup without joining adjacent text
- escape mention IDs before regex matching
- preserve unrelated user mentions during cleanup
- add regression tests for stacked, legacy, and special-character mentions
@IQ-Director

Copy link
Copy Markdown
Contributor Author

Sourcery review follow-up

Thanks for the suggestions. Addressed the review feedback:

  • Added an inline comment explaining why bot-generated @all mentions are skipped on QQ Official.
  • Replaced direct str.replace cleanup with a narrowly scoped regex.
  • Escaped mention IDs before regex matching.
  • Replaced consecutive mention markup with a single space to preserve word boundaries.
  • Preserved unrelated user mentions.
  • Avoided globally normalizing message whitespace or line breaks.
  • Added regression coverage for mention markup between adjacent text, stacked current mention formats, legacy <qqbot-at-user> markup, unrelated user mentions, and mention IDs containing regex-special characters.

Validation after the changes:

  • QQ Official regression tests: 24 passed
  • Code-quality test: 1 passed
  • Ruff check and format: passed
  • Smoke test workflow: passed

CI status

The failing Run pytest suite check is inherited from the current dev baseline and does not appear to be caused by this PR.

Latest PR run:

https://github.com/AstrBotDevs/AstrBot/actions/runs/31917884276

The dev base commit eede34c18360d12100f27fc2f24c8718777d237b fails with the same 13 collection errors:

https://github.com/AstrBotDevs/AstrBot/actions/runs/31774429869

The matching errors include:

  • Missing astrbot.dashboard.routes.* modules.
  • Missing AstrBotJSONProvider.
  • Missing readability.
  • Missing long_term_memory.

None of those modules are changed by this PR. The PR-specific QQ Official tests pass.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:platform The bug / feature is about IM platform adapter, such as QQ, Lark, Telegram, WebChat and so on. size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant