Skip to content

fix: suppress duplicate OneBot message delivery - #10653

Open
mvanhorn wants to merge 1 commit into
AstrBotDevs:masterfrom
mvanhorn:fix/7094-astrbot-onebot-duplicate-messages
Open

mvanhorn wants to merge 1 commit into
AstrBotDevs:masterfrom
mvanhorn:fix/7094-astrbot-onebot-duplicate-messages

Conversation

@mvanhorn

@mvanhorn mvanhorn commented Oct 20, 2026 •

Copy link
Copy Markdown

A single QQ message can be processed more than once, causing repeated replies when the same OneBot event is delivered repeatedly. The adapter queued every incoming message without checking whether its OneBot identity had already been delivered. Repeated callbacks for the same event therefore created separate queued events.

The adapter now suppresses duplicate message identities for 60 seconds, using the bot, message type, conversation, sender, and message ID. The cache is limited to 1,024 entries and cleared on termination; incomplete identities and non-message events bypass deduplication. Identity checks, enqueueing, and cache recording run synchronously, and identities are recorded only after a successful enqueue so failures remain retryable. Regression coverage exercises duplicate callbacks, identity boundaries, expiry, eviction, termination, and retries.

Ran make pr-test-neo locally on macOS for the AstrBot AiocqhttpAdapter change; it passed.
python_tests found no tests to run.
make pr-test-neo passed the Ruff format check.
make pr-test-neo passed the Ruff lint check.
make pr-test-neo passed 13 tests with one warning.
make pr-test-neo passed the smoke test.

Modifications / 改动点

  • This is NOT a breaking change. / 这不是一个破坏性变更。

Screenshots or Test Results / 运行截图或测试结果


Checklist / 检查清单

  • 😊 If there are new features added in the PR, I have discussed it with the authors through issues/emails, etc.
    / 如果 PR 中有新加入的功能,已经通过 Issue / 邮件等方式和作者讨论过。

  • 👀 My changes have been well-tested, and "Verification Steps" and "Screenshots" have been provided above.
    / 我的更改经过了良好的测试,并已在上方提供了“验证步骤”和“运行截图”。

  • 📚 I checked the affected WebUI instructions and screenshots in docs/zh and docs/en against the changed navigation, page structure, and labels, and updated them in this PR (or explained why no documentation update is needed). For renamed, moved, or merged entry points, I included an old entry → new entry mapping in the documentation and changelog.
    / 我已对照变化后的 WebUI 入口、页面结构和术语,核对并在本 PR 中更新 docs/zh 和 docs/en 的相关操作说明与截图(或说明无需更新文档的原因)。入口改名、移动或合并时,已在文档和 changelog 中补充 旧入口 → 新入口 对照。

  • 🤓 I have ensured that no new dependencies are introduced, OR if new dependencies are introduced, they have been added to the appropriate locations in requirements.txt and pyproject.toml.
    / 我确保没有引入新依赖库,或者引入了新依赖库的同时将其添加到 requirements.txt 和 pyproject.toml 文件相应位置。

  • 😮 My changes do not introduce malicious code.
    / 我的更改没有引入恶意代码。

Fixes #7094

Summary by Sourcery

Suppress repeated OneBot message deliveries while retaining valid retry and lifecycle behavior.

Bug Fixes:

  • Prevent duplicate OneBot message deliveries from being queued within a 60-second window.
  • Preserve retryability when event creation or enqueueing fails, while allowing incomplete identities and non-message events through.

Enhancements:

  • Bound recent-message deduplication state to 1,024 entries and clear it when the adapter terminates.

Tests:

  • Add regression coverage for duplicate callbacks, identity boundaries, expiry, eviction, termination, and enqueue retries.

The adapter now suppresses duplicate message identities for 60 seconds,
using the bot, message type, conversation, sender, and message ID. The
cache is limited to 1,024 entries and cleared on termination; incomplete
identities and non-message events bypass deduplication. Identity checks,
enqueueing, and cache recording run synchronously, and identities are
recorded only after a successful enqueue so failures remain retryable.
Regression coverage exercises duplicate callbacks, identity boundaries,
expiry, eviction, termination, and retries.

Fixes AstrBotDevs#7094

@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 found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py" line_range="45" />
<code_context>
         super().__init__(platform_config, event_queue)

         self.settings = platform_settings
+        self._recent_messages: dict[tuple[str, ...], float] = {}
         self.host = platform_config["ws_reverse_host"]
         self.port = platform_config["ws_reverse_port"]
</code_context>
<issue_to_address>
**Duplicates across adapters remain queued**

When the same OneBot bot is connected to two enabled Aiocqhttp adapter instances and both receive the same event, each `AiocqhttpAdapter` checks only its own `_recent_messages`, so two adapters receiving the same OneBot event both enqueue it to the shared event queue and the bot can reply twice.

Coordinate deduplication across adapters that feed the shared queue, using the OneBot identity as the shared key.
</issue_to_address>

Sourcery assessment

Needs a human reviewer. 1 finding to address first, and if OneBot reuses a message ID for a distinct message from the same sender and conversation within the 60-second window, this cache will silently skip that message, so its handlers or commands never run. Reverting removes the suppression but does not restore messages already skipped.

Blocking findings: astrbot/core/platform/sources/aiocqhttp/aiocqhttp_platform_adapter.py:45


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

This branch has not been deployed

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

Labels

None yet

Projects

Status: In Progress

Development

Successfully merging this pull request may close these issues.

[Bug]QQ机器人对话,发送一次消息机器人多次回复,查看日志显示发送了多次消息但是QQ上只发送了一次

1 participant