Pabal API
Pabal API(MTProto)
アプリと同じ方式でサーバーに接続するクライアントを作るとき:接続情報、サーバーの公開鍵、認証の流れ、アップデート、サポート範囲。
Pabal アプリは、サーバーと MTProto 2.0 でやり取りします。同じ方式で接続するプログラム — 別のアプリ、人のアカウントで動く自動化、研究用のクライアント — を作るときは、このドキュメントを参照してください。ボットを作るなら、よりシンプルな Bot API で十分です。
Pabal は Telegram の公開プロトコルに従っているため、プロトコルとメソッドの詳しい定義は MTProto と API メソッドのドキュメントが基準です。このドキュメントには、Pabal サーバーに接続するときの違いとサポート範囲を記載しています。
接続情報
| 項目 | 値 |
|---|---|
| アドレス | 122.34.175.215 |
| ポート | 8443(TCP) |
| DC | 1〜5 はすべて同じアドレスです。どの DC に接続してもよく、通常は 2 を使います |
| プロトコル | MTProto 2.0、API レイヤー 216 |
| トランスポート | Abridged、Intermediate、Padded Intermediate、Full — それぞれ難読化(obfuscated2)を含む |
| サーバーの公開鍵 | server-key.pem · fingerprint 8724853375441383205 |
| api_id · api_hash | 検証しません。任意の値を入れてください |
HTTP トランスポート、WebSocket トランスポート、MTProxy はまだありません。クライアント端末の時計が正確である必要があります — MTProto のメッセージ番号は時刻から作られるため、時計が大きくずれていると、サーバーがメッセージを破棄します。
サーバーの公開鍵
MTProto クライアントは、はじめて接続するときに、サーバーの RSA 公開鍵で認証キーの交換を暗号化します。Telegram クライアントには Telegram の公開鍵が組み込まれているため、Pabal に接続するにはこの鍵を代わりに(または追加で)組み込む必要があります。この鍵によって、別のサーバーが Pabal サーバーになりすますことを防ぎます。
-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA4hH74xPQsUwr/pyXPdF4tVicYr6QbfeDrKC7mUOVrPLSL4FtmgGn
w+O4u6lVvOf3Udd1KY6+OL4fUZdMBlLzwsoGLoniiVR09dnvyXHE8LhQSS+i1LmI
oJbhQwXplLnUJf272fLXkD23e7ppKLkjYk+jeYObueCy5HYMSThklVeXEzbZVGZv
47o/mjU2vyFoRpa6wCIE4rpj1UIPtOMpekMI/TocIlGVJ+ch6cAVNxIDro53a1eG
/1oZRLQH4oViEGxeMMjBMY5gk5HPkZvjbqy4h8TjXEz7O5o0BRsSqG/OPtP/dRQV
X4ewPhj2WCU4e6l5X59ZKIcv4sQLOwClqQIDAQAB
-----END RSA PUBLIC KEY-----
curl -s -o server-key.pem https://pabal.me/docs/server-key.pem
この鍵はサーバーの初回起動時に一度だけ作られ、その後は変わりません。サーバーログの Loaded RSA key … (fingerprint …) の値と、上の fingerprint が一致するか確認してください。
Telethon で接続する
Python の Telethon で人のアカウントにログインし、メッセージをやり取りする例です。アカウントは先にアプリで登録しておいてください(下記参照)。
# pabal_client.py — pip install telethon==1.42.0
# 同じフォルダーに server-key.pem(上でダウンロードしたサーバーの公開鍵)
import asyncio
from telethon import TelegramClient, events
from telethon.crypto import rsa
rsa.add_key(open("server-key.pem").read(), old=False) # Pabal サーバーの公開鍵
client = TelegramClient("pabal", api_id=1, api_hash="0" * 32) # pabal.session にログインを保存
client.session.set_dc(2, "122.34.175.215", 8443)
@client.on(events.NewMessage(incoming=True))
async def show(event):
sender = await event.get_sender()
print(f"{sender.first_name}: {event.raw_text}")
async def main():
await client.start(phone=lambda: input("電話番号 (+8190…): ")) # 初回に一度だけコードを入力
me = await client.get_me()
print(f"ログイン: {me.first_name} (id {me.id})")
await client.send_message("BotFather", "/help")
await client.run_until_disconnected()
asyncio.run(main())
- Telethon 1.42 を使ってください。レイヤー 216 を話すバージョンです。より新しいバージョンはより高いレイヤーでレスポンスを読もうとするため、
TypeNotFoundErrorで失敗します。 - 新規アカウントの登録は、Telethon 側で無効にされています(
sign_up())。アプリで登録するか、auth.signUpを直接呼んでください。 - ログインは
pabal.sessionファイルに保存され、次回からはコードを聞かれません。このファイルはアカウントの鍵なので、大切に守ってください。
ログインの流れ
図の説明
- 2 つの段階:上は暗号化に使う認証キーを作る段階(プロトコル)、下はそのキーにアカウントを結び付けるログインの段階(API)です。上の段階は、ライブラリが自動的に処理します。
- ログインは認証キーに紐づきます:一度ログインしたキーで複数のセッションを開いても、すべてログイン状態です。キーを失うと(セッションファイルを削除した場合など)、再度ログインする必要があります。
- コードの届け方は、サーバーの運営者が決めます:SMS・管理者による伝達なら
sentCodeTypeSms、メールならsentCodeTypeSetUpEmailRequiredが返り、クライアントがメールアドレスを尋ねてaccount.sendVerifyEmailCodeを呼びます(中央の行)。 - 赤い点線は新しい番号の経路です:コードは正しいのにアカウントがない場合は
authorizationSignUpRequiredが返り、名前を入れてauth.signUpを呼ぶと登録が完了します。登録は、コードが正しかった場合にのみ可能です。 - ボットは下の段階の代わりに、
auth.importBotAuthorization(ボットトークン)の 1 回でログインします。
| エラー | 発生する場面 |
|---|---|
PHONE_NUMBER_INVALID | 番号が正しくない、またはサーバーが新規登録を停止している状態での新しい番号 |
PHONE_NUMBER_BANNED | 運営者がブロックした番号 |
FLOOD_WAIT_n(420) | コードを頻繁に要求しすぎた。n 秒後に再試行 |
PHONE_CODE_INVALID | コードが正しくない(規定の回数を超えるとロック) |
PHONE_CODE_EXPIRED | コードの期限切れ・ロック、または不明な phone_code_hash — sendCode からやり直す |
EMAIL_INVALID, EMAIL_NOT_ALLOWED | メールアドレスが正しくない / 既存のアカウントで、登録済みのログイン用メールアドレスではない |
AUTH_KEY_UNREGISTERED(401) | ログインしていないキーで、ログインが必要なメソッドを呼んだ |
運営者がテスト番号を有効にしたサーバーでは、+99966XYYYY 形式の番号が、実際のコード配信なしにコード XXXXX(X を 5 回)でログインできます。開発用サーバー専用の機能で、本番サーバーでは無効になっています。
アップデートの受信
- リアルタイム:接続が開いていれば、サーバーは新しいメッセージ・編集・削除を
updateShortMessage、updatesですぐに送ります。リクエストを送ったセッションは、その結果を RPC のレスポンスとして受け取るため、同じものがプッシュでもう一度届くことはありません。 - pts:ユーザーごとに、変化に通し番号(pts)が付きます。受け取った pts に抜けがあれば、クライアントは何かを見逃しています。
- 追いつき:
updates.getStateで現在の pts を覚えておき、再接続したらupdates.getDifference(pts, date, qts)で、その間の新しいメッセージ・削除と、関連するユーザー・グループを受け取ります。クライアントの持つ pts がサーバーより進んでいる場合(サーバーのデータが初期化された場合など)はdifferenceTooLongが返るので、会話一覧を読み込み直してください。 - Telethon のようなライブラリは、この処理をすべて自動で行います。
ID とピア
| 対象 | ID | メモ |
|---|---|---|
| 人 | 100001 から | peerUser。@BotFather は 100000 |
| ボット | 人と同じ番号体系 | user.bot = true。トークンの先頭の数字がボット ID |
| 基本グループ | 1000001 から | peerChat。Bot API では負の数(-chat_id)として見えます |
| メッセージ | メッセージボックスごとに 1 から | 1:1 の会話では、参加者がそれぞれ自分のコピーと自分の番号を持ちます。同じメッセージでも、2 人で番号が異なることがあります |
access_hash は、サーバーから受け取った値をそのまま保存しておいて使ってください(ユーザー名の検索・会話一覧・アップデートと一緒に届きます)。
ファイル
- アップロード:
upload.saveFilePartで分割してアップロードし、inputFileUploaded… で参照します。大きなファイル用のupload.saveBigFilePartはまだないため、事実上 10MB までです。 - 送信:
messages.sendMediaにinputMediaUploadedPhoto(新しい写真)またはinputMediaPhoto(サーバー上の写真)。ほかのメディアはMEDIA_INVALID。 - 受信:
upload.getFileにinputPhotoFileLocation(メッセージの写真)、inputPeerPhotoFileLocation(プロフィール写真)。一度に最大 1MB。 - プロフィール写真:
photos.uploadProfilePhoto、photos.updateProfilePhoto、photos.getUserPhotos、photos.deletePhotos。 - 写真は原本の 1 サイズだけで保存します(サムネイルは別途作りません)。
サポート範囲
サーバーにはレイヤー 216 の 408 メソッド分のハンドラーがあり、すべてリクエストを読み取れますが、実際のクライアントで最後まで確認したものは下のメソッドです。それ以外は形式どおりに応答しますが、内容が空だったり、記録されなかったりすることがあります。
| 分野 | 確認したメソッド |
|---|---|
| 接続 | initConnection, invokeWithLayer, help.getConfig, auth.bindTempAuthKey(PFS の一時キー), auth.exportAuthorization/importAuthorization |
| ログイン | auth.sendCode, auth.signIn, auth.signUp, auth.logOut, auth.importBotAuthorization, account.sendVerifyEmailCode |
| ユーザー・連絡先 | users.getUsers, users.getFullUser, contacts.resolveUsername, contacts.importContacts, contacts.search |
| メッセージ | messages.sendMessage, messages.sendMedia(写真), messages.getHistory, messages.getDialogs, messages.getMessages, messages.editMessage, messages.deleteMessages |
| グループ | messages.createChat, messages.deleteChatUser, messages.editChatTitle(messages.addChatUser は公式アプリでのみ確認) |
| ボット | messages.getBotCallbackAnswer, messages.setBotCallbackAnswer, ボタン(reply_markup)付きのメッセージ |
| アップデート | updates.getState, updates.getDifference, リアルタイムプッシュ |
| ファイル・写真 | upload.saveFilePart, upload.getFile, photos.*(上記) |
公式の Telegram Desktop 6.2.6 が、登録・ログイン・会話・写真・グループ・再接続まで、改変なしで動作することも確認しました。アプリの起動時に呼ばれる約 60 個のメソッドは、レスポンスの形式を個別に検査しています。
エラー
エラーは標準の rpc_error(error_code + error_message)で返ります。error_message は常に大文字・数字・アンダースコア(PEER_ID_INVALID)の形式で、必要に応じて : 説明 が付きます。
| コード | 意味 |
|---|---|
400 | リクエストが正しくない — PEER_ID_INVALID, MESSAGE_ID_INVALID, MEDIA_INVALID, USERNAME_NOT_OCCUPIED … |
401 | ログインが必要 — AUTH_KEY_UNREGISTERED |
403 | 権限がない — メンバーではないグループなど |
420 | FLOOD_WAIT_n — n 秒待つ |
500 | サーバー内部エラー |
トランスポートエラー -404 | サーバーがこの認証キーを知らない — 新しいキーを作って再ログイン(永続キー)、または再バインド(一時キー) |
まだないもの
- チャンネル・スーパーグループ(
channels.*は応答するが、アプリで正しく表示されない)、シークレットチャット、通話 - 写真以外のメディア、
upload.saveBigFilePart、サムネイル - 2 段階認証(SRP)— 設定できず、2 段階認証が必要な操作は拒否されます
- HTTP・WebSocket トランスポート、MTProxy、
msgs_ackの送信、bad_server_saltによるソルトの切り替え - 複数サーバーへの分散 — 1 台のサーバーが DC 1〜5 をすべて担当します
公式 Telegram アプリを Pabal に接続するには
Telegram アプリは、サーバーのアドレスと公開鍵をビルド時に組み込みます。そのため、設定画面ではなく、ソースを修正して再ビルドする必要があります。Pabal アプリ(Pabal.app)は、そのようにして作られたものです。サーバーの運営者なら、サーバーのインストール — アプリの接続を参照してください。