開発者ドキュメント
日本語

Pabal API

Pabal API(MTProto)

アプリと同じ方式でサーバーに接続するクライアントを作るとき:接続情報、サーバーの公開鍵、認証の流れ、アップデート、サポート範囲。

Pabal アプリは、サーバーと MTProto 2.0 でやり取りします。同じ方式で接続するプログラム — 別のアプリ、人のアカウントで動く自動化、研究用のクライアント — を作るときは、このドキュメントを参照してください。ボットを作るなら、よりシンプルな Bot API で十分です。

Telegram のドキュメントもあわせて参照してください

Pabal は Telegram の公開プロトコルに従っているため、プロトコルとメソッドの詳しい定義は MTProto と API メソッドのドキュメントが基準です。このドキュメントには、Pabal サーバーに接続するときの違いとサポート範囲を記載しています。

接続情報

項目値
アドレス122.34.175.215
ポート8443(TCP)
DC1〜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 ファイルに保存され、次回からはコードを聞かれません。このファイルはアカウントの鍵なので、大切に守ってください。

ログインの流れ

1 · 認証キー (1 回) req_pq_multi → req_DH_params set_client_DH_params 認証キー 2048 ビットサーバー公開鍵で保護 2 · ログイン (認証キーごとに 1 回) auth.sendCode電話番号 sentCodeTypeSmsSMS または管理者が伝達 SetUpEmailRequiredメール方式 → アドレスを入力 account.sendVerifyEmailCodepurpose: loginSetup auth.signInphone_code または email コード auth.authorizationログイン完了 authorizationSignUpRequired新しい番号 → auth.signUp(名前) コードが届いたら
認証キーを作り、コードでログインする

図の説明

  • 2 つの段階:上は暗号化に使う認証キーを作る段階(プロトコル)、下はそのキーにアカウントを結び付けるログインの段階(API)です。上の段階は、ライブラリが自動的に処理します。
  • ログインは認証キーに紐づきます:一度ログインしたキーで複数のセッションを開いても、すべてログイン状態です。キーを失うと(セッションファイルを削除した場合など)、再度ログインする必要があります。
  • コードの届け方は、サーバーの運営者が決めます:SMS・管理者による伝達なら sentCodeTypeSms、メールなら sentCodeTypeSetUpEmailRequired が返り、クライアントがメールアドレスを尋ねて account.sendVerifyEmailCode を呼びます(中央の行)。
  • 赤い点線は新しい番号の経路です:コードは正しいのにアカウントがない場合は authorizationSignUpRequired が返り、名前を入れて auth.signUp を呼ぶと登録が完了します。登録は、コードが正しかった場合にのみ可能です。
  • ボットは下の段階の代わりに、auth.importBotAuthorization(ボットトークン)の 1 回でログインします。
エラー発生する場面
PHONE_NUMBER_INVALID番号が正しくない、またはサーバーが新規登録を停止している状態での新しい番号
PHONE_NUMBER_BANNED運営者がブロックした番号、または停止されたアカウントの番号(停止されたまま退会した場合は既定で 1 年 — 運営者は延長や無期限にできます)
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 が返るので、会話一覧を読み込み直してください。
  • プッシュ通知 — Android:接続していない Android アプリは、Firebase のプッシュで起こされます。アプリが account.registerDevice(token_type 2 = FCM トークン、secret = 256 バイトのデバイス鍵)で登録すると、サーバーは新しいメッセージや着信をそのデバイス鍵で暗号化して FCM で送り、起きたアプリは updates.getDifference で追いつきます。プッシュが届くのは、サーバー運営者の Firebase プロジェクトでビルドされたアプリだけです。
  • プッシュ通知 — iPhone:iPhone アプリは同じリクエストで 2 つを登録します — token_type 1(APNs の通知トークン)と 9(PushKit の VoIP トークン)で、secret はどちらもそのアカウントの 256 バイトのデバイス鍵です。新しいメッセージは APNs の通知で送られます。Apple に見える表向きの文言はアプリ名と「You have a new notification」のような中身のない言葉だけで、メッセージはデバイス鍵で暗号化して p に載せて送り、アプリの通知拡張(Notification Service Extension)がそれを復号して表示します。着信は VoIP プッシュで CallKit の着信画面を表示し(運営者が VoIP を有効にしたビルドから。それまでは通知で届きます)、別の端末で既読にしたものや削除したものはバックグラウンドプッシュで知らせます。プッシュが届くのは、サーバー運営者の APNs キーと同じチームが署名し、バンドル ID も同じアプリだけです。それ以外の token_type には boolTrue と応答するだけで、送信はしません。
  • Telethon のようなライブラリは、この処理をすべて自動で行います。

ID とピア

対象IDメモ
人100001 からpeerUser。@BotFather は 100000
ボット人と同じ番号体系user.bot = true。トークンの先頭の数字がボット ID
基本グループ1000001 からpeerChat。Bot API では負の数(-chat_id)として見えます
メッセージサーバー全体で 1 つの連番1 対 1 の会話では、2 つのコピーが同じ番号を持ちます。そのため、ピアを指定せずメッセージ番号だけを受け取るリクエスト(messages.getMessages、messages.deleteMessages)も、2 人にとって同じメッセージを指します。チャンネルだけは、チャンネルごとに 1 から数えます

access_hash は、サーバーから受け取った値をそのまま保存しておいて使ってください(ユーザー名の検索・会話一覧・アップデートと一緒に届きます)。

ファイル

  • アップロード:upload.saveFilePart でファイルをパート(1 つ 512KB まで、4,000 個まで)に分けてアップロードし、inputFileUploaded… で参照します。10MB を超えるファイルは upload.saveBigFilePart でアップロードします — パートの総数を先に伝えるため、サーバーが受け付けないサイズなら、すべて送り終える前に FILE_TOO_BIG で拒否します。パートは届いたそばからディスクに書き込まれ、メッセージがそのファイルを指したときに 1 つにまとめられます。
  • 送信:messages.sendMedia に inputMediaUploadedPhoto(新しい写真)、inputMediaPhoto(サーバー上の写真)、inputMediaUploadedDocument(新しいファイル)、inputMediaDocument(サーバー上のファイル)。それ以外のメディアは MEDIA_INVALID。messages.uploadMedia で送らずに先にアップロードしておくこともでき(返ってきた media を後からそのまま送ります)、messages.sendMultiMedia は受け付けますが、まとまらずに 1 件ずつ送られます(grouped_id なし)。
  • 受信:upload.getFile に inputPhotoFileLocation(メッセージの写真)、inputDocumentFileLocation(メッセージのファイル)、inputPeerPhotoFileLocation(プロフィール写真)。一度に最大 1MB。受け取る側も、ファイル全体をメモリに読み込まず、要求した範囲だけを読みます。
  • プロフィール写真:photos.uploadProfilePhoto、photos.updateProfilePhoto、photos.getUserPhotos、photos.deletePhotos。
  • 写真は原本の 1 サイズだけで保存します(サムネイルは別途作りません)。ファイルには手を加えずそのまま置き、送信側が付けた documentAttribute* はバイトのまま受信側に渡されます — 動画の長さや音声の波形をサーバーが理解する必要はなく、そのまま伝えるだけです。

サポート範囲

サーバーにはレイヤー 216 の 469 メソッド分のハンドラーがあり、すべてリクエストを読み取れますが、実際のクライアントで最後まで確認したものは下のメソッドです。それ以外は形式どおりに応答しますが、内容が空だったり、記録されなかったりすることがあります。

分野確認したメソッド
接続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.saveBigFilePart(10MB 超)、upload.getFile、photos.*(上記)

公式の Telegram Desktop 6.2.6 が、登録・ログイン・会話・写真・グループ・再接続まで、改変なしで動作することも確認しました。アプリの起動時に呼ばれる約 60 個のメソッドは、レスポンスの形式を個別に検査しています。

その後に加えたもの — 端末の一覧と終了(account.getAuthorizations、account.resetAuthorization、auth.resetAuthorizations)、デスクトップアプリのデータのエクスポート(account.initTakeoutSession、invokeWithTakeout、invokeWithMessagesRange、account.finishTakeoutSession — アカウントごとに 1 日 3 回、超えると 420 TAKEOUT_INIT_DELAY_n)、番号の変更(account.sendChangePhoneCode、account.changePhone — 運営者が有効にするまでは 400 PHONE_CHANGE_DISABLED、ボットには BOT_METHOD_INVALID) — はサーバーのテストで確認しました。実際のアプリで最後まで確かめるのはこれからです。

エラー

エラーは標準の 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権限がない — メンバーではないグループなど
406今はできない — ログインして 24 時間たっていない端末が、ほかの端末を終了させようとしたとき FRESH_RESET_AUTHORISATION_FORBIDDEN
420FLOOD_WAIT_n — n 秒待つ
500サーバー内部エラー
トランスポートエラー -404サーバーがこの認証キーを知らない — 新しいキーを作って再ログイン(永続キー)、または再バインド(一時キー)

ウォッチ API

サーバーの /api/watch/v1/ は、Pabal の Apple Watch アプリ専用の HTTPS JSON の入口です。ウォッチは MTProto を使えないため別に用意したもので、公開仕様ではなく準備中のため、予告なく変わります。Pabal に接続するプログラムを作るなら、このページの MTProto を使ってください。サーバーの運営者はサーバーのインストール — Apple Watch の入口をご覧ください。

まだないもの

  • チャンネル・スーパーグループ(channels.* は応答するが、アプリで正しく表示されない)、シークレットチャット、グループ通話
  • アプリ内で再生される動画・音声メッセージ、ひとまとめに表示されるアルバム(grouped_id)、ステッカー、サムネイル(ファイルは原本のみ)
  • 2 段階認証(SRP)— 設定できず、2 段階認証が必要な操作は拒否されます
  • HTTP・WebSocket トランスポート、MTProxy、msgs_ack の送信、サーバーソルトの定期的な切り替え(誤ったソルトには bad_server_salt で正しい値を返します)
  • 複数サーバーへの分散 — 1 台のサーバーが DC 1〜5 をすべて担当します

公式 Telegram アプリを Pabal に接続するには

Telegram アプリは、サーバーのアドレスと公開鍵をビルド時に組み込みます。そのため、設定画面ではなく、ソースを修正して再ビルドする必要があります。Pabal アプリ(Pabal.app)は、そのようにして作られたものです。サーバーの運営者なら、サーバーのインストール — アプリの接続を参照してください。