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

Discord Platform Feature Documentation

DiscordAdapter is an adapter built on the Discord Gateway (WebSocket) and REST API v10 protocol, integrating the core functionalities of Discord Bots and providing unified event handling and message operation interfaces.


Documentation Information

Basic Information

Configuration

DiscordAdapter supports multi-account configuration, with each account corresponding to an independent Discord Bot.

# config.toml

# Account 1
[DiscordAdapter.accounts.default]
token = "YOUR_BOT_TOKEN"       # Discord Bot Token (required)
intents = 33281                 # Gateway Intents (optional, default: 33281)
enabled = true                  # Whether to enable (optional, default: true)

# Account 2
[DiscordAdapter.accounts.bot2]
token = "ANOTHER_BOT_TOKEN"
intents = 33281
enabled = true

Configuration Item Description (per account):

Gateway Intents

Intents use a bitmask, calculated as the bitwise OR (|) of each Intent value:

Intent Bit Value Description Privileged
GUILDS 1 << 0 1 Server creation/deletion/update, channel, role changes No
GUILD_MEMBERS 1 << 1 2 Member join/leave/update Yes
GUILD_MESSAGES 1 << 9 512 Server message sending/receiving No
MESSAGE_CONTENT 1 << 15 32768 Message content (empty if this Intent is not present) Yes

Default value 33281 = GUILDS(1) | GUILD_MESSAGES(512) | MESSAGE_CONTENT(32768).

Note: Privileged Intents must be enabled in Discord Developer Portal → Bot → Privileged Gateway Intents. If the Bot is in more than 100 servers, Discord approval is also required.

API Environment:

v5 Paradigm Update (4.2.0)

This adapter has completed alignment with the v5 paradigm (incremental upgrade, API compatible):

Standard API Actions

from ErisPulse import sdk
discord = sdk.adapter.get("discord")

result = await discord.Api.get_self_info()                # GET /users/@me
result = await discord.Api.get_user_info(user_id)         # GET /users/{id}
result = await discord.Api.get_guild_info(guild_id)       # GET /guilds/{id}
result = await discord.Api.get_guild_list()               # GET /users/@me/guilds
result = await discord.Api.get_channel_list(guild_id)     # GET /guilds/{id}/channels
result = await discord.Api.get_guild_member_info(gid, uid)
await discord.Api.delete_message(message_id)              # channel_id auto-completed in the registration table
await discord.Api.leave_guild(guild_id)
result = await discord.Api.Using("main").get_self_info()

Buttons (keyboard / components)

rows = [[{"label": "Click", "type": "callback", "data": "btn:1"},
         {"label": "Website", "type": "link", "data": "https://example.com"}]]
await discord.Send.To("channel", channel_id).Keyboard(rows).Text("Please Select")
# Automatically converts components: callback → custom_id / link → url

Supported Message Sending Types

All sending methods are implemented through a fluent interface, for example:

from ErisPulse.Core import adapter
discord = adapter.get("discord")

await discord.Send.To("group", channel_id).Text("Hello World!")

The supported sending types include:

Fluent Modifier Methods (Can Be Combined)

Fluent modifier methods return self and support fluent chaining, must be called before the final sending method:

Fluent Chaining Examples

# Basic sending
await discord.Send.To("group", channel_id).Text("Hello")

# Reply to message
await discord.Send.To("group", channel_id).Reply(msg_id).Text("Reply message")

# Convenient reply (one-step)
await discord.Send.To("group", channel_id).Reply("Reply content", msg_id)

# Mention user
await discord.Send.To("group", channel_id).At("user_id").Text("Hello")

# Mention multiple users
await discord.Send.To("group", channel_id).At("user1").At("user2").Text("Multiple @")

# Mention everyone
await discord.Send.To("group", channel_id).AtAll().Text("Announcement")

# Combine methods
await discord.Send.To("group", channel_id).Reply(msg_id).At("user_id").Text("Composite message")

# Embed message
embed = {
    "title": "Notice",
    "description": "This is an embedded message",
    "color": 5814783,
    "fields": [{"name": "Field", "value": "Value", "inline": True}],
}
await discord.Send.To("group", channel_id).Embed(embed)

# Send image
await discord.Send.To("group", channel_id).Image("https://example.com/image.png")

Private Message Sending

When sending private messages, the adapter will automatically create a DM channel:

# Send private message
await discord.Send.To("user", user_id).Text("Private message content")
await discord.Send.To("user", user_id).Embed(embed)

Message Operations

# Recall message
await discord.Send.To("group", channel_id).Recall(msg_id)

# OneBot12 format
ob12_msg = [
    {"type": "text", "data": {"text": "Hello "}},
    {"type": "mention", "data": {"user_id": "user_id"}},
]
await discord.Send.To("group", channel_id).Raw_ob12(ob12_msg)

Send Method Return Values

All send methods return a Task object, which can be awaited directly to obtain the send result. The returned result follows the ErisPulse adapter's standardized return specification:

{
    "status": "ok",           // Execution status: "ok" or "failed"
    "retcode": 0,             // Return code (0 indicates success)
    "data": {...},            // Original Discord API response
    "message_id": "xxx",      // Message ID (when sending a message)
    "message": "",            // Error message
    "discord_raw": {...}      // Raw response data
}

Error Code Description

retcode Description
0 Success
33001 Network error (connection failed, timeout, etc.)
34000 Discord API returned error (insufficient permissions, invalid parameters, etc.)

Unique Event Types

The platform-specific features require platform == "discord" detection before use.

Core Differences

  1. Server/Channel System: Discord uses a two-tier structure of servers (Guild) and channels (Channel), with channels being the basic targets for message sending.
  2. Gateway Events: All events are received via the WebSocket Gateway using the Opcode + Dispatch mechanism.
  3. Intents Subscription: Event types are subscribed via bitmask, and MESSAGE_CONTENT requires Privileged permissions.
  4. Message Segment Types: Supports text, images, files, videos, audio, Embed, Sticker, and other message segments.
  5. Mention Format: Discord uses the <@user_id> format for user mentions.

Extended Fields

All unique fields are prefixed with discord_:

detail_type Mapping

Discord Scenario detail_type Description
Channel Message channel ErisPulse extension type
Direct Message (DM) private OneBot12 standard type

Event Type Mapping

Discord Event OneBot12 type detail_type Description
MESSAGE_CREATE message channel/private Message creation
MESSAGE_UPDATE message channel/private Message edit
MESSAGE_DELETE notice group_message_delete / private_message_delete Message deletion
GUILD_MEMBER_ADD notice group_member_increase Member join
GUILD_MEMBER_REMOVE notice group_member_decrease Member leave
GUILD_MEMBER_UPDATE notice group_member_update Member information update
GUILD_ROLE_CREATE notice group_role_create Role creation
GUILD_ROLE_DELETE notice group_role_delete Role deletion
CHANNEL_CREATE notice channel_create Channel creation
CHANNEL_DELETE notice channel_delete Channel deletion
INTERACTION_CREATE request interaction Interaction (button, command, etc.)

Special Field Examples

# Channel text message
{
  "type": "message",
  "detail_type": "channel",
  "user_id": "sender ID",
  "user_nickname": "username",
  "group_id": "channel ID",
  "message_id": "message ID",
  "discord_raw": {...},
  "discord_raw_type": "MESSAGE_CREATE",
  "discord_guild_id": "server ID",
  "discord_channel_id": "channel ID",
  "message": [
    {"type": "text", "data": {"text": "Hello"}}
  ],
  "alt_message": "Hello"
}

# Direct message
{
  "type": "message",
  "detail_type": "private",
  "user_id": "sender ID",
  "user_nickname": "username",
  "message_id": "message ID",
  "discord_raw": {...},
  "discord_raw_type": "MESSAGE_CREATE",
  "discord_channel_id": "DM channel ID",
  "message": [
    {"type": "text", "data": {"text": "private message content"}}
  ],
  "alt_message": "private message content"
}

# Message with Embed
{
  "type": "message",
  "detail_type": "channel",
  "message": [
    {"type": "discord_embed", "data": {"embed": {...}}}
  ],
  "alt_message": "[embedded message]"
}

# Message with attachment
{
  "type": "message",
  "detail_type": "channel",
  "message": [
    {"type": "text", "data": {"text": "Look at this image"}},
    {"type": "image", "data": {"file": "image URL", "url": "image URL", "file_name": "image.png"}}
  ],
  "alt_message": "Look at this image[image]"
}

Message Segment Types

Discord message content is automatically converted into corresponding message segments based on the content, attachments, and embeds fields:

Source Conversion Type Description
content text text Plain text content
content <@id> mention User mention
content <@&id> discord_role_mention Role mention
content <#id> discord_channel_mention Channel mention
attachments (image/*) image Image attachment
attachments (video/*) video Video attachment
attachments (audio/*) audio Audio attachment
attachments (other) file File attachment
embeds discord_embed Embedded message
sticker_items discord_sticker Sticker

discord_embed Message Segment

{
  "type": "discord_embed",
  "data": {
    "embed": {
      "title": "Title",
      "description": "Description",
      "color": 12345,
      "fields": [...],
      "image": {"url": "..."},
      "thumbnail": {"url": "..."},
      "footer": {"text": "..."}
    }
  }
}

Gateway Connection

Connection Flow

  1. Call GET /gateway/bot to get the WebSocket gateway URL
  2. Connect to wss://gateway.discord.gg/?v=10&encoding=json
  3. Receive opcode 10 HELLO: contains heartbeat_interval
  4. Send opcode 2 IDENTIFY: includes token, intents, and properties
  5. Start heartbeat loop: send opcode 1 Heartbeat at intervals of heartbeat_interval
  6. Receive opcode 0 Dispatch: event dispatch (t=event name, s=sequence number, d=data)
  7. Receive opcode 11 Heartbeat ACK: heartbeat acknowledgment

Opcode Reference

Opcode Name Direction Description
0 Dispatch Receive Event dispatch (includes t, s, d fields)
1 Heartbeat Send/Receive Heartbeat (includes last seq)
2 Identify Send Identity authentication
6 Resume Send Resume session
7 Reconnect Receive Server requests reconnection
9 Invalid Session Receive Invalid session
10 Hello Receive Connection handshake (includes heartbeat_interval)
11 Heartbeat ACK Receive Heartbeat acknowledgment

Reconnection and RESUME

Heartbeat Mechanism

Usage Examples

Handling Channel Messages

from ErisPulse.Core.Event import message
from ErisPulse import sdk

discord = sdk.adapter.get("discord")

@message.on_message()
async def handle_group_msg(event):
    if event.get("platform") != "discord":
        return

    text = event.get_text()
    channel_id = event.get("group_id")

    if text == "hello":
        await discord.Send.To("group", channel_id).Text("Hello!")

Handling Private Messages

@message.on_message()
async def handle_private_msg(event):
    if event.get("platform") != "discord":
        return
    if not event.is_dm():
        return

    text = event.get_text()
    user_id = event.get("user_id")

    await discord.Send.To("user", user_id).Text(f"You said: {text}")

Sending Embed Messages

embed = {
    "title": "Server Announcement",
    "description": "Welcome to use ErisPulse Discord adapter",
    "color": 3447003,
    "fields": [
        {"name": "Version", "value": "4.0.0", "inline": True},
        {"name": "Framework", "value": "ErisPulse", "inline": True},
    ],
    "footer": {"text": "Powered by ErisPulse"},
    "timestamp": "2025-01-01T00:00:00.000Z",
}
await discord.Send.To("group", channel_id).Embed(embed)

Using Discord-Specific Methods

@message.on_message()
async def handle(event):
    if event.get("platform") != "discord":
        return

    channel_id = event.get_channel_id()
    guild_id = event.get_guild_id()
    is_dm = event.is_dm()
    embeds = event.get_embeds()
    attachments = event.get_attachments()

    if embeds:
        await discord.Send.To("group", channel_id).Text(
            f"Received {len(embeds)} embeds"
        )

Handling Interaction Events

from ErisPulse.Core.Event import request

@request.on_request()
async def handle_interaction(event):
    if event.get("platform") != "discord":
        return

    interaction = event.get_interaction_data()
    if interaction.get("type") == 3:  # MESSAGE_COMPONENT
        await event.reply("Button clicked!")