fix(qqofficial): allocate fresh msg_seq per inbound msg_id (#2290)#2294
Open
dadachann wants to merge 62 commits into
Open
fix(qqofficial): allocate fresh msg_seq per inbound msg_id (#2290)#2294dadachann wants to merge 62 commits into
dadachann wants to merge 62 commits into
Conversation
- TelegramAdapter inherits AbstractPlatformAdapter with all capabilities - TelegramEventConverter handles all Update types: message, edited_message, chat_member, my_chat_member, callback_query, message_reaction - TelegramAPIMixin implements: edit_message, delete_message, forward_message, get_group_info, get_group_member_list/info, get_user_info, get_file_url, mute/unmute/kick_member, leave_group - PLATFORM_API_MAP for call_platform_api: pin/unpin message, set chat title/desc, get admins, send chat action, create invite link, answer callback query - Full backward compat: legacy FriendMessage/GroupMessage listeners still work - Preserves all existing functionality: stream output, markdown card, forum topics - Old sources/telegram.py untouched for gradual migration
# Conflicts: # pyproject.toml # uv.lock
Expose skill tools (activate/register_skill/native exec) like native tools instead of gating them behind the skill_authoring capability: - toolmgr.get_all_tools drops include_skill_authoring; SkillToolLoader self-gates on sandbox + skill_mgr - preproc drops the include_skill_authoring branch; pipeline-bound skills and the skills resource gate on skill_mgr presence Persist activated skills into host.activated_skills conversation state so they survive across runs (host writes at activate; last-write-wins); drop the dead restore_activated_skills helper. Prefill ToolResource.parameters host-side (tool_mgr.get_tool_schema) so runners build LLM tools without per-tool get_tool_detail round-trips. Align agent-runner-pluginization design docs to the all-tool model.
- references/skill-all-tool-acceptance.md: acceptance matrix for the skill all-tool model (runner x lifecycle x backend), case status, exit criteria, and the #2271 known issue (pre-existing box nested-mount, not this branch) - cases/skill-discovery-via-mcp-gateway.yaml: schema-valid case proving an external harness discovers skills via langbot_list_assets (the new 'skills' asset class); marked blocked-env until remote claude-code is responsive
# Conflicts: # pyproject.toml # uv.lock
- Replace legacy pipeline binding card + RoutingRulesEditor with unified EventBindingsEditor; remove use_pipeline_uuid/pipeline_routing_rules from bot form schema and API update handler - Add _augment_event_data() to botmgr for filter virtual fields (message_text, message_element_types, chat_type) - Add alembic migration 0009: migrate use_pipeline_uuid and pipeline_routing_rules into event_bindings on first run - Fix command.tsx: data-[disabled] -> data-[disabled=true] so cmdk 1.x items (data-disabled=false) are not pointer-events:none - EventBindingsEditor: onSelect on CommandItems, filter conditions panel, disabled bindings section, dnd reorder - i18n: add filter/condition keys for zh-Hans and en-US - Update tests to match new bot service behavior Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
groupByCategory pushed multi-category adapters (lark, wecom, discord, slack) into every matching bucket, so the adapter Select rendered duplicate SelectItem values — triggering React duplicate-key warnings and corrupting Radix item tracking. Assign each item to its highest -priority matching category only. Also de-dupes the wizard card grid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Unorchestrated events are discarded by default, so an explicit discard target is redundant. Drop the discard CommandGroup (and the now-unused CommandSeparator import); the currentLabel() discard branch is kept so any pre-existing discard bindings still render correctly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 12 old adapters that now have an EBA replacement are tagged `spec.legacy: true` in their source manifests. Principle: don't delete, de-emphasize. - sources/*.yaml (aiocqhttp, dingtalk, discord, kook, lark, officialaccount, qqofficial, slack, telegram, wecom, wecombot, wecomcs): add spec.legacy: true - Adapter / IChooseAdapterEntity types: add optional legacy flag - BotForm adapter Select: split legacy adapters into a collapsed, grayscale group at the bottom with an explanatory hint; auto-expand when the bot already uses a legacy adapter - Wizard platform picker: same collapsed legacy section - i18n: legacyAdapters / legacyAdaptersHint (zh-Hans, en-US) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Telegram (EBA) manifest was missing spec.categories, so it fell into the uncategorized/protocol bucket. Restore popular + global to match the legacy telegram adapter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Move conditions toggle between event select and arrow; drop "IF" label - Remove "all events" (*) option from event select - Add i18n labels for concrete event names (zh/en/ja) via bots.eventNames - Narrow fallback event set to message.received for adapters without declared supported_events (legacy adapters only emit message.received) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Only surface `ns.*` wildcard when the namespace has 2+ concrete events, so single-event adapters (legacy) show just the one event - Show raw event code as a muted subtitle under each option's label Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a trailing badge (icon + label) to each entry in the sidebar Agent section so users can tell Agent orchestration apart from legacy Pipeline. Thread the agent `kind` field through SidebarEntityItem. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Drop the text label; keep the icon with a title tooltip so names have more room in the narrow sidebar. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a toggle (left of the "+" button) that groups the Agent section by kind, showing "Agent" and "Pipeline" sub-headers. State persists in localStorage, mirroring the extensions group-by-type pattern. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add group-by-type button to extensions category header (mirrors the agent group-by-kind pattern) — syncs with the extensions page Switch via shared SidebarDataContext state - Relocate both group and refresh controls to sit left-aligned immediately after the title for both Agent and Extensions sections - Add plugins.groupByTypeShort i18n key to all 8 locales Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…0054005 dedup QQ Official v2 API deduplicates passive messages by (msg_id, msg_seq). The adapter reused msg_seq across multiple sends under the same inbound msg_id, so multi-part text replies, rich media, and especially streaming chunks (msg_seq pinned at 1) were rejected with: 40054005 消息被去重,请检查请求msgseq Add a per-inbound-msg_id sequence allocator (next_reply_msg_seq) on QQOfficialClient, backed by a bounded OrderedDict + asyncio lock, and use it for C2C/group text sends and rich media. Streaming now advances ctx['msg_seq'] per chunk. Proactive sends (no msg_id) fall back to the existing global counter. Closes #2290
Contributor
Author
CI 说明:当前红勾来自 base 分支 (
|
| Check | 失败原因 | 是否本 PR 引入 |
|---|---|---|
| Ruff Lint & Format | dev/4.11.x 上已有 48 个文件未通过 ruff format --check(涉及 slack / telegram / wecombot / agent-runner / alembic 等,本 PR 均未触碰) |
否 |
| Unit Tests 3.11/3.12/3.13 | 51 个 collection error,全部为 ModuleNotFoundError: No module named 'langbot_plugin.api.entities.builtin.agent_runner',即 CI 解析到的 langbot_plugin 版本尚无该模块 |
否 |
| E2E / Fast Integration | 同上,import 阶段即失败 | 否 |
| Frontend Lint | 前端检查,与后端改动无关 | 否 |
| Box Integration / CLA | ✅ pass | — |
验证:
- 直接 checkout 未带本改动的干净
origin/dev/4.11.x,执行uv run ruff format src --check同样报48 files would be reformatted—— 基线本身即红。 - 本 PR 改动的 3 个文件单独跑 CI 同版本 ruff(0.14.14):
All checks passed+3 files already formatted。 - 51 个 test collection error 中无任何 qqofficial 相关项;本 PR 新增的 3 个回归测试本地全部通过(
12 passed)。
结论:本 PR 代码与测试本身可通过全部相关检查。待 dev/4.11.x 的格式化债与 langbot_plugin 版本问题在 base 修复后 rerun 即可转绿。
huanghuoguoguo
force-pushed
the
dev/4.11.x
branch
from
July 12, 2026 12:37
a8315b7 to
99d9c22
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #2290. QQ Official v2 API deduplicates passive messages by
(msg_id, msg_seq). The adapter reusedmsg_seqfor multiple sends tied to the same inboundmsg_id, so QQ rejected later sends with:Three pre-existing strategies collided:
send_private_text_msg/send_group_text_msg): nomsg_seqat all → QQ defaulted it to1, so any 2nd text reply under the samemsg_idwas a duplicate._send_media_msg): used a global_msg_seq_counter— unique, but could still collide with the implicitmsg_seq=1of a text send under the samemsg_id.reply_message_chunk):msg_seqpinned at1forever, onlyindexadvanced → every chunk reusedmsg_seq=1(the most frequent trigger).Changes
QQOfficialClient.next_reply_msg_seq(msg_id)— a per-inbound-msg_idallocator backed by a boundedOrderedDict(max 1024 keys, LRU eviction) + anasyncio.Lockfor concurrency safety. Nomsg_id(proactive sends) falls back to the existing global counter.ctx['msg_seq']per chunk.send_channle_*) are intentionally left unchanged — they hit the legacy guild API, which does not use this dedup mechanism.Tests
Added 3 regression tests to
test_qqofficial_eba_adapter.py:msg_id, is independent across ids, falls back for missing id, and is gap-free under 50 concurrent allocations;msg_idcarrymsg_seq1, 2, 3;msg_seq.All 12 tests in the file pass;
ruff checkandruff format --checkclean.