ErisPulse 请求操作规范
本文档定义了 ErisPulse 适配器中请求事件操作的标准化规范,包括请求事件的字段要求、Request DSL 的使用方式和适配器实现要求。
1. 概述
请求事件(type: "request")是 OneBot12 标准中定义的特殊事件类型,代表需要 Bot 做出决策的请求(如好友请求、群邀请等)。
与消息事件不同,请求事件需要双向交互:
- 接收:适配器将平台原生请求转换为标准请求事件
- 响应:模块通过
RequestDSL 或Event.approve()/Event.reject()执行操作
平台原生请求事件
│
▼
Converter.convert() ← 适配器实现(正向转换)
│
▼
标准请求事件 (含 request_id)
│
├─→ 模块处理器 @request.on_friend_request()
│ │
│ ├─→ event.approve() ← 同意请求
│ └─→ event.reject() ← 拒绝请求
│ │
│ ▼
│ adapter.Request(request_id).accept()
│ │
│ ▼
│ BaseAdapter.Request.accept() ← 适配器重写
│ │
│ ▼
│ 平台 API 调用
│
└─→ 或直接通过适配器操作
await adapter.Request("req_id").accept()
2. 请求事件字段要求
2.1 标准字段
请求事件除必须包含 OneBot12 标准字段外,还需包含以下字段:
| 字段 | 类型 | 必选 | 说明 |
|---|---|---|---|
request_id |
string | 强烈推荐 | 请求标识符,用于同意/拒绝操作 |
user_id |
string | 是 | 请求发起者ID |
user_nickname |
string | 否 | 请求发起者昵称 |
comment |
string | 否 | 请求附言 |
2.2 request_id 字段
request_id 是请求操作的核心标识符:
- 用途:标识一个可操作的请求,供
RequestDSL 使用 - 生成规则:
- 优先使用平台原生的请求标识(如 OneBot11 的
flag字段、Telegram 的chat_invite_link等) - 如果平台没有原生请求ID,适配器应生成一个唯一标识(建议格式:
{platform}_{timestamp}_{user_id})
- 优先使用平台原生的请求标识(如 OneBot11 的
- 唯一性:在同一平台范围内应保持唯一
- 缺失行为:当
request_id缺失时,event.approve()/event.reject()将抛出ValueError
2.3 请求事件示例
{
"id": "evt_123456",
"time": 1752241225,
"type": "request",
"detail_type": "friend",
"platform": "onebot11",
"self": {
"platform": "onebot11",
"user_id": "bot_123"
},
"user_id": "user_456",
"user_nickname": "YingXinche",
"comment": "请加好友",
"request_id": "flag_abc123",
"onebot11_raw": {...},
"onebot11_raw_type": "request"
}
3. Request DSL
3.1 链式调用
Request 提供与 Send 风格一致的链式调用接口:
# 基本用法
await adapter.Request("req_id").accept()
await adapter.Request("req_id").reject()
# 指定 Bot 账号
await adapter.Request("req_id").Using("bot1").accept()
# 附带备注(通过 kwargs)
await adapter.Request("req_id").accept(comment="欢迎")
await adapter.Request("req_id").reject(comment="暂不添加")
# 组合使用
await adapter.Request("req_id").Using("bot1").accept(comment="欢迎")
3.2 方法列表
| 方法 | 说明 | 返回值 |
|---|---|---|
Using(account_id) |
指定执行操作的 Bot 账号 | RequestDSL(支持链式调用) |
accept(**kwargs) |
同意请求 | asyncio.Task(await 后返回标准响应) |
reject(**kwargs) |
拒绝请求 | asyncio.Task(await 后返回标准响应) |
3.3 返回值格式
操作返回标准 API 响应格式:
成功:
{
"status": "ok",
"retcode": 0,
"data": null,
"message_id": "",
"message": ""
}
失败:
{
"status": "failed",
"retcode": 34001,
"data": null,
"message_id": "",
"message": "请求已过期或不存在"
}
未实现(适配器未重写 accept/reject):
{
"status": "failed",
"retcode": 10002,
"data": null,
"message_id": "",
"message": "平台 MyAdapter 未实现请求操作 (accept)"
}
4. Event 便捷方法
Event 包装类提供了便捷方法,适合在请求事件处理器中使用:
from ErisPulse.Core.Event import request
@request.on_friend_request()
async def handle_friend_request(event):
# 检查请求ID
request_id = event.get_request_id()
if not request_id:
print("警告:请求事件缺少 request_id")
return
# 同意请求
result = await event.approve()
# 或拒绝请求
# result = await event.reject(comment="暂不添加好友")
# 检查结果
if result.get("status") == "ok":
print("操作成功")
else:
print(f"操作失败: {result.get('message')}")
4.1 Event 方法列表
| 方法 | 说明 | 返回值 |
|---|---|---|
get_request_id() |
获取请求ID | str |
approve(comment=None) |
同意当前请求事件 | 标准响应格式 |
reject(comment=None) |
拒绝当前请求事件 | 标准响应格式 |
5. 适配器实现要求
5.1 转换器要求
适配器的转换器在转换请求事件时,必须正确设置 request_id 字段:
def convert_request_event(self, raw_event: dict) -> dict:
"""转换平台原生请求事件"""
return {
"id": self._generate_event_id(raw_event),
"time": int(time.time()),
"type": "request",
"detail_type": self._map_request_type(raw_event), # "friend" 或 "group"
"platform": self._platform_name,
"self": {
"platform": self._platform_name,
"user_id": str(self._bot_id),
},
"user_id": str(raw_event.get("user_id", "")),
"user_nickname": raw_event.get("nickname", ""),
"comment": raw_event.get("message", ""),
"request_id": self._extract_request_id(raw_event), # ← 关键字段
f"{self._platform_name}_raw": raw_event,
f"{self._platform_name}_raw_type": raw_event.get("type", ""),
}
def _extract_request_id(self, raw_event: dict) -> str:
"""
从平台原生事件提取请求ID
优先使用平台原生的请求标识,若无则生成唯一ID
"""
# 优先使用平台原生ID
if flag := raw_event.get("flag"):
return str(flag)
if request_key := raw_event.get("request_key"):
return str(request_key)
# 兜底:生成唯一ID
import hashlib
raw = f"{self._platform_name}_{raw_event.get('user_id')}_{raw_event.get('timestamp')}"
return hashlib.md5(raw.encode()).hexdigest()
5.2 Request 内部类实现
适配器在 Request 内部类中重写 accept 和 reject 即可:
from ErisPulse.Core import BaseAdapter, RequestDSL
class MyAdapter(BaseAdapter):
class Request(RequestDSL):
"""MyPlatform 请求操作实现"""
def accept(self, **kwargs):
"""
同意请求
:param kwargs: 扩展参数,如 comment="备注"
:return: asyncio.Task
"""
async def _do():
try:
result = await self._adapter.call_api(
endpoint="/set_request",
request_id=self._request_id,
approve=True,
**kwargs,
)
return {
"status": "ok" if result.get("code") == 0 else "failed",
"retcode": result.get("code", 0),
"data": None,
"message_id": "",
"message": result.get("message", ""),
}
except Exception as e:
return {
"status": "failed",
"retcode": 34001,
"data": None,
"message_id": "",
"message": f"请求操作失败: {e}",
}
return self._create_task(_do())
def reject(self, **kwargs):
"""拒绝请求"""
async def _do():
try:
result = await self._adapter.call_api(
endpoint="/set_request",
request_id=self._request_id,
approve=False,
**kwargs,
)
return {
"status": "ok" if result.get("code") == 0 else "failed",
"retcode": result.get("code", 0),
"data": None,
"message_id": "",
"message": result.get("message", ""),
}
except Exception as e:
return {
"status": "failed",
"retcode": 34001,
"data": None,
"message_id": "",
"message": f"请求操作失败: {e}",
}
return self._create_task(_do())
5.3 平台不支持请求操作
如果平台本身不支持好友请求/群邀请操作(如某些平台自动处理请求),适配器可以:
- 不重写
Request内部类:使用基类默认实现,调用accept()/reject()时返回retcode=10002 - **在转换时跳过
request_id**:不生成request_id,让event.approve()抛出ValueError - 记录日志:在
accept/reject中记录警告并返回适当错误码
5.4 总结:Send 与 Request 并行
适配器有两个并行的 DSL 内部类,各司其职:
BaseAdapter
├── Send(SendDSL) ← 消息发送
│ ├── Raw_ob12() ← 必须实现
│ ├── Text() ← 推荐实现
│ └── Image() ← 按需实现
│
└── Request(RequestDSL) ← 请求操作
├── accept() ← 按需实现
└── reject() ← 按需实现
5.5 适配器 __init__ 注意事项
重写 Request 内部类的 __init__ 时,必须透传参数并调用 super().__init__(),详见 适配器开发入门 - __init__ 注意事项(Request 同理,参数为 adapter, request_id, account_id)。
6. 适配器实现检查清单
基础要求
- 若重写了
__init__,已调用super().__init__()(确保 Send / Request 工厂初始化)
请求事件转换
- 请求事件包含
request_id字段(强烈推荐) -
detail_type正确映射为"friend"或"group" - 保留平台原始数据在
{platform}_raw字段中 -
request_id生成规则有文档说明
请求操作
-
Request内部类已实现(如平台支持请求操作) -
accept()方法已实现 -
reject()方法已实现 - 操作返回标准 API 响应格式
- 不支持的操作返回
retcode=10002 - 网络错误返回
retcode=33xxx(遵循 API 响应标准)
7. 错误码扩展
请求操作相关的适配器实现层推荐错误码(遵循 API 响应标准 §3.2,
落在 34xxx 平台错误段的低三位自定义):
| 错误码 | 错误名 | 说明 |
|---|---|---|
| 34001 | Request Not Found | 请求不存在或已过期 |
| 34002 | Request Already Handled | 请求已被处理 |
| 34003 | Request Not Supported | 平台不支持该类型的请求操作 |
| 34004 | Permission Denied | Bot 无权处理此请求(平台返回) |
与框架码的边界:以上
340xx是平台/适配器返回的请求处理失败; ErisPulse 框架在scope.actions禁用某模块的 request 动作时,在调用适配器之前 直接返回34601(Action Denied,见 API 响应标准 §5.3), 两者互不替代:先过34601框架闸口,再落到平台层340xx错误。