# QQOfficial EBA Adapter Adapter directory: `src/langbot/pkg/platform/adapters/qqofficial/` Manifest name: `qqofficial-omni` Status: partial migration. The EBA adapter structure, manifest, converters, cache-backed safe APIs, platform API map, unit tests, and direct live probe scaffold are in place. A real QQ Official WebSocket bot on `dev.rockchin.top` received an inbound user message and drove LangBot into the normal pipeline path; the response path was blocked by the test environment model service returning `model_not_found` for `deepseek-v3`. ## Config | Field | Required | Notes | | --- | --- | --- | | `appid` | yes | QQ Official app ID. | | `secret` | yes | QQ Official app secret. | | `token` | yes | QQ Official callback token. | | `enable-webhook` | yes | Uses LangBot unified webhook when true; otherwise uses the QQ WebSocket gateway. | | `enable-stream-reply` | yes | Enables C2C streaming replies when supported by the QQ Official endpoint. | | `webhook_url` | no | Generated by LangBot and copied into the QQ Official callback settings in webhook mode. | ## Events | Event | Evidence | Notes | | --- | --- | --- | | `message.received` | adapter-live, unit | `C2C_MESSAGE_CREATE`, `DIRECT_MESSAGE_CREATE`, `GROUP_AT_MESSAGE_CREATE`, `GROUP_MESSAGE_CREATE`, and `AT_MESSAGE_CREATE` map to common `MessageReceivedEvent`. Full-group mode has protocol regression coverage; the earlier live probe below covered the original message path. | | `message.reaction` | unit | `MESSAGE_REACTION_ADD` and `MESSAGE_REACTION_REMOVE` map to common `MessageReactionEvent`. Live gateway evidence is pending. | | `group.member_joined` | unit | `GUILD_MEMBER_ADD` and `GROUP_MEMBER_ADD` map to common `MemberJoinedEvent` when the gateway payload carries a group/guild/channel ID and member openid. | | `group.member_left` | unit | `GUILD_MEMBER_REMOVE` and `GROUP_MEMBER_REMOVE` map to common `MemberLeftEvent`. Live gateway evidence is pending. | | `bot.invited_to_group` | unit | `GUILD_CREATE` and `GROUP_ADD_ROBOT` map to common `BotInvitedToGroupEvent`. | | `bot.removed_from_group` | unit | `GUILD_DELETE` and `GROUP_DEL_ROBOT` map to common `BotRemovedFromGroupEvent`. | | `platform.specific` | unit | Unmapped gateway events are emitted as structured `PlatformSpecificEvent`; live evidence is pending. | ## Full-group messages After a group owner enables QQ's "receive all messages" permission, QQ delivers `GROUP_MESSAGE_CREATE`, including messages that mention the bot. Both the EBA and legacy adapters accept this event over WebSocket and unified Webhook, retain the sender's `member_openid` (with the older `openid` alias as fallback), and use the original message ID for passive group replies. Ordinary messages and images do not receive a bot `At` component. A full-group message receives that component only when QQ supplies a boolean `mentions[].is_you` equal to `true` and a matching native `<@ID>` or `<@!ID>` token in its text. Only the bot's own token is removed; mentions of other members remain text. Nicknames, `bot: true` alone, and a text that looks like a mention do not authorize a reply. The EBA component targets the adapter's configured bot account so existing `is_at_bot` route filters continue to match. Default EBA group routes requiring `is_at_bot` still exclude unmentioned group messages. Custom event routes or plugin subscriptions may opt into ordinary messages. This change does not alter QQ permissions, existing bot configuration, event bindings, or plugin subscriptions. `tests/unit_tests/platform/test_qqofficial_full_group_messages.py` exercises the real adapters and shared QQ parser with external gateway/download/send transports substituted. It covers text, images, self/other/unverified mentions, sender aliases, legacy @-only mode, private messages, and passive reply targets. This is protocol regression coverage, not a claim of a fresh live QQ probe of the whole upstream build. ## Common APIs | API | Evidence | Notes | | --- | --- | --- | | `send_message` | unit, blocked | Sends private C2C, group, and text-only channel messages through the existing QQ Official client. Live outbound UI verification is pending because the test pipeline failed before producing a bot response. | | `reply_message` | unit, blocked | Replies using the source `QQOfficialEvent` message ID when available. Live reply was blocked by the test environment model service returning `model_not_found`. | | `get_message` | unit | Returns cached inbound `MessageReceivedEvent`. | | `get_user_info` | unit | Returns cached inbound sender. | | `get_friend_list` | unit | Returns cached private senders. | | `get_group_info` | unit | Returns cached group/channel metadata from inbound events. | | `get_group_member_info` | unit | Returns cached group sender as a common member. | | `get_group_member_list` | unit | Returns cached group members observed by the adapter. | | `call_platform_api` | unit, blocked | Safe diagnostic actions are implemented; live calls are pending credentials. | ## Platform APIs | Action | Evidence | Notes | | --- | --- | --- | | `check_access_token` | unit, blocked | Calls the existing client token check. | | `refresh_access_token` | unit, blocked | Forces token refresh. | | `get_gateway_url` | unit, blocked | Fetches the WebSocket gateway URL. | | `get_mode` | unit | Returns webhook and stream-reply mode. | ## Components | Receive Component | Evidence | Notes | | --- | --- | --- | | `Source` | unit | Uses QQ message/event IDs and timestamp. | | `Plain` | unit | Preserves text content. | | `At` | unit | Group and channel mention events insert an adapter bot mention marker. | | `Image` | unit | QQ image attachment URL is converted to common `Image`; falls back to URL if download fails. | | `Unknown` | unit | Unsupported/empty native payloads become `Unknown`. | | `Voice`, `File`, `Quote`, `Face`, `Forward`, mixed chain | blocked | Current native parser only exposes text and image attachments; live endpoint behavior still needs verification. | | Send Component | Evidence | Notes | | --- | --- | --- | | `Plain` | unit, blocked | Sends through private, group, or channel text APIs. | | `At`, `AtAll` | unit, blocked | Converted to readable mention text. | | `Image` | unit, blocked | Sends through the QQ Official rich media upload/send path for C2C and group targets. | | `Voice` | unit, blocked | Sends through the QQ Official rich media upload/send path for C2C and group targets. | | `File` | unit, blocked | Sends through the QQ Official rich media upload/send path for C2C and group targets. | | `Quote`, `Forward`, mixed chain | unit, blocked | Flattened to ordered send payloads where possible. | | `Face` | not-supported | No common QQ Official face mapping is implemented. | ## Verification Record Test date: 2026-06-02 Endpoint/simulator: `dev.rockchin.top` with a real QQ Official WebSocket bot (`qqofficial-omni`, bot UUID `80a5560b-52b1-40e7-b7d6-4a2341eb4780`) and LangBot running from `/home/wgc/LangBotxg/LangBotEbaTest`. Observed evidence: - The QQ Official WebSocket bot was enabled with `enable-webhook=false`. - A real user message reached LangBot and entered the standard pipeline path. - The response path stopped at the model layer with `model_not_found` for `deepseek-v3`; this is a model/provider configuration issue, not an adapter conversion failure. - `qq-webhook.langbot.dev` was temporarily routed through Caddy to `127.0.0.1:5301` for webhook checks, but the observed EBA bot used WebSocket mode. Standalone runtime command: ```bash cd langbot-plugin-sdk uv run python -m langbot_plugin.cli.__init__ rt --debug-only --ws-control-port 5400 --ws-debug-port 5401 --skip-deps-check ``` Probe plugin: `data/plugins/LangBot__EBAEventProbe` when live credentials are available. Adapter live probe: ```bash uv run python -m py_compile tests/e2e/live_qqofficial_eba_probe.py QQOFFICIAL_APPID=... QQOFFICIAL_SECRET=... QQOFFICIAL_TOKEN=... uv run python tests/e2e/live_qqofficial_eba_probe.py ``` Webhook-mode probe: ```bash QQOFFICIAL_APPID=... QQOFFICIAL_SECRET=... QQOFFICIAL_TOKEN=... uv run python tests/e2e/live_qqofficial_eba_probe.py --webhook --host 0.0.0.0 --port 5312 ``` Evidence JSONL path: `data/temp/qqofficial_eba_probe.jsonl` for direct adapter live probe; plugin E2E evidence should use `data/temp/qqofficial_eba_plugin_probe.jsonl`. Destructive operations: none implemented. Blocked items: - `plugin-e2e-ui`: standalone probe plugin JSONL evidence is still pending; the observed live run reached LangBot core/pipeline but was not recorded by the EBA probe plugin. - `plugin-e2e-outbound`: waiting for visible QQ client verification of plugin `send_message`/`reply_message` output after a working model/provider is configured. - Inbound non-text media and platform lifecycle events require endpoint evidence before they can be marked complete.