跳转至

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_id
  • user_id
  • group_id
  • message_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_msgmessage_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 areaguild_id
oopz_channel_id channel_id

auto_escape

auto_escape=truemessage 是字符串时,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_messagedelete_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": {}
}