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

ErisPulse Send Method Specification

This document defines the naming conventions, parameter specifications, and reverse conversion requirements for the Send class methods in the ErisPulse adapter.

0. Keyword Conventions

The keywords MUST, SHOULD, and MAY in this document are interpreted as follows (referencing RFC 2119):

Keyword Meaning Consequence of Violation
MUST Mandatory requirement; framework behavior or cross-platform consistency depends on it Adapter is considered non-compliant; module code may not work
SHOULD Strongly recommended; unless there is sufficient reason, follow it Deviation must be explained in adapter documentation with reasons and alternative behavior
MAY Optional; decide based on platform capability None

1. Standard Method Naming

All send methods use PascalCase naming, with the first letter capitalized.

1.1 Standard Send Methods

Method Name Description Parameter Type Implementation Requirement
Text Send text message str MUST
Image Send image str | bytes MUST (built-in in base class, see §6.4)
Voice Send voice str | bytes MUST (built-in in base class; downgrade per §2.1.5 if platform does not support voice)
Video Send video str | bytes MUST (built-in in base class; downgrade per §2.1.5 if platform does not support video)
File Send file str | bytes, filename: str | None = None MUST (built-in in base class)
At @ User/Group str (user_id) Modifier method, optional
Face Send emoji str (emoji) MAY
Reply Reply to message str (message_id) Modifier method, optional
Forward Forward message str (message_id) MAY
Markdown Send Markdown message str MAY
HTML Send HTML message str MAY
Card Send card message dict MAY

Standard methods (Text/Image/Voice/Video/File) are built-in in the base class SendDSL and default to delegating to Raw_ob12. Adapters do not need to re-implement these methods to obtain type signatures; only override individual methods when platform-specific logic is required (see §6.4).

1.2 Chained Modifier Methods

Method Name Description Parameter Type
At @ User (can be called multiple times) str (user_id)
AtAll @ All Members None
Reply Reply to message str (message_id)

1.3 Protocol Methods

Method Name Description Required?
Raw_ob12 Send OneBot12 formatted message segment MUST

Raw_ob12 is a required method. This is one of the adapter's core responsibilities: receiving OneBot12 standard message segments and converting them into native platform API calls. Raw_ob12 serves as the unified entry point for reverse conversion (OneBot12 → Platform), ensuring modules can send messages directly using standard message segments without relying on platform-specific methods.

Behavior when Raw_ob12 is not overridden: The default base class implementation logs an error-level message and returns a standard error response format (status: "failed", retcode: 10002), prompting adapter developers to implement this method.

If adapters need to support sending non-OneBot12 formatted raw data (such as platform-specific JSON, XML, etc.), the following naming convention is recommended:

Recommended Method Name Description
Raw_json Send arbitrary JSON data
Raw_xml Send arbitrary XML data

Note: These methods are not provided by the base class, nor are they mandatory. They are only recommended naming conventions, and adapters can define them as needed. If an adapter does not support these formats, there is no need to define them.

Message Builder (MessageBuilder): ErisPulse provides the MessageBuilder utility class for convenient construction of OneBot12 message segment lists, used in conjunction with Raw_ob12. See the MessageBuilder section.

2. Detailed Parameter Specification

2.1 Media Message Sending Protocol (Image / Voice / Video / File)

This section defines the unified protocol standard for media sending: modules use the same code to call the four media methods, and adapters are responsible for converting the various forms of the file parameter into native platform upload/send behavior.

2.1.1 Valid Forms of the file Parameter

Form Example Adapter Requirement
HTTP(S) URL https://example.com/image.jpg MUST accept
Local file path /path/to/file.jpg, C:\path\to\file.jpg MUST accept
Binary data b"\x89PNG..." MUST accept
file:// URI file:///path/to/file.jpg SHOULD accept (can be forwarded as local path)
Base64 string / Data URI iVBORw0KGgo=..., data:image/png;base64,... SHOULD accept (compatible with OneBot12 ecosystem conventions)

Adapters must behave consistently across the three required forms—regardless of whether modules pass a URL, path, or bytes, the received message should be the same. If the platform cannot directly use a certain form (e.g., the platform API does not support referencing external URLs), the adapter should download/read and upload it, not require the module to retry with a different form.

2.1.2 Form Determination Order

When implementing media parameter handling, adapters should determine the form in the following order:

  1. bytes type → Upload directly
  2. String starts with http:// / https:// → Handle as URL (reference directly or download and upload, depending on platform capability)
  3. String starts with file:// → Strip prefix and handle as local path
  4. Other strings → Handle as local path (read and upload if exists; return standard error response if not)
def _resolve_media(self, file: "str | bytes") -> bytes:
    """Form determination and normalization (example)"""
    if isinstance(file, (bytes, bytearray)):
        return bytes(file)
    if file.startswith(("http://", "https://")):
        return self._download(file)          # Download if platform cannot reference URLs
    if file.startswith("file://"):
        file = file[len("file://"):]
    with open(file, "rb") as f:              # Local path
        return f.read()

2.1.3 File Name Semantics for File

File method signature: File(file, filename=None) (filename is optional, built-in in base class).

File name derivation order (adapter generates this when filename is not explicitly provided):

  1. Explicit filename parameter (highest priority)
  2. URL's basename (e.g., https://host/a/b/report.pdf → report.pdf, query string stripped)
  3. Local path's basename (e.g., /tmp/data/backup.zip → backup.zip)
  4. Platform default generation (e.g., file_{timestamp}; should retain real extension—extension affects platform-side type recognition and preview behavior)

Image / Voice / Video can also accept filename (passed via message segment data.filename), but only File's file name has cross-platform semantic guarantee.

2.1.4 Declaration of Platform Limitations

Platforms have different constraints on media size, format (MIME), duration (audio/video), etc. Adapters should:

2.1.5 Capability Degradation Hierarchy

When a platform does not support a certain media type, the following degradation hierarchy applies (following the general principle of "capability degradation without error"):

Scenario Degradation Behavior
Voice does not support voice messages Should send as File (or platform-adjacent form); return retcode=10002 if unable to express
Video does not support video messages Same as above
Media type completely unsupported (no file capability) Return retcode=10002, message indicates unsupported data type
Form not supported (e.g., cannot handle base64) Return retcode=10002, can include a message suggesting the module switch to URL/bytes

Prohibited actions: Silent drop (no return), throw exceptions, require the module to write platform-specific handling.

2.2 @User Parameter Specification

Method: At (modifier method)

Parameter: user_id (str)

Requirements:

Example:

# Single @ user
Send.To("group", "g123").At("123456").Text("Hello")

# Multiple @ users (chained call)
send.To("group", "g123").At("123456").At("789012").Text("Hello everyone")

2.3 Reply Message Parameter Specification

Method: Reply (modifier method)

Parameter: message_id (str)

Requirements:

Example:

send.To("group", "g123").Reply("msg_123456").Text("Received")

3. Platform-Specific Method Naming

Do not directly add platform-prefixed methods in the Send class. It is recommended to use generic method names or Raw_{protocol} methods.

Not recommended:

def YunhuForm(self, form_id: str):  # ❌ Not recommended
    pass

def TelegramSticker(self, sticker_id: str):  # ❌ Not recommended
    pass

Recommended:

def Form(self, form_id: str):  # ✅ Generic method name
    pass

def Sticker(self, sticker_id: str):  # ✅ Generic method name
    pass

def Raw_ob12(self, message):  # ✅ Send OneBot12 format
    pass

Extension method requirements:

4. Parameter Naming Convention

Parameter Name Description Type
text Text content str
file Media content (URL/path/bytes, see §2.1.1) str / bytes
filename File name (optional for File, see §2.1.3) str / None
user_id User ID str / int
group_id Group ID str / int
message_id Message ID str
data Data object (e.g., card data) dict

5. Return Value Specification


6. Reverse Conversion Specification (OneBot12 → Platform)

Adapters must not only convert platform-native events into OneBot12 format (forward conversion), but also must provide the ability to convert OneBot12 message segments back into platform-native API calls (reverse conversion). The unified entry point for reverse conversion is the Raw_ob12 method.

6.1 Conversion Model

Forward Conversion (Receiving Direction)                Reverse Conversion (Sending Direction)
─────────────────                ─────────────────
Platform-native event                       OneBot12 message segment list
    │                                  │
    ▼                                  ▼
Converter.convert()               Send.Raw_ob12()
    │                                  │
    ▼                                  ▼
OneBot12 Standard Event                 Platform-native API call
(containing {platform}_raw)             (returns standard response format)

Core Symmetry: Forward conversion retains original data in {platform}_raw, and reverse conversion accepts OneBot12 standard format and restores it into platform calls.

6.2 Raw_ob12 Implementation Specification

Raw_ob12 receives a list of OneBot12 standard message segments and must convert them into platform-native API calls.

Method Signature:

def Raw_ob12(self, message_segments: List[Dict]) -> asyncio.Task:
    """
    Send OneBot12 standard message segments

    :param message_segments: List of OneBot12 message segments
        [
            {"type": "text", "data": {"text": "Hello"}},
            {"type": "image", "data": {"file": "https://..."}},
            {"type": "mention", "data": {"user_id": "123"}},
        ]
    :return: asyncio.Task, await returns standard response format
    """

Implementation Requirements:

  1. Must handle all standard message segment types: At least support text, image, audio, video, file, mention, reply
  2. Must handle platform extension message segments: For message segments of type {platform}_xxx, convert them into corresponding platform-native calls
  3. Must return standard response format: Follow API Response Standard
  4. Unsupported message segments should be skipped and a warning logged, not throw exceptions causing the entire message to fail

6.3 Message Segment Conversion Rules

6.3.1 Standard Message Segment Conversion

Adapters must implement the following standard message segment conversions:

OneBot12 Message Segment Conversion Requirement
text Use data.text directly
image data.file processed per §2.1 media protocol (three must-accept forms + determination order)
audio Same processing logic as image
video Same processing logic as image
file Same processing logic as image; file name processed per §2.1.3 derivation order for data.filename
mention Convert to platform @user mechanism (e.g., Telegram's entities, Yunhu's at_uid)
reply Convert to platform reply reference mechanism
face Convert to platform emoji sending mechanism; skip if not supported
location Convert to platform location sending mechanism; skip if not supported

6.3.2 Platform Extension Message Segment Conversion

For message segments with platform prefixes, adapters should recognize and convert them:

def _convert_ob12_segments(self, segments: List[Dict]) -> Any:
    """Convert OneBot12 message segments to platform-native format"""
    platform_prefix = f"{self._platform_name}_"
    
    for segment in segments:
        seg_type = segment["type"]
        seg_data = segment["data"]
        
        if seg_type.startswith(platform_prefix):
            # Platform extension message segment → Platform-native call
            self._handle_platform_segment(seg_type, seg_data)
        elif seg_type in self._standard_segment_handlers:
            # Standard message segment → Platform equivalent operation
            self._standard_segment_handlers[seg_type](seg_data)
        else:
            # Unknown message segment → Log warning and skip
            logger.warning(f"Unsupported message segment type: {seg_type}")

6.3.3 Handling Composite Message Segments

A message may contain multiple message segments, and adapters need to correctly handle composite messages:

# Module sends a message containing text + image + @user
await send.Raw_ob12([
    {"type": "mention", "data": {"user_id": "123"}},
    {"type": "text", "data": {"text": "Hello"}},
    {"type": "image", "data": {"file": "https://example.com/img.jpg"}}
])

Handling Strategy:

6.4 Relationship Between Raw_ob12 and Standard Methods

Adapter's standard send methods (Text, Image, etc.) are already implemented and default-delegated to Raw_ob12 by the SendDSL base class; adapter subclasses do not need to re-implement them:

class Send(SendDSL):
    def Raw_ob12(self, message_segments: List[Dict]) -> asyncio.Task:
        """Core implementation: OneBot12 message segments → Platform API (must implement)"""
        return asyncio.create_task(self._send_ob12(message_segments))

    # Text/Image/Voice/Video/File inherited from base class, automatically delegate to Raw_ob12
    # Override individual methods if platform-specific logic is needed:
    # def Text(self, text: str) -> asyncio.Task:
    #     return self.Raw_ob12([{"type": "text", "data": {"text": text}}])

Benefits:

6.5 Implementation Example

class YunhuSend(SendDSL):
    """Yunhu platform Send implementation"""
    
    def Raw_ob12(self, message_segments: list) -> asyncio.Task:
        """OneBot12 message segments → Yunhu API call"""
        return asyncio.create_task(self._do_send(message_segments))
    
    async def _do_send(self, segments: list) -> dict:
        """Actual sending logic"""
        # 1. Parse modifier state
        at_users = self._at_users or []
        reply_to = self._reply_to
        at_all = self._at_all
        
        # 2. Convert message segments
        yunhu_elements = []
        for seg in segments:
            seg_type = seg["type"]
            seg_data = seg["data"]
            
            if seg_type == "text":
                yunhu_elements.append({"type": "text", "content": seg_data["text"]})
            elif seg_type == "image":
                yunhu_elements.append({"type": "image", "url": seg_data["file"]})
            elif seg_type == "mention":
                at_users.append(seg_data["user_id"])
            elif seg_type == "reply":
                reply_to = seg_data["message_id"]
            elif seg_type == "yunhu_form":
                # Platform extension message segment
                yunhu_elements.append({"type": "form", "form_id": seg_data["form_id"]})
            else:
                logger.warning(f"Yunhu does not support message segment: {seg_type}")
        
        # 3. Call Yunhu API
        response = await self._call_yunhu_api(yunhu_elements, at_users, reply_to, at_all)
        
        # 4. Return standard response format
        return {
            "status": "ok" if response["code"] == 0 else "failed",
            "retcode": response["code"],
            "data": {"message_id": response.get("msg_id", ""), "time": int(time.time())},
            "message_id": response.get("msg_id", ""),
            "message": "",
            "yunhu_raw": response
        }

7. Method Discovery

Module developers can query the supported send methods of an adapter via API (do not hardcode a list of methods for a specific platform in the module—each adapter's extension methods evolve with versions, and runtime discovery should be used):

from ErisPulse import adapter

# List all send methods
methods = adapter.list_sends("myplatform")
# ["Batch", "Form", "Image", "Recall", "Sticker", "Text", ...]

# View method details
info = adapter.send_info("myplatform", "Form")
# {
#     "name": "Form",
#     "parameters": [{"name": "form_id", "type": "str", ...}],
#     "return_type": "Awaitable[Any]",
#     "docstring": "Send Yunhu form"
# }

9. Adapter Development Notes

For how to correctly override BaseAdapter, Send, Request's __init__, see Adapter Development Guide - __init__ Notes.



10. Adapter Implementation Checklist

Send Methods

Media Sending Protocol

Reverse Conversion


11. Message Builder (MessageBuilder)

MessageBuilder is a message segment builder tool provided by ErisPulse, used in conjunction with Raw_ob12 to simplify the construction of OneBot12 message segments.

11.1 Import

from ErisPulse.Core import MessageBuilder
# or
from ErisPulse.Core.Event import MessageBuilder

11.2 Chainable Construction

# Build a message containing text, image, and @user
segments = (
    MessageBuilder()
    .mention("123456")
    .text("Hello, look at this picture")
    .image("https://example.com/img.jpg")
    .reply("msg_789")
    .build()
)

# Send
await adapter.Send.To("group", "456").Raw_ob12(segments)

11.3 Quick Single Segment Construction

# Quickly build a single message segment (returns list[dict], can be directly passed to Raw_ob12)
await adapter.Send.To("user", "123").Raw_ob12(MessageBuilder.text("Hello"))
await adapter.Send.To("group", "456").Raw_ob12(MessageBuilder.image("https://..."))
await adapter.Send.To("group", "456").Raw_ob12(MessageBuilder.mention("123"))
await adapter.Send.To("group", "456").Raw_ob12(MessageBuilder.reply("msg_id"))
await adapter.Send.To("group", "456").Raw_ob12(MessageBuilder.at_all())

11.4 Use with Event.reply_ob12

from ErisPulse.Core import MessageBuilder

@message()
async def handle(event: Event):
    await event.reply_ob12(
        MessageBuilder()
        .mention(event.get_user_id())
        .text("Received your message")
        .build()
    )

11.5 Supported Message Segment Methods

Method Description data fields
text(text) Text text
image(file) Image file
audio(file) Audio file
video(file) Video file
file(file, filename=None) File file, filename (optional)
mention(user_id, user_name=None) @User user_id, user_name (optional)
at(user_id, user_name=None) @User (alias of mention) Same as mention
reply(message_id) Reply message_id
at_all() @All Members {}
custom(type, data) Custom/Platform Extension Custom

11.6 Utility Methods

builder = MessageBuilder().text("Base content")

# Copy (deep copy)
msg1 = builder.copy().image("img1").build()
msg2 = builder.copy().image("img2").build()

# Clear
builder.clear().text("New content").build()

# Check if empty
if builder:
    print(f"Contains {len(builder)} message segments")