アダプタ標準化変換規格
1. 核心原則
- 严格的互換性:すべての標準フィールドはOneBot12仕様に完全に準拠する必要があります。
- 明確な拡張:プラットフォーム固有の機能には必ず {platform}_ 前置きを付ける必要があります(例:yunhu_form)。
- データの完全性:元のイベントデータは {platform}_raw フィールドに、元のイベントタイプは {platform}_raw_type フィールドに保持する必要があります。
- 時間の統一:すべてのタイムスタンプは10桁のUnixタイムスタンプ(秒単位)に変換する必要があります。
- プラットフォームの統一:platform項目の命名は、ErisPulseで登録した名称/別称と一致する必要があります。
2. 標準フィールド要件
2.1 必須フィールド
| フィールド | 型 | 説明 |
|---|---|---|
| id | string | イベントの一意の識別子 |
| time | integer | Unixタイムスタンプ(秒単位) |
| type | string | イベントの種類 |
| detail_type | string | イベントの詳細な種類(会話タイプ標準を参照) |
| platform | string | プラットフォーム名 |
| self | object | ロボット自身の情報 |
| self.platform | string | プラットフォーム名 |
| self.user_id | string | ロボットのユーザーID |
detail_type の規格:
- ErisPulse 標準会話タイプを使用する必要があります(会話タイプ標準を参照)
- 対応するタイプ:
private,group,user,channel,guild,thread - アダプターは、プラットフォーム固有のタイプを標準タイプにマッピングする責任があります
2.2 メッセージイベントフィールド
| フィールド | 型 | 説明 |
|---|---|---|
| message | array | メッセージセグメントの配列 |
| alt_message | string | メッセージセグメントの代替テキスト |
| user_id | string | ユーザーID |
| user_nickname | string | ユーザーのニックネーム(オプション) |
2.3 通知イベントフィールド
| フィールド | 型 | 説明 |
|---|---|---|
| user_id | string | ユーザーID |
| user_nickname | string | ユーザーのニックネーム(オプション) |
| operator_id | string | 操作者のID(オプション) |
2.4 要求イベントフィールド
| フィールド | 型 | 説明 |
|---|---|---|
| user_id | string | ユーザーID |
| user_nickname | string | ユーザーのニックネーム(オプション) |
| comment | string | 要求の付言(オプション) |
| request_id | string | 要求の識別子(強く推奨、同意/拒否操作に使用) |
request_id フィールドの説明:
request_idは要求イベントの一意の操作識別子であり、HandleRequestDSL を使用して同意/拒否操作を実行するために使用されます- アダプターは、要求イベントを変換する際に、プラットフォーム固有の要求識別子をこのフィールドにマッピングする必要があります
- プラットフォームに要求IDがない場合、アダプターは一意の識別子(例:タイムスタンプ+ユーザーIDのハッシュ)を生成する必要があります
request_idが欠落している場合、event.approve()/event.reject()はValueErrorをスローします
3. イベント形式の例
3.1 メッセージイベント (message)
{
"id": "1234567890",
"time": 1752241223,
"type": "message",
"detail_type": "group",
"platform": "yunhu",
"self": {
"platform": "yunhu",
"user_id": "bot_123"
},
"message": [
{
"type": "text",
"data": {
"text": "抽選 超大賞"
}
}
],
"alt_message": "抽選 超大賞",
"user_id": "user_456",
"user_nickname": "YingXinche",
"group_id": "group_789",
"yunhu_raw": {...},
"yunhu_raw_type": "message.receive.normal",
"yunhu_command": {
"name": "抽選",
"args": "超大賞"
}
}
3.2 通知イベント (notice)
{
"id": "1234567891",
"time": 1752241224,
"type": "notice",
"detail_type": "group_member_increase",
"platform": "yunhu",
"self": {
"platform": "yunhu",
"user_id": "bot_123"
},
"user_id": "user_456",
"user_nickname": "YingXinche",
"group_id": "group_789",
"operator_id": "",
"yunhu_raw": {...},
"yunhu_raw_type": "bot.followed"
}
3.3 要求イベント (request)
{
"id": "1234567892",
"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": "req_abc123",
"onebot11_raw": {...},
"onebot11_raw_type": "request"
}
4. メッセージセグメント標準
4.1 標準メッセージセグメント
標準メッセージセグメントにはプラットフォームプレフィックスは不要です。
| タイプ | 説明 | data フィールド |
|---|---|---|
text |
純粋なテキスト | text: str |
image |
画像 | file, url: str |
audio |
音声 | file, url: str |
video |
動画 | file, url: str |
file |
ファイル | file, url: str, filename: str |
mention |
ユーザーへのメンション | user_id: str, user_name: str |
reply |
メッセージへの返信 | message_id: str |
face |
エモート | id: str |
location |
位置情報 | latitude: float, longitude: float |
keyboard |
ボタン/インラインキーボード | rows: list[list[button]](4.1.1を参照) |
メディアセグメント file のフィールド形式(送信方向、image / audio / video / file に共通):
| 形態 | 例 | アダプタの要件 |
|---|---|---|
| HTTP(S) URL | https://example.com/a.png |
必須で受け入れること |
| ローカルファイルパス | /tmp/a.png、C:\tmp\a.png |
必須で受け入れること |
| 2進数データ | bytes |
必須で受け入れること |
file:// URI / Base64 / Data URI |
file:///tmp/a.png、data:image/png;base64,... |
推奨で受け入れること |
完全なメディア送信プロトコル(形態の判定順序、ファイル名の推定、機能の降格段階)は 送信メソッド仕様 §2.1 を参照してください。
フィールドの方向性の意味:
file:送信方向のコンテンツソース(上記の形態);受信方向はアダプタがプラットフォームで再取得可能な形態を埋める (通常はダウンロード可能な URL、またはget_file類のアクションで利用可能なリソース識別子)url:受信方向のプラットフォームの戻りリンク(アダプタがプラットフォームイベントを変換する際に可能な限り埋める、モジュールが直接利用できるようにする);送信方向は未記入でも可filename:fileセグメントのファイル名(送信方向はオプション、省略時はアダプタが 送信メソッド仕様 §2.1.3 の推定順序に従って生成する;受信方向は必ずプラットフォームの元のファイル名を埋める)
{
"type": "text",
"data": {
"text": "Hello World"
}
}
4.1.1 keyboard ボタン/インラインキーボードセグメント(クロスプラットフォーム共通)
ボタンやインラインキーボードは、Telegram / 云湖 / QQBot / Kook / Discord などの複数のプラットフォームで対応しており、
クロスプラットフォーム共通の概念であるため、プラットフォームプレフィックスのない標準メッセージセグメントとして扱います。アダプタは標準セグメントを
プラットフォームのネイティブ構造に変換する必要があります。プラットフォーム固有の拡張セグメント(例:telegram_inline_keyboard)は引き続き透過的に保持します。
{
"type": "keyboard",
"data": {
"rows": [
[
{"label": "オプションA", "type": "callback", "data": "vote:A"},
{"label": "公式サイト", "type": "link", "data": "https://example.com"}
]
]
}
}
フィールドの説明:
| フィールド | タイプ | 必須 | 説明 |
|---|---|---|---|
rows |
2次元配列 | 是 | 各サブ配列が1行のボタンを表す |
rows[][].label |
str | 是 | ボタンに表示するテキスト |
rows[][].type |
str | 是 | callback(クリック時にデータを返す)/ link(URLに移動する) |
rows[][].data |
str | 是 | コールバックデータ(type=callback)または移動先アドレス(type=link) |
rows[][].* |
Any | 否 | プラットフォーム固有のオプションフィールド(例:web_app、menus)、アダプタは機能に応じてマッピングするか無視する |
アダプタの変換例(完全なマッピングとインタラクションコールバックイベントの標準は クロスプラットフォームインタラクションコンポーネント標準 を参照):
| プラットフォーム | 標準セグメント → プラットフォームネイティブ |
|---|---|
| Telegram | inline_keyboard:[{text, callback_data | url}] |
| 云湖 | buttons:[{label, action_type: 2=コールバック | 1=移動, ...}] |
| QQBot | keyboard.content.rows:[{label, type: 2=コールバック | 0=移動, data}](markdown形式のメッセージが必要) |
| Kook | カードの action-group モジュール |
| Discord | components:action_row + buttons(custom_id/url) |
4.2 プラットフォーム拡張メッセージセグメント
プラットフォーム固有のメッセージセグメントには、プラットフォームプレフィックスを付ける必要があります:
// 云湖 - フォーム
{"type": "yunhu_form", "data": {"form_id": "123456", "form_name": "応募フォーム"}}
// Telegram - ステッカー
{"type": "telegram_sticker", "data": {"file_id": "CAACAgIAAxkBAA...", "emoji": "😂"}}
拡張メッセージセグメントの要件:
- data 内部のフィールドにプレフィックスを付けない:
{"type": "yunhu_form", "data": {"form_id": "..."}}ではなく{"type": "yunhu_form", "data": {"yunhu_form_id": "..."}} - 降格対応を提供する:モジュールが拡張メッセージセグメントを認識しない場合、アダプタは
alt_messageにテキストによる代替を提供する - ドキュメントの完全性:各拡張メッセージセグメントは、適切なドキュメントで
type、dataの構造と使用シーンを説明する必要がある
5. 未知イベントの処理
イベントの種類が識別できない場合、警告イベントを生成する必要があります:
{
"id": "1234567893",
"time": 1752241223,
"type": "unknown",
"platform": "yunhu",
"yunhu_raw": {...},
"yunhu_raw_type": "unknown",
"warning": "サポートされていないイベントタイプ: special_event",
"alt_message": "このシステムではこのイベントタイプはサポートされていません。"
}
6. 拡張名命名規則
6.1 フィールド名
規則: {platform}_{field_name}
プラットフォーム接頭辞 フィールド名 完全なフィールド名
──────── ─────── ──────────
yunhu command yunhu_command
telegram sticker_file_id telegram_sticker_file_id
onebot11 anonymous onebot11_anonymous
email subject email_subject
要件:
platformは、アダプタ登録時のプラットフォーム名と完全に一致する必要があります(大文字小文字を区別)field_nameはsnake_caseで命名する__で始まるダブルアンダースコアは使用禁止(Python 予約)- 標準フィールド名(
type、time、messageなど)と重複しないこと
6.2 メッセージセグメント型名
規則: {platform}_{segment_type}
標準のメッセージセグメント型(text、image、audio、video、mention、reply など)には、プラットフォーム接頭辞を追加しないでください。プラットフォーム固有のメッセージセグメント型のみ接頭辞を追加する必要があります。
6.3 元データフィールド名
以下のフィールド名は予約フィールドであり、すべてのアダプタは以下の要件を遵守する必要があります:
| 予約フィールド | 型 | 説明 |
|---|---|---|
{platform}_raw |
any |
プラットフォームの元のイベントデータの完全なコピー |
{platform}_raw_type |
string |
プラットフォームの元のイベントタイプの識別子 |
要件:
{platform}_rawは、参照ではなく元データのディープコピーである必要があります{platform}_raw_typeは文字列型である必要があります。プラットフォームが数値型を使用している場合でも、文字列に変換する必要があります- これらの2つのフィールドは、すべてのイベントで必ず存在する必要があります(取得できない場合は
nullと空文字列""とする)
6.4 プラットフォーム固有フィールドの例
{
"yunhu_command": {
"name": "抽奖",
"args": "超级大奖"
},
"yunhu_form": {
"form_id": "123456"
},
"telegram_sticker": {
"file_id": "CAACAgIAAxkBAA..."
}
}
6.5 嵌套拡張フィールド
拡張フィールドは単純な値でも、ネストされたオブジェクトでも構いません:
{
"telegram_chat": {
"id": 123456,
"type": "supergroup",
"title": "My Group"
},
"telegram_forward_from": {
"user_id": "789",
"user_name": "ForwardUser"
}
}
ネストされたフィールドの要件:
- トップレベルのキーには必ずプラットフォーム接頭辞を付ける
- ネストされた内部フィールドには、プラットフォーム接頭辞を追加しない
- ネストの深さは3層を超えないようにすることを推奨
6.6 self フィールドの拡張
self オブジェクトの標準必須フィールド(platform、user_id)は §2.1 を参照してください。以下は ErisPulse が拡張したオプションフィールドです:
| フィールド | 型 | 説明 |
|---|---|---|
self.user_name |
string |
ロボットのニックネーム |
self.avatar |
string |
ロボットのアバター URL |
self.account_id |
string |
マルチアカウントモードにおけるアカウント識別子 |
Bot 状態の追跡: アダプタは
type: "meta"イベントを送信して、フレームワークに Bot の接続状態を通知します。サポートされるdetail_type:connect(オンライン)、heartbeat(ハートビート)、disconnect(オフライン)。システムは、このdetail_typeからselfフィールドの Bot 元情報を取り出して自動的に状態を追跡します。また、通常のイベントにおけるselfフィールドも自動的に Bot を検出します。詳細は アダプタシステム API - Bot 状態管理 を参照してください。
7. セッションタイプ拡張
ErisPulse は OneBot12 標準の private、group の上に以下のセッションタイプを拡張しています。
| タイプ | OneBot12 標準 | ErisPulse 拡張 | 説明 |
|---|---|---|---|
private |
✅ | — | 1対1のプライベートチャット |
group |
✅ | — | グループチャット |
user |
— | ✅ | ユーザータイプ(Telegram など) |
channel |
— | ✅ | チャンネル(放送型) |
guild |
— | ✅ | サーバー/コミュニティ |
thread |
— | ✅ | トピック/サブチャンネル |
アダプタ独自のタイプ拡張:
from ErisPulse.Core.Event.session_type import register_custom_type
# アダプタ起動時に登録
register_custom_type(
receive_type="email", # 受信イベント中の detail_type
send_type="email", # 送信時の対象タイプ
id_field="email_id", # 対応するIDフィールド名
platform="email" # プラットフォーム識別子
)
独自タイプの要件:
- アダプタの
start()時に登録し、shutdown()時に登録解除する必要があります receive_typeは標準タイプと重複しないようにする必要がありますid_fieldは{対象}_idの命名規則に従う必要があります
完全なセッションタイプ定義とマッピング関係は、セッションタイプ標準を参照してください。
8. モジュール開発者ガイド
8.1 拡張フィールドのアクセス
from ErisPulse.Core.Event import message
@message()
async def handle_message(event):
# 標準フィールドのアクセス
text = event.get_text()
user_id = event.get_user_id()
# プラットフォーム拡張フィールドのアクセス - 方法1: 直接 get
yunhu_command = event.get("yunhu_command")
# プラットフォーム拡張フィールドのアクセス - 方法2: 点式アクセス (Event ラッパークラス)
# event.yunhu_command
# 送信元データのアクセス
raw_data = event.get("yunhu_raw")
raw_type = event.get_raw_type()
# プラットフォームの判定
platform = event.get_platform()
if platform == "yunhu":
pass
elif platform == "telegram":
pass
8.2 拡張メッセージセグメントの処理
@message()
async def handle_message(event):
message_segments = event.get("message", [])
for segment in message_segments:
seg_type = segment.get("type")
seg_data = segment.get("data", {})
if seg_type == "text":
text = seg_data["text"]
elif seg_type.startswith("yunhu_"):
if seg_type == "yunhu_form":
form_id = seg_data["form_id"]
elif seg_type.startswith("telegram_"):
if seg_type == "telegram_sticker":
file_id = seg_data["file_id"]
8.3 最適な実践方法
- 標準フィールドの優先使用: 拡張フィールドが必ず存在すると仮定しないこと
- プラットフォームの判定: 拡張フィールドの存在によってプラットフォームを推測するのではなく、
event.get_platform()を使用すること - エラーハンドリング: 拡張メッセージセグメントを処理できない場合は、
alt_messageをバックアップとして使用すること - プレフィックスのハードコーディングを避ける:
platform変数を使って動的に文字列を連結すること
# ✅ 推奨
platform = event.get_platform()
raw_data = event.get(f"{platform}_raw")
# ❌ 推奨されない
raw_data = event.get("yunhu_raw")
8.4 要求イベントの処理
モジュール開発者は、event.approve() および event.reject() を使用して要求イベントを処理することができます。
from ErisPulse.Core.Event import request
# フレンドリクエスト: 自動的に承認
@request.on_friend_request()
async def handle_friend_request(event):
user_name = event.get_user_nickname() or event.get_user_id()
comment = event.get_comment()
# 承認リクエスト
result = await event.approve()
if result.get("status") == "ok":
print(f"{user_name} からのフレンドリクエストを承認しました")
else:
print(f"フレンドリクエストの承認に失敗しました: {result.get('message')}")
# グループ招待: 条件に応じて決定
@request.on_group_request()
async def handle_group_request(event):
comment = event.get_comment()
# 拒否リクエスト
result = await event.reject(comment="暫定的に新しいグループに参加しません")
アダプターを介した直接操作(イベントハンドラ以外の場面に適用可能):
from ErisPulse import adapter
# request_id を使用して直接操作
await adapter.myplatform.Request("req_abc123").accept()
await adapter.myplatform.Request("req_abc123").reject()
# 特定の Bot アカウントで操作
await adapter.myplatform.Request("req_abc123").Using("bot1").accept()
# 備考を付けて操作
await adapter.myplatform.Request("req_abc123").accept(comment="ようこそ")
9. notice / request イベントのセッションタイプ推論
9.1 問題の背景
notice イベントと request イベントの detail_type は意味論的なサブタイプ(例: group_member_increase、friend_increase)であり、セッションタイプ(例: group、private)ではありません。
type detail_type 含意 セッションタイプ
──── ─────────── ──── ────────
message group 群チャットメッセージ group(detail_type がセッションタイプ)
message private プライベートチャットメッセージ private(detail_type がセッションタイプ)
notice group_member_increase 群メンバーの増加 group(group_id から推論)
notice friend_increase 友達の増加 private(user_id から推論)
request friend 友達リクエスト private(user_id から推論)
request group 群リクエスト group(detail_type がセッションタイプ)
9.2 推論ルール
infer_receive_type() の推論順序は以下の通りです:
detail_typeが既知のセッションタイプ(private/group/channel/guild/thread/user)である場合、そのまま使用するdetail_typeがカスタムセッションタイプである場合、そのまま使用する- それ以外(notice/request の意味論的サブタイプ)の場合、ID フィールドに基づいて推論する:
group_idがある →"group"channel_idがある →"channel"guild_idがある →"guild"thread_idがある →"thread"user_idがある →"private"
9.3 event.reply() の送信先推論
notice/request イベントにおける event.reply() の送信先は、セッションタイプの推論によって決まります:
- グループ通知イベント(
group_idを含む)→ グループに返信 - 友達通知イベント(
user_idだけを含む)→ ユーザーのプライベートチャットに返信
from ErisPulse.Core.Event import notice
@notice.on_group_increase()
async def handle_welcome(event):
group_id = event.get("group_id") # "group_789"
user_id = event.get("user_id") # "user_456"
# event.reply() はグループ(group/group_789)に送信
await event.reply("ようこそ!")
# 管理者に通知する場合(プライベートチャット)、明示的に送信先を指定する:
await adapter.Send.To("user", "admin_id").Text(f"新メンバー {user_id} が {group_id} に参加しました")
9.4 アダプタ開発の推奨事項
notice/request イベントにおいて、正しい ID フィールドが含まれていることを確認してください:
| detail_type | 必須の ID フィールド | 推論されるセッションタイプ |
|---|---|---|
group_member_increase |
group_id + user_id |
group |
group_member_decrease |
group_id + user_id |
group |
friend_increase |
user_id |
private |
friend_decrease |
user_id |
private |
friend(リクエスト) |
user_id |
private |
group(リクエスト) |
group_id |
group |
10. 関連ドキュメント
- 各プラットフォームの機能ドキュメント - ここでは、各プラットフォームの機能や既知の拡張イベントやメッセージセグメントなどについて説明しています。
- 会話タイプの標準 - 会話タイプの定義とマッピング関係
- 送信メソッドの規格 - Send クラスのメソッド命名、パラメータ規格および逆変換の要件
- API 応答の標準 - アダプターの API 応答形式の標準
- API アクションの標準 - OneBot12 標準 API アクションの統一インターフェース