OneBot v11 适配¶
Action 支持情况¶
默认启用¶
| Action | 状态 | 说明 |
|---|---|---|
get_supported_actions |
✅ | 获取当前 adapter 实际注册的 action 列表。 |
.get_supported_actions |
✅ | 兼容形式。 |
get_latest_events |
✅ | 获取 adapter 内最近缓存的事件,可传 limit。 |
get_status |
✅ | 返回在线状态。 |
get_version_info |
✅ | 返回 app / 协议版本信息。 |
get_version |
✅ | get_version_info 别名。 |
can_send_image |
✅ | 返回 {"yes": true}。 |
can_send_record |
✅ | 返回 {"yes": false},当前不支持语音 record 消息。 |
send_msg |
✅ | 根据 message_type 分发到私聊或群聊。 |
send_private_msg |
✅ | 发送私聊消息。 |
send_group_msg |
✅ | 发送频道消息,映射为 v11 group。 |
delete_msg |
✅ | 撤回消息。 |
recall_message |
✅ | delete_msg 别名。 |
get_msg |
✅ | 通过 message mapping 获取消息。 |
get_login_info |
✅ | 获取当前 bot 登录信息。 |
get_stranger_info |
✅ | 获取用户信息。 |
get_friend_list |
✅ | 获取好友/私聊相关列表。 |
set_friend_add_request |
✅ | 处理好友请求。 |
get_group_info |
✅ | 获取频道/群信息。 |
get_group_list |
✅ | 获取频道列表,映射为 group 列表。 |
get_group_member_info |
✅ | 获取成员信息。 |
set_group_name |
✅ | 修改频道名称。 |
cleanup_message_mapping |
✅ | 清理旧 message mapping。 |
需要显式启用¶
因为当前实现会把 v11 的群管理操作映射为 Oopz 域级别的操作,所以默认不启用以下 action,需要在创建 adapter 时打开对应开关:
| Action | 开关 | 映射行为 |
|---|---|---|
set_group_ban |
enable_area_scoped_group_ban=True |
是否启用群组禁言被当做整个域禁言的 action |
set_group_leave |
enable_set_group_leave_as_area_leave=True |
是否启用群组离开被当做整个域离开的 action |
set_group_kick |
enable_set_group_kick_as_area_kick=True |
是否启用群组踢人被当做整个域移除的 action |
Action 是否可用由
OneBotV11Adapter.SUPPORTED_ACTIONS和创建 adapter 时的开关共同决定,get_supported_actions返回当前实际可用列表。
快速使用¶
import os
import asyncio
from oopz_sdk import OopzBot, OopzConfig, OneBotV11Config
from oopz_sdk.adapters.onebot.v11 import OneBotV11Adapter, OneBotV11Server, OneBotV11ServerConfig
config = OopzConfig(
device_id=os.environ["OOPZ_DEVICE_ID"],
person_uid=os.environ["OOPZ_PERSON_UID"],
jwt_token=os.environ["OOPZ_JWT_TOKEN"],
private_key=os.environ["OOPZ_PRIVATE_KEY"],
onebot_v11=OneBotV11Config(
enabled=True,
host="127.0.0.1",
port=6700,
# HTTP action 服务
enable_http=True,
# 正向 WebSocket 服务,提供 /api、/event、/ 三种连接
enable_ws=True,
# access_token 非空时会启用鉴权
access_token="",
# HTTP POST 上报签名密钥,可选
secret="",
# HTTP POST 事件上报,可按需启用
enable_http_post=False,
http_post_urls=[
# "http://127.0.0.1:8080/onebot",
],
# 反向 WebSocket
enable_ws_reverse=True,
ws_reverse_url="ws://127.0.0.1:8080/ws",
# 如果对端区分 API / Event 连接,也可以分别配置:
# ws_reverse_api_url="ws://127.0.0.1:8080/api",
# ws_reverse_event_url="ws://127.0.0.1:8080/event",
),
)
bot = OopzBot(config)
asyncio.run(bot.run())
HTTP / WebSocket 路由¶
HTTP API¶
| 路由 | 方法 | 说明 |
|---|---|---|
/{action} |
POST |
调用 action,请求体直接作为 params。 |
/{action} |
GET |
调用 action,query 参数作为 params。 |
/{action}/ |
POST |
兼容尾斜杠形式。 |
/{action}/ |
GET |
兼容尾斜杠形式。 |
例如:
curl -X POST http://127.0.0.1:6700/send_private_msg \
-H "Content-Type: application/json" \
-d '{"user_id": 123456, "message": "hello"}'
如果需要获取状态,可以直接调用 get_status action:
curl http://127.0.0.1:6700/get_status
正向 WebSocket¶
| 路由 | 说明 |
|---|---|
/api |
API 连接,只处理 action 调用。 |
/event |
Event 连接,只推送事件。 |
/ |
Universal 连接,同时支持 action 调用和事件推送。 |
反向 WebSocket¶
反向 WebSocket 会按连接角色发送请求头。常见角色包括:
| 角色 | 说明 |
|---|---|
Universal |
一条连接同时处理 action 和事件,推荐用于 AstrBot / Hoshino / NoneBot 等常见接入场景。 |
API |
只处理 action 调用。 |
Event |
只推送事件。 |
连接时会携带请求头:
X-Client-Role: Universal
X-Self-ID: <adapter.self_id>
User-Agent: CQHttp/4.15.0
Authorization: Bearer <access_token> # access_token 非空时
HTTP / 正向 WebSocket 当前鉴权支持以下形式:
Authorization: Bearer <access_token>
?access_token=<access_token>
HTTP POST 事件上报¶
当 enable_http_post=True 且配置了 http_post_urls 时,adapter 会将 v11 事件通过 HTTP POST 上报到指定地址。
onebot_v11=OneBotV11Config(
enable_http_post=True,
http_post_urls=[
"http://127.0.0.1:8080/onebot",
],
secret="optional-secret",
)
上报请求会携带:
X-Self-ID: <adapter.self_id>
Content-Type: application/json
如果配置了 secret,还会携带:
X-Signature: sha1=<signature>
Action 响应格式¶
成功响应:
{
"status": "ok",
"retcode": 0,
"data": {},
"message": "",
"echo": "optional"
}
失败响应:
{
"status": "failed",
"retcode": 1400,
"data": null,
"message": "error message",
"echo": "optional"
}
常见错误码:
| retcode | 场景 |
|---|---|
1400 |
参数缺失、参数类型错误、payload 格式错误。 |
1401 |
鉴权失败。 |
1404 |
action 不支持或映射 ID 不存在。 |
1500 |
adapter 内部异常或 Oopz API 调用异常。 |
ID 策略¶
OneBot v11 生态通常要求以下字段是数字:
self_iduser_idgroup_idmessage_id
但 Oopz 的真实 ID 多数是字符串,因此 v11 adapter 使用 IdStore 将 Oopz 原始 ID 或复合 source 映射成稳定的数字 ID。
| OneBot 字段 | source 形态 | 说明 |
|---|---|---|
self_id |
self:<uid> |
当前 Oopz bot 用户 UID。 |
user_id |
user:<uid> |
Oopz 用户 UID。 |
group_id |
group:<area>:<channel> |
Oopz area + channel,避免跨域频道冲突。 |
message_id |
message:<area>:<channel>:<target>:<message_id> |
Oopz 消息 ID 复合上下文。 |
映射数据默认保存在:
.oopz_sdk/onebot_v11.sqlite3
v11 数字 ID 使用随机分配并持久化保存,范围限制在 JS 安全整数以内,避免 Web / JS 客户端出现精度问题。
Message mapping¶
除了 ID 映射,adapter 还会保存 OneBot message ID 到 Oopz message ID 的消息映射,用于:
get_msg查询消息;delete_msg/recall_message撤回消息;- 区分私聊消息和频道消息,避免私聊撤回误报为群撤回。
以下消息会进入 message mapping:
- adapter 收到并转换过的 Oopz 消息事件;
- adapter 自己通过
send_private_msg/send_group_msg发送成功的消息。
清理旧映射:
{
"action": "cleanup_message_mapping",
"params": {
"older_than_seconds": 604800
}
}
默认清理 7 天以前的记录。
事件¶
频道消息¶
Oopz 频道消息会转为 v11 group 消息:
{
"time": 1777629501,
"self_id": 97795367218,
"post_type": "message",
"message_type": "group",
"sub_type": "normal",
"message_id": 13579246,
"group_id": 12345678,
"user_id": 87654321,
"message": [
{"type": "text", "data": {"text": "hello"}}
],
"raw_message": "hello",
"font": 0,
"sender": {
"user_id": 87654321,
"nickname": "nickname"
},
"extra": {
"oopz_area_id": "OOPZ_AREA_ID",
"oopz_channel_id": "OOPZ_CHANNEL_ID",
"oopz_user_id": "OOPZ_USER_ID",
"oopz_message_id": "OOPZ_MESSAGE_ID"
}
}
私聊消息¶
Oopz 私聊消息会转为 v11 private 消息:
{
"time": 1777629501,
"self_id": 97795367218,
"post_type": "message",
"message_type": "private",
"sub_type": "friend",
"message_id": 13579246,
"user_id": 87654321,
"message": [
{"type": "text", "data": {"text": "hello"}}
],
"raw_message": "hello",
"font": 0,
"sender": {
"user_id": 87654321,
"nickname": "nickname"
},
"extra": {
"oopz_user_id": "OOPZ_USER_ID",
"oopz_target_id": "OOPZ_TARGET_ID",
"oopz_message_id": "OOPZ_MESSAGE_ID"
}
}
频道消息撤回¶
{
"time": 1777629501,
"self_id": 97795367218,
"post_type": "notice",
"notice_type": "group_recall",
"group_id": 12345678,
"user_id": 87654321,
"operator_id": 87654321,
"message_id": 13579246,
"extra": {
"oopz_area_id": "OOPZ_AREA_ID",
"oopz_channel_id": "OOPZ_CHANNEL_ID",
"oopz_user_id": "OOPZ_USER_ID",
"oopz_message_id": "OOPZ_MESSAGE_ID"
}
}
私聊消息撤回¶
{
"time": 1777629501,
"self_id": 97795367218,
"post_type": "notice",
"notice_type": "friend_recall",
"user_id": 87654321,
"message_id": 13579246,
"extra": {
"oopz_user_id": "OOPZ_USER_ID",
"oopz_target_id": "OOPZ_USER_ID",
"oopz_message_id": "OOPZ_MESSAGE_ID",
"oopz_channel_id": "OOPZ_PRIVATE_CHANNEL_ID"
}
}
好友请求事件¶
Oopz 好友请求会转为 v11 request.friend:
{
"time": 1777629501,
"self_id": 97795367218,
"post_type": "request",
"request_type": "friend",
"user_id": 87654321,
"comment": "nickname",
"flag": "oopz_friend_request:4454403:OOPZ_USER_ID",
"extra": {
"oopz_friend_request_id": 4454403,
"oopz_user_id": "OOPZ_USER_ID",
"oopz_name": "nickname",
"oopz_type": 1,
"oopz_avatar": "https://..."
}
}
发送消息¶
send_msg 发送消息¶
send_msg 根据 message_type 分发:
{
"action": "send_msg",
"params": {
"message_type": "group",
"group_id": 12345678,
"message": "hello"
}
}
message_type="private" 时走 send_private_msg,message_type="group" 时走 send_group_msg。
send_private_msg 发送私聊消息¶
{
"action": "send_private_msg",
"params": {
"user_id": 87654321,
"message": "hello"
}
}
如果 user_id 是 adapter 已映射过的 v11 数字 ID,adapter 会自动还原为 Oopz UID。
send_group_msg 发送频道消息¶
{
"action": "send_group_msg",
"params": {
"group_id": 12345678,
"message": "hello"
}
}
如果 group_id 未映射,需要补充 Oopz 上下文:
{
"action": "send_group_msg",
"params": {
"group_id": 12345678,
"oopz_area_id": "OOPZ_AREA_ID",
"oopz_channel_id": "OOPZ_CHANNEL_ID",
"message": "hello"
}
}
兼容字段:
| Oopz 上下文 | 兼容字段 |
|---|---|
oopz_area_id |
area、guild_id |
oopz_channel_id |
channel_id |
auto_escape¶
当 auto_escape=true 且 message 是字符串时,adapter 会把整段字符串当普通文本发送,不解析 CQ 码。
{
"action": "send_private_msg",
"params": {
"user_id": 87654321,
"message": "[CQ:at,qq=123] hello",
"auto_escape": true
}
}
CQ 码和消息段支持¶
字符串消息会解析部分 CQ 码:
| CQ 码 | 转换结果 |
|---|---|
[CQ:at,qq=123] |
Mention(123),发送前会尝试还原 Oopz UID。 |
[CQ:at,qq=all] |
MentionAll()。 |
[CQ:image,file=file:///path/to/a.png] |
本地图片。 |
[CQ:image,file=<file_key>] |
已上传 Oopz 图片资源。 |
[CQ:image,url=https://...] |
使用图片 URL / file 字段构造图片资源。 |
数组消息段也支持:
[
{"type": "text", "data": {"text": "hello "}},
{"type": "at", "data": {"qq": 12345678}},
{"type": "at", "data": {"qq": "all"}},
{"type": "image", "data": {"file": "file:///tmp/a.png"}}
]
当前支持的消息段:
| v11 消息段 | Oopz segment |
|---|---|
text |
Text / 普通字符串。 |
at |
Mention / MentionAll。 |
image |
Image。 |
未知数组消息段会被转成类似文本:
[type:{...data}]
get_msg 获取消息¶
get_msg 依赖 message mapping。只有以下消息能被查询:
- adapter 收到并保存过的事件消息;
- adapter 自己通过
send_private_msg/send_group_msg发送成功并保存过的消息。
{
"action": "get_msg",
"params": {
"message_id": 13579246
}
}
事件消息会尽量按原始 v11 payload 还原:
{
"time": 1777629501,
"message_type": "group",
"message_id": 13579246,
"real_id": 13579246,
"sender": {
"user_id": 87654321,
"nickname": "nickname"
},
"message": [
{"type": "text", "data": {"text": "hello"}}
]
}
自己发送的消息如果没有完整事件 payload,会返回最小结构;群聊消息会补充 group_id。
如果映射不存在,会返回失败响应。
delete_msg 撤回消息¶
{
"action": "delete_msg",
"params": {
"message_id": 13579246
}
}
recall_message 是 delete_msg 的别名。
如果 message mapping 存在:
detail_type="private"时调用私聊撤回;- 其他情况调用频道消息撤回。
如果 message mapping 不存在,但 message_id 能从 IdStore 反查出 message:<area>:<channel>:<target>:<message_id>,adapter 会尝试使用该 source 撤回。
如果仍然缺少上下文,频道消息撤回需要额外提供:
{
"action": "delete_msg",
"params": {
"message_id": 13579246,
"oopz_area_id": "OOPZ_AREA_ID",
"oopz_channel_id": "OOPZ_CHANNEL_ID"
}
}
私聊消息撤回可以提供:
{
"action": "delete_msg",
"params": {
"message_id": "OOPZ_MESSAGE_ID",
"user_id": 87654321
}
}
get_login_info 获取登录号信息¶
{
"action": "get_login_info",
"params": {}
}
返回:
extra会携带
{
"user_id": 97795367218,
"nickname": "bot name",
"extra": {
// models.Profile的字段
}
}
get_stranger_info 获取陌生人信息¶
{
"action": "get_stranger_info",
"params": {
"user_id": 87654321
}
}
返回 v11 标准字段,并将 Oopz 原始信息放入 extra:
{
"user_id": 87654321,
"nickname": "nickname",
"sex": "unknown",
"age": 0,
"extra": {
// models.Profile 的字段
}
}
get_friend_list 获取好友列表¶
{
"action": "get_friend_list",
"params": {}
}
返回:
[
{
"user_id": 87654321,
"nickname": "nickname",
"remark": "",
"extra": {
"oopz_user_id": "OOPZ_USER_ID"
}
}
]
set_friend_add_request 处理加好友请求¶
{
"action": "set_friend_add_request",
"params": {
"flag": "oopz_friend_request:4454403:OOPZ_USER_ID",
"approve": true,
"remark": "备注名"
}
}
群 / 频道接口¶
v11 的 group 在这里映射为 Oopz 的 area + channel。
get_group_list 获取群列表¶
{
"action": "get_group_list",
"params": {}
}
adapter 会获取已加入域,再遍历域内频道,生成 v11 group 列表:
[
{
"group_id": 12345678,
"group_name": "频道名称",
"member_count": 0,
"max_member_count": 0,
"extra": {
"oopz_area_id": "OOPZ_AREA_ID",
"oopz_channel_id": "OOPZ_CHANNEL_ID"
}
}
]
get_group_info 获取群信息¶
{
"action": "get_group_info",
"params": {
"group_id": 12345678
}
}
返回:
{
"group_id": 12345678,
"group_name": "频道名称",
"member_count": 0,
"max_member_count": 0,
"extra": {
"oopz_area_id": "OOPZ_AREA_ID",
"oopz_channel_id": "OOPZ_CHANNEL_ID"
}
}
get_group_member_info 获取群成员信息¶
{
"action": "get_group_member_info",
"params": {
"group_id": 12345678,
"user_id": 87654321
}
}
返回 v11 标准字段,并保留 Oopz 扩展信息:
sex, age, area, join_time, last_sent_time, unfriendly, title, title_expire_time,
card_changeable无法获取, 在返回值为固定值. role 目前只能区分域主和成员
{
"group_id": 12345678,
"user_id": 87654321,
"nickname": "nickname",
"card": "群昵称",
"sex": "unknown",
"age": 0,
"area": "",
"join_time": 0,
"last_sent_time": 0,
"level": "1",
"role": "member",
"unfriendly": false,
"title": "",
"title_expire_time": 0,
"card_changeable": false,
"shut_up_timestamp": 1777872164,
"extra": {
// models.UserInfo 的字段
}
}
get_group_member_list 获取群成员列表¶
{
"action": "get_group_member_list",
"params": {
"group_id": 18391569874
}
}
响应内容为 JSON 数组,每个元素的内容和上面的 get_group_member_info 接口相同,但是有些字段例如shut_up_timestamp无法获取,具体应以单独的成员信息为准。
set_group_name 设置群(频道)名¶
{
"action": "set_group_name",
"params": {
"group_id": 12345678,
"group_name": "新的频道名"
}
}
该 action 会将 v11 group 名称修改映射为 Oopz 频道名称修改。
如果 group_name 为空字符串,adapter 会直接返回成功但不做修改。
可选群管理接口¶
这些接口默认不启用,需要在创建 adapter 时显式打开。
set_group_ban 群组单人禁言¶
启用:enable_area_scoped_group_ban=True
调用:
{
"action": "set_group_ban",
"params": {
"group_id": 12345678,
"user_id": 87654321,
"duration": 600
}
}
说明: OneBot v11 的 duration 单位是秒而Oopz 禁言接口单位是分钟, 所以函数adapter 会向上取整,最少 1 分钟;
并且oopz禁言时长有固定区间, 提供的时长会被适配到该区间内, 可能会导致实际禁言时长与预期不完全一致。具体请参考Oopz API 文档关于禁言时长的说明。
set_group_kick 群组踢人¶
启用:enable_set_group_kick_as_area_kick
调用:
{
"action": "set_group_kick",
"params": {
"group_id": 12345678,
"user_id": 87654321,
"reject_add_request": false
}
}
set_group_leave 退出群组¶
启用:enable_set_group_leave_as_area_leave
调用:
{
"action": "set_group_leave",
"params": {
"group_id": 12345678
}
}
说明:
- 当前暂时未处理
is_dismiss参数
未知 Oopz 事件¶
无法精确映射到 v11 消息、通知或请求的 Oopz 事件,会作为 meta_event 透传:
{
"time": 1777629501,
"self_id": 97795367218,
"post_type": "meta_event",
"notice_type": "oopz",
"sub_type": "channel.update",
"oopz_event_name": "channel.update",
"oopz_event_type": 123,
"payload": {}
}
如果输入不是 Oopz Event 模型,则会返回更通用的 meta payload:
{
"time": 1777629501,
"self_id": 97795367218,
"post_type": "meta_event",
"meta_event_type": "oopz",
"payload": {}
}