简体中文 English 繁體中文 日本語 Русский
本文为静态镜像,内容以交互版为准 在交互式文档中心打开 →

ErisPulse Request Operation Specification

This document defines the standardized specification for request event operations in the ErisPulse adapter, including the field requirements for request events, the usage of Request DSL, and adapter implementation requirements.

1. Overview

The request event (type: "request") is a special event type defined in the OneBot12 standard, representing a request that requires the Bot to make a decision (such as friend requests or group invitations).

Unlike message events, request events require bidirectional interaction:

  1. Receiving: The adapter converts the native platform request into a standard request event.
  2. Responding: The module executes operations through the Request DSL or Event.approve()/Event.reject().
Platform native request event
    │
    ▼
Converter.convert()        ← Adapter implementation (forward conversion)
    │
    ▼
Standard request event (with request_id)
    │
    ├─→ Module handler @request.on_friend_request()
    │       │
    │       ├─→ event.approve()     ← Approve the request
    │       └─→ event.reject()      ← Reject the request
    │               │
    │               ▼
    │       adapter.Request(request_id).accept()
    │               │
    │               ▼
    │       BaseAdapter.Request.accept()  ← Adapter override
    │               │
    │               ▼
    │       Platform API call
    │
    └─→ Or directly through adapter operation
            await adapter.Request("req_id").accept()

2. Request Event Field Requirements

2.1 Standard Fields

In addition to the standard OneBot12 fields, the request event must also include the following fields:

Field Type Required Description
request_id string Strongly Recommended Request identifier, used for approve/reject operations
user_id string Yes ID of the requester
user_nickname string No Nickname of the requester
comment string No Request comment

2.2 request_id Field

request_id is the core identifier for request operations:

2.3 Request Event Example

{
  "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": "Please add as a friend",
  "request_id": "flag_abc123",
  "onebot11_raw": {...},
  "onebot11_raw_type": "request"
}

3. Request DSL

3.1 Method Chaining

The Request class provides a method chaining interface consistent with the Send style:

# Basic usage
await adapter.Request("req_id").accept()
await adapter.Request("req_id").reject()

# Specify Bot account
await adapter.Request("req_id").Using("bot1").accept()

# Attach remarks (via kwargs)
await adapter.Request("req_id").accept(comment="Welcome")
await adapter.Request("req_id").reject(comment="Temporarily not adding")

# Combinatorial usage
await adapter.Request("req_id").Using("bot1").accept(comment="Welcome")

3.2 Method List

Method Description Return Value
Using(account_id) Specify the Bot account to perform the operation RequestDSL (supports method chaining)
accept(**kwargs) Accept the request asyncio.Task (await returns standard response)
reject(**kwargs) Reject the request asyncio.Task (await returns standard response)

3.3 Return Value Format

The operation returns a standard API response format:

Success:

{
    "status": "ok",
    "retcode": 0,
    "data": null,
    "message_id": "",
    "message": ""
}

Failure:

{
    "status": "failed",
    "retcode": 34001,
    "data": null,
    "message_id": "",
    "message": "Request has expired or does not exist"
}

Not Implemented (adapter did not override accept/reject):

{
    "status": "failed",
    "retcode": 10002,
    "data": null,
    "message_id": "",
    "message": "Platform MyAdapter does not implement request operation (accept)"
}

4. Event Convenience Methods

The Event wrapper class provides convenience methods suitable for use in request event handlers:

from ErisPulse.Core.Event import request

@request.on_friend_request()
async def handle_friend_request(event):
    # Get the request ID
    request_id = event.get_request_id()
    if not request_id:
        print("Warning: Request event missing request_id")
        return
    
    # Approve the request
    result = await event.approve()
    
    # Or reject the request
    # result = await event.reject(comment="Temporarily not adding friends")
    
    # Check the result
    if result.get("status") == "ok":
        print("Operation successful")
    else:
        print(f"Operation failed: {result.get('message')}")

4.1 Event Method List

Method Description Return Value
get_request_id() Get the request ID str
approve(comment=None) Approve the current request event Standard response format
reject(comment=None) Reject the current request event Standard response format

5. Adapter Implementation Requirements

5.1 Converter Requirements

The adapter's converter must correctly set the request_id field when converting request events:

def convert_request_event(self, raw_event: dict) -> dict:
    """Convert platform-native request events"""
    return {
        "id": self._generate_event_id(raw_event),
        "time": int(time.time()),
        "type": "request",
        "detail_type": self._map_request_type(raw_event),  # "friend" or "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),  # ← Key field
        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:
    """
    Extract request ID from platform-native event
    
    Prefer native platform request identifier; generate unique ID if not available
    """
    # Prefer native platform ID
    if flag := raw_event.get("flag"):
        return str(flag)
    if request_key := raw_event.get("request_key"):
        return str(request_key)
    
    # Fallback: generate unique 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 Inner Class Implementation

The adapter can implement accept and reject methods in the Request inner class:

from ErisPulse.Core import BaseAdapter, RequestDSL

class MyAdapter(BaseAdapter):
    
    class Request(RequestDSL):
        """MyPlatform request operation implementation"""
        
        def accept(self, **kwargs):
            """
            Approve request
            
            :param kwargs: Additional parameters, such as 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"Request operation failed: {e}",
                    }
            
            return self._create_task(_do())
        
        def reject(self, **kwargs):
            """Reject request"""
            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"Request operation failed: {e}",
                    }
            
            return self._create_task(_do())

5.3 Platform Does Not Support Request Operations

If the platform does not support friend requests/group invitations (e.g., some platforms handle requests automatically), the adapter can:

  1. Do not override the Request inner class: Use the default implementation from the base class, returning retcode=10002 when calling accept()/reject()
  2. Skip generating request_id during conversion: Do not generate request_id, causing event.approve() to raise ValueError
  3. Log warnings: Record warnings in accept/reject and return appropriate error codes

5.4 Summary: Send and Request in Parallel

The adapter has two parallel DSL inner classes, each with distinct responsibilities:

BaseAdapter
├── Send(SendDSL)     ← Message sending
│   ├── Raw_ob12()    ← Must be implemented
│   ├── Text()        ← Recommended implementation
│   └── Image()       ← Implemented as needed
│
└── Request(RequestDSL) ← Request operations
    ├── accept()        ← Implemented as needed
    └── reject()        ← Implemented as needed

5.5 Adapter __init__ Considerations

When overriding the Request inner class's __init__, ensure that parameters are passed through and super().__init__() is called. See Adapter Development Introduction - __init__ Considerations (Request is similar, with parameters adapter, request_id, account_id).

6. Adapter Implementation Checklist

Basic Requirements

Request Event Transformation

Request Handling

7. Error Code Extension

The adapter implementation layer related to request operations recommends error codes (following API Response Standard §3.2, falling within the low three digits of the 34xxx platform error segment for customization):

Error Code Error Name Description
34001 Request Not Found The request does not exist or has expired
34002 Request Already Handled The request has already been processed
34003 Request Not Supported The platform does not support this type of request operation
34004 Permission Denied The Bot is not authorized to process this request (returned by the platform)

Boundary with Framework Code: The above 340xx codes indicate request processing failures returned by the platform/adapter;
When the ErisPulse framework disables a module's request action in scope.actions, it directly returns 34601 (Action Denied) (see API Response Standard §5.3) before calling the adapter.
The two are not mutually replaceable: first pass through the 34601 framework gate, then fall into the platform layer 340xx error.