mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-06-02 12:05:54 +00:00
feat: migrate aiocqhttp adapter to eba
This commit is contained in:
@@ -9,10 +9,13 @@ This directory records adapter-level migration details for the Event-Based Agent
|
||||
|
||||
## Adapter Documents
|
||||
|
||||
General acceptance checklist: [EBA Adapter Acceptance Checklist](./acceptance-checklist.md)
|
||||
|
||||
| Adapter | Status | Document |
|
||||
|---------|--------|----------|
|
||||
| Telegram | Migrated and live-tested | [Telegram](./telegram.md) |
|
||||
| Discord | Migrated and live-tested | [Discord](./discord.md) |
|
||||
| OneBot v11 / aiocqhttp | Migrated; Matcha-tested where supported | [OneBot v11 / aiocqhttp](./aiocqhttp.md) |
|
||||
|
||||
## Documentation Checklist
|
||||
|
||||
|
||||
195
docs/event-based-agents/adapters/acceptance-checklist.md
Normal file
195
docs/event-based-agents/adapters/acceptance-checklist.md
Normal file
@@ -0,0 +1,195 @@
|
||||
# EBA Adapter Acceptance Checklist
|
||||
|
||||
This checklist is the architecture-level acceptance standard for every Event-Based Agents platform adapter. It is not platform-specific. Adapter migration is not complete until the adapter has a written result against this checklist.
|
||||
|
||||
## Evidence Levels
|
||||
|
||||
Use these evidence levels consistently in adapter records:
|
||||
|
||||
| Level | Meaning | Can Mark Complete |
|
||||
|-------|---------|-------------------|
|
||||
| `plugin-e2e` | Real SDK plugin running through standalone runtime, LangBot core, the migrated adapter, and a real or simulator platform endpoint. | Yes |
|
||||
| `adapter-live` | Direct adapter probe connected to a real or simulator platform endpoint, bypassing plugin runtime. | No, auxiliary only |
|
||||
| `unit` | Unit/API-shape tests with mocked platform SDK objects or mocked APIs. | No, auxiliary only |
|
||||
| `not-supported` | Platform protocol or SDK has no equivalent capability. Must include reason and source. | Yes, as explicitly unsupported |
|
||||
| `blocked` | Intended capability could not be verified because of credentials, permissions, endpoint gaps, or simulator gaps. | No |
|
||||
|
||||
The primary acceptance path must be `plugin-e2e`. `adapter-live` and `unit` tests are useful, but they do not prove the EBA architecture path.
|
||||
|
||||
## Required Architecture Path
|
||||
|
||||
Every adapter must prove this full path:
|
||||
|
||||
```text
|
||||
Real platform / simulator UI
|
||||
-> platform SDK native event
|
||||
-> adapter event converter
|
||||
-> unified EBA event/entity/message types
|
||||
-> LangBot core event dispatch
|
||||
-> standalone SDK runtime
|
||||
-> real test plugin listener
|
||||
-> plugin calls platform APIs through SDK
|
||||
-> LangBot core API dispatch
|
||||
-> adapter API implementation
|
||||
-> real platform / simulator UI
|
||||
```
|
||||
|
||||
The test plugin must record JSONL evidence containing:
|
||||
|
||||
- event class and `event.type`
|
||||
- adapter name
|
||||
- chat type and chat ID
|
||||
- sender/user/group IDs with secrets redacted
|
||||
- message component list for received messages
|
||||
- API action name, input summary, result or error
|
||||
- raw unsupported/blocked reason when an item is skipped
|
||||
|
||||
## Required Message Receive Tests
|
||||
|
||||
For every adapter, inbound message conversion must be tested through `plugin-e2e` for each component the platform can receive. If the platform cannot create a component from the UI/simulator, record it as `blocked` with the endpoint limitation.
|
||||
|
||||
| Component | Required Receive Assertion |
|
||||
|-----------|----------------------------|
|
||||
| `Source` | Message ID and timestamp are present and stable enough for reply/get/delete APIs. |
|
||||
| `Plain` | Text is preserved exactly, including spaces and multi-line content. |
|
||||
| `At` | Mentioned user ID is converted to common `At.target`. |
|
||||
| `AtAll` | Broadcast mention is converted to common `AtAll`, if platform supports it. |
|
||||
| `Image` | Image ID, URL, path, or base64 is represented without leaking platform-native segment shape. |
|
||||
| `Voice` | Voice/audio component is represented as `Voice` when the platform exposes it. |
|
||||
| `File` | File name, ID/URL, and size are represented as `File` when available. |
|
||||
| `Quote` | Reply/quote source ID and origin content are represented when the platform exposes it. |
|
||||
| `Face` | Native emoji/sticker/dice/rps-like components are represented as `Face` or documented as platform-specific. |
|
||||
| `Forward` | Merged/forwarded messages are represented as `Forward` when the platform exposes structured content. |
|
||||
| `Unknown` | Unsupported native segments become `Unknown` or `PlatformSpecificEvent` data, not crashes. |
|
||||
| Mixed chain | A message containing multiple component types preserves order. |
|
||||
|
||||
The plugin must subscribe to `MessageReceivedEvent` and assert that `message_chain` contains common `langbot_plugin.api.entities.builtin.platform.message` components, not platform-native SDK objects.
|
||||
|
||||
## Required Message Send Tests
|
||||
|
||||
For every adapter, outbound message conversion must be tested through `plugin-e2e` by having the plugin call SDK platform APIs and verifying the platform UI/simulator receives the expected message.
|
||||
|
||||
| Component | Required Send Assertion |
|
||||
|-----------|-------------------------|
|
||||
| `Plain` | Text appears exactly on the platform. |
|
||||
| `At` | User mention renders as a mention or platform equivalent. |
|
||||
| `AtAll` | Broadcast mention renders or is explicitly unsupported. |
|
||||
| `Image` | URL, path, or base64 image sends and renders/downloads correctly. |
|
||||
| `Voice` | Voice/audio sends when supported. |
|
||||
| `File` | File sends with name and content/link when supported. |
|
||||
| `Quote` | Quoted reply points to the original message when supported. |
|
||||
| `Face` | Native emoji/sticker/dice/rps sends or is explicitly unsupported. |
|
||||
| `Forward` | Forward/merged-forward sends when supported; otherwise fallback behavior is documented. |
|
||||
| Mixed chain | A mixed chain preserves component order as closely as the platform allows. |
|
||||
|
||||
If a platform supports a component only in one direction, the adapter record must say so explicitly.
|
||||
|
||||
## Required Event Tests
|
||||
|
||||
The plugin must subscribe to every event declared in `manifest.yaml -> spec.supported_events` and record one of `plugin-e2e`, `not-supported`, or `blocked`.
|
||||
|
||||
| Event | Required Assertion |
|
||||
|-------|--------------------|
|
||||
| `message.received` | Real message reaches plugin as `MessageReceivedEvent`. |
|
||||
| `message.edited` | Edited message reaches plugin with message ID and new content, if declared. |
|
||||
| `message.deleted` | Deleted/recalled message reaches plugin with message ID and operator when available, if declared. |
|
||||
| `message.reaction` | Reaction add/remove reaches plugin with message ID, user, reaction, and direction, if declared. |
|
||||
| `feedback.received` | Feedback payload reaches plugin with feedback type and message/session IDs, if declared. |
|
||||
| `group.member_joined` | Join event reaches plugin with group and member. |
|
||||
| `group.member_left` | Leave/kick event reaches plugin with group, member, and kick flag. |
|
||||
| `group.member_banned` | Mute/ban event reaches plugin with group, member, operator, and duration. |
|
||||
| `group.info_updated` | Group metadata update reaches plugin with changed fields, if declared. |
|
||||
| `friend.request_received` | Friend request reaches plugin with request ID and message. |
|
||||
| `friend.added` | Friend-added event reaches plugin. |
|
||||
| `friend.removed` | Friend-removed event reaches plugin, if declared. |
|
||||
| `bot.invited_to_group` | Bot invite/join request reaches plugin with group and inviter/request ID. |
|
||||
| `bot.removed_from_group` | Bot removal reaches plugin with group and operator when available. |
|
||||
| `bot.muted` | Bot mute reaches plugin with duration. |
|
||||
| `bot.unmuted` | Bot unmute reaches plugin. |
|
||||
| `platform.specific` | At least one unmapped native event is delivered as structured platform-specific data, if declared. |
|
||||
|
||||
Do not declare an event in the manifest unless there is an implementation path and an acceptance entry.
|
||||
|
||||
## Required Common API Tests
|
||||
|
||||
The plugin must call every common API declared in `manifest.yaml -> spec.supported_apis.required` and `optional`. Each call must be recorded with input summary and result.
|
||||
|
||||
| API | Required Assertion |
|
||||
|-----|--------------------|
|
||||
| `send_message` | Plugin sends to private and group/channel targets where supported. |
|
||||
| `reply_message` | Plugin replies to the triggering message, with quoted mode tested when supported. |
|
||||
| `edit_message` | Plugin edits a bot-sent message, if declared. |
|
||||
| `delete_message` | Plugin deletes/recalls a bot-sent message, if declared and permissions allow. |
|
||||
| `forward_message` | Plugin forwards or emulates forwarding a real message, if declared. |
|
||||
| `get_message` | Plugin retrieves a real message and receives common `MessageReceivedEvent` shape. |
|
||||
| `get_group_info` | Plugin receives `UserGroup` with ID/name/count where available. |
|
||||
| `get_group_list` | Plugin receives joined groups/channels list where supported. |
|
||||
| `get_group_member_list` | Plugin receives list of `UserGroupMember` where supported. |
|
||||
| `get_group_member_info` | Plugin receives one member with role/display name where available. |
|
||||
| `set_group_name` | Plugin changes and restores a disposable group name, if declared. |
|
||||
| `mute_member` | Plugin mutes a disposable target, if declared. |
|
||||
| `unmute_member` | Plugin unmutes the same target, if declared. |
|
||||
| `kick_member` | Plugin kicks a disposable target only in destructive test mode, if declared. |
|
||||
| `leave_group` | Plugin leaves only in destructive test mode and only at the end, if declared. |
|
||||
| `get_user_info` | Plugin receives common `User` shape. |
|
||||
| `get_friend_list` | Plugin receives friend/contact list where supported. |
|
||||
| `approve_friend_request` | Plugin accepts/rejects a disposable friend request, if declared. |
|
||||
| `approve_group_invite` | Plugin accepts/rejects a disposable group invite, if declared. |
|
||||
| `upload_file` | Plugin uploads a real small file, if declared. |
|
||||
| `get_file_url` | Plugin resolves a real file ID to a URL, if declared. |
|
||||
| `call_platform_api` | Plugin calls every declared platform-specific action with safe parameters. |
|
||||
|
||||
Destructive APIs must be opt-in and documented with the exact target used.
|
||||
|
||||
## Platform-Specific API Tests
|
||||
|
||||
Every action listed in `manifest.yaml -> spec.platform_specific_apis` must have one acceptance entry:
|
||||
|
||||
- `plugin-e2e`: called by the plugin against the live/simulator endpoint.
|
||||
- `not-supported`: removed from manifest or explained if the platform SDK exposes it but this adapter intentionally does not.
|
||||
- `blocked`: endpoint did not implement it, permissions missing, or safe fixture unavailable.
|
||||
|
||||
Do not leave a platform-specific API in the manifest without a corresponding test record.
|
||||
|
||||
## Required Compatibility Tests
|
||||
|
||||
Each migrated adapter must also prove:
|
||||
|
||||
- Manifest supported events match `adapter.get_supported_events()`.
|
||||
- Manifest supported APIs match `adapter.get_supported_apis()`.
|
||||
- Manifest platform-specific actions match `PLATFORM_API_MAP`.
|
||||
- Legacy `FriendMessage` / `GroupMessage` listeners still work when the core registers them.
|
||||
- EBA listener dispatch prefers the most specific event class, then `EBAEvent`, then base `Event`.
|
||||
- Self-message filtering prevents bot echo loops without dropping edit/delete/moderation events needed for API tests.
|
||||
- `source_platform_object` is present for reply/debug but not required by plugins for common behavior.
|
||||
|
||||
## Required Documentation Per Adapter
|
||||
|
||||
Each adapter document must include:
|
||||
|
||||
- adapter directory and manifest name
|
||||
- config table
|
||||
- supported event table with evidence level per event
|
||||
- supported common API table with evidence level per API
|
||||
- platform-specific API table with evidence level per action
|
||||
- receive component table with evidence level per component
|
||||
- send component table with evidence level per component
|
||||
- exact test date
|
||||
- exact platform endpoint or simulator used
|
||||
- standalone runtime command
|
||||
- plugin path/name used for testing
|
||||
- evidence JSONL path
|
||||
- destructive operations performed or explicitly skipped
|
||||
- blocked items and reasons
|
||||
|
||||
## Acceptance Rule
|
||||
|
||||
An adapter can be marked migrated only when:
|
||||
|
||||
1. All declared events have `plugin-e2e` or `not-supported` evidence.
|
||||
2. All declared APIs have `plugin-e2e` or `not-supported` evidence.
|
||||
3. All platform-supported receive/send message components have `plugin-e2e` evidence.
|
||||
4. Unit tests cover conversion and API-shape boundaries.
|
||||
5. The adapter document lists every blocked or skipped item honestly.
|
||||
|
||||
If any declared capability is only covered by `adapter-live` or `unit`, the adapter status must remain partial.
|
||||
137
docs/event-based-agents/adapters/aiocqhttp.md
Normal file
137
docs/event-based-agents/adapters/aiocqhttp.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# OneBot v11 / aiocqhttp EBA Adapter
|
||||
|
||||
## Status
|
||||
|
||||
OneBot v11 has been migrated to the EBA adapter directory:
|
||||
|
||||
```text
|
||||
src/langbot/pkg/platform/adapters/aiocqhttp/
|
||||
├── adapter.py
|
||||
├── api_impl.py
|
||||
├── event_converter.py
|
||||
├── manifest.yaml
|
||||
├── message_converter.py
|
||||
├── platform_api.py
|
||||
├── types.py
|
||||
└── onebot.svg
|
||||
```
|
||||
|
||||
The EBA adapter is registered as `aiocqhttp-eba`. The legacy adapter remains at `src/langbot/pkg/platform/sources/aiocqhttp.py`.
|
||||
|
||||
## Configuration
|
||||
|
||||
| Field | Required | Default | Description |
|
||||
|-------|----------|---------|-------------|
|
||||
| `host` | Yes | `0.0.0.0` | Host for the reverse WebSocket server that the OneBot endpoint connects to. |
|
||||
| `port` | Yes | `2280` | Reverse WebSocket listen port. |
|
||||
| `access-token` | No | `""` | OneBot access token, if the endpoint is configured to use one. |
|
||||
|
||||
## Events
|
||||
|
||||
The adapter declares these EBA events:
|
||||
|
||||
- `message.received`
|
||||
- `message.deleted`
|
||||
- `group.member_joined`
|
||||
- `group.member_left`
|
||||
- `group.member_banned`
|
||||
- `friend.request_received`
|
||||
- `friend.added`
|
||||
- `bot.invited_to_group`
|
||||
- `bot.removed_from_group`
|
||||
- `bot.muted`
|
||||
- `bot.unmuted`
|
||||
- `platform.specific`
|
||||
|
||||
`platform.specific` is used for OneBot notice/request/meta events that do not yet have a common EBA event type, such as group admin changes, group file uploads, pokes, honor changes, and group join requests from non-bot users.
|
||||
|
||||
## Common APIs
|
||||
|
||||
| API | Status | Notes |
|
||||
|-----|--------|-------|
|
||||
| `send_message` | Supported | Supports private and group text, mentions, images, voice, files, faces, and flattened forwards. Group merged forwards are sent through OneBot forward APIs when possible. |
|
||||
| `reply_message` | Supported | Uses the original OneBot event and can prepend a reply segment. |
|
||||
| `edit_message` | Not supported | OneBot v11 has no standard message edit action. |
|
||||
| `delete_message` | Supported | Uses `delete_msg`; permission depends on endpoint and group role. |
|
||||
| `forward_message` | Supported | Emulates forward by fetching the source message with `get_msg` and sending its content to the target chat. |
|
||||
| `get_message` | Supported | Uses `get_msg` and converts the response into `MessageReceivedEvent`. |
|
||||
| `get_group_info` | Supported | Uses `get_group_info`. |
|
||||
| `get_group_list` | Supported | Uses `get_group_list`. |
|
||||
| `get_group_member_list` | Supported | Uses `get_group_member_list`. |
|
||||
| `get_group_member_info` | Supported | Uses `get_group_member_info`. |
|
||||
| `set_group_name` | Supported | Uses `set_group_name`; may be unsupported by mock endpoints. |
|
||||
| `get_user_info` | Supported | Uses `get_stranger_info`. |
|
||||
| `get_friend_list` | Supported | Uses `get_friend_list`. |
|
||||
| `approve_friend_request` | Supported | Uses `set_friend_add_request`. |
|
||||
| `approve_group_invite` | Supported | Uses `set_group_add_request` with `sub_type=invite`. |
|
||||
| `upload_file` | Not supported | OneBot v11 has endpoint-specific file upload extensions but no portable standalone upload action. |
|
||||
| `get_file_url` | Not supported | OneBot v11 file URL resolution is endpoint-specific. Use `call_platform_api("get_image")`, `get_record`, or endpoint extensions when available. |
|
||||
| `mute_member` | Supported | Uses `set_group_ban`. |
|
||||
| `unmute_member` | Supported | Uses `set_group_ban` with duration `0`. |
|
||||
| `kick_member` | Supported | Destructive; test only with disposable members. |
|
||||
| `leave_group` | Supported | Destructive; should run last in live tests. |
|
||||
| `call_platform_api` | Supported | See below. |
|
||||
|
||||
## Platform-Specific APIs
|
||||
|
||||
`call_platform_api(action, params)` supports:
|
||||
|
||||
- `get_login_info`
|
||||
- `get_status`
|
||||
- `get_version_info`
|
||||
- `get_group_honor_info`
|
||||
- `set_group_card`
|
||||
- `set_group_special_title`
|
||||
- `set_group_admin`
|
||||
- `set_group_whole_ban`
|
||||
- `send_group_forward_msg`
|
||||
- `get_forward_msg`
|
||||
- `get_record`
|
||||
- `get_image`
|
||||
- `can_send_image`
|
||||
- `can_send_record`
|
||||
|
||||
## Message Conversion Notes
|
||||
|
||||
Incoming OneBot segments are converted into common `MessageChain` components before LangBot core/plugin dispatch:
|
||||
|
||||
- `text` -> `Plain`
|
||||
- `at` -> `At` / `AtAll`
|
||||
- `image` -> `Image` or `Face` for OneBot emoji-package images
|
||||
- `record` -> `Voice`
|
||||
- `file` -> `File`
|
||||
- `reply` -> `Quote`
|
||||
- `face`, `rps`, `dice` -> `Face`
|
||||
- unsupported segments -> `Unknown`
|
||||
|
||||
Outgoing `MessageChain` components are converted back into `aiocqhttp.Message` segments. Base64 media strings are normalized to OneBot `base64://...` format.
|
||||
|
||||
## Live Test Record
|
||||
|
||||
The direct live probe is:
|
||||
|
||||
```bash
|
||||
PYTHONPATH=/Users/qinjunyan/code/projects/langbot/langbot-plugin-sdk/src \
|
||||
uv run python tests/e2e/live_aiocqhttp_eba_probe.py --host 127.0.0.1 --port 2280
|
||||
```
|
||||
|
||||
It starts the reverse WebSocket adapter directly, records observed EBA events to `data/temp/aiocqhttp_eba_live_probe.jsonl`, waits for a real Matcha or OneBot message, then tries reply/send/get/delete/group/user/platform API calls as far as the endpoint supports them.
|
||||
|
||||
Verified on May 10, 2026 with local Matcha connected to `ws://127.0.0.1:2280/ws`:
|
||||
|
||||
- Real inbound group message converted to `MessageReceivedEvent`.
|
||||
- Real lifecycle connection converted to `PlatformSpecificEvent`.
|
||||
- Real reply API succeeded and rendered a quoted bot reply in Matcha.
|
||||
- Real proactive send API succeeded and rendered a bot group message in Matcha.
|
||||
- Real outgoing component sweep succeeded for text, `At`, `AtAll`, `Face`, and base64 `Image`.
|
||||
- Real `get_message`, `get_group_info`, `get_login_info`, `get_status`, `get_version_info`, `can_send_image`, and `can_send_record` calls succeeded against Matcha.
|
||||
- Unit conversion and API-shape tests passed for `Plain`, `At`, `AtAll`, `Image`, `Voice`, `File`, `Quote`, `Face`, `rps`, `dice`, `Forward`, `Unknown`, private/group message events, delete notices, group join/leave/ban notices, bot mute notices, friend requests, group invites, friend added notices, dispatch specificity, send, reply, delete, forward, get message, group APIs, user APIs, request approval APIs, moderation APIs, leave group, unsupported file APIs, and all declared `call_platform_api` actions.
|
||||
|
||||
Skipped or residual live-test items:
|
||||
|
||||
- `edit_message`: not implemented because OneBot v11 has no standard edit action.
|
||||
- `upload_file` and `get_file_url`: not implemented as common APIs because portable OneBot v11 file upload/download URL semantics are endpoint-specific.
|
||||
- `kick_member` and `leave_group`: destructive; run only with explicit `--destructive` and disposable Matcha/OneBot state.
|
||||
- `group.info_updated`, message reactions, and message edits are not declared because OneBot v11 does not provide standard equivalents for them.
|
||||
- Matcha returned `ActionFailed` for outgoing `File` segment rendering and did not support merged-forward actions in this run. The adapter keeps the conversion/API implementations because they are valid OneBot/NapCat-style capabilities, but the Matcha live probe records them as skipped.
|
||||
- Matcha returned an empty `get_group_member_list` for the test group, so `get_group_member_info`, mute/unmute, kick, and leave were covered by unit/API-shape tests only in this run.
|
||||
Reference in New Issue
Block a user