telegram-bots について
公式 Bot API で Telegram ボットを開発する人向けのリファレンススキルです。更新配信、レート制限、決済、運用上の対策を扱い、障害ケースを試せる Python のテスト用フィクスチャも含みます。
主な機能
- 01更新配信と重複排除の指針
- 02Webhook とポーリングの比較
- 03レート制限と再開可能な一斉送信
- 04Stars 決済の処理
- 05実行可能な Python 障害テスト用フィクスチャ
活用シーン
- 01再配信に対応するハンドラーの設計
- 02Webhook とロングポーリングの選択
- 03再開可能な大量配信の構築
- 04Telegram Stars 購入処理の実装
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ ssheleg/ telegram-dev --skill telegram-botsインストール後、Claude Code で/telegram-botsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
fixtures/update_delivery.pyupdate_pump.pyreferences/frameworks.mdhigh-load.mdlimits-and-files.mdpayments-stars.mdupdates-and-delivery.md詳細
Telegram ボット — 公式 Bot API
Telegram はチャットを保持します。後で処理するすべてのデータは、あなたのデータベースに保持します。 深刻なボットの不具合はすべて、アップデートが二重に処理されたか、誰も取得しようとしなかったために配信されなかったことが原因です。
Bot API 自体がボットの障害箇所なのではありません。問題はその境目で起こります。クラッシュ後に再配信されたアップデート、誰も読まなかったデフォルト設定のために届かないメンバーイベント、誤ったシグナルによる支払い確認、ユーザー 31 人目で 429 に達して黙って停止する一斉送信などです。
2026-08-25 に、バージョン 10.3(2026-08-24 リリース)の Bot API に基づいて確認しました。バージョンを引用する前に、core.telegram.org/bots/api-changelog を再確認してください。この API はおよそ毎月更新されます。2026-10-19 に再確認したところ、引き続き 10.3 が最新でした。機械可読用に、Telegram はドキュメントを同じパスに .md を付けた Markdown 形式で提供しています。core.telegram.org/bots/api.md、メソッドごとに 1 ファイル(/bots/api/methods/getupdates.md)、および型のインデックス(/bots/api/types.md)です。これは公式で解析可能ですが、JSON スキーマではなく Markdown です。
必要に応じて参照する詳しい資料:
| 参照先 | 参照するタイミング |
|---|---|
references/updates-and-delivery.md | ポーリングまたは Webhook を設定するとき — 配信の契約、順序、重複排除、および両者間の移行 |
references/payments-stars.md | 決済を受け付けるとき — Stars、請求書、事前チェックアウトの時間枠、返金、サブスクリプション |
references/limits-and-files.md | 大量送信やファイルの転送を行うとき — 公開されているすべての制限と、それぞれへの対処方法 |
references/frameworks.md | ライブラリを選ぶとき、または監査するとき — aiogram、grammY、Telegraf、python-telegram-bot、および各ライブラリが隠蔽するもの |
references/high-load.md | ボットに実トラフィックがあるとき、または Telegram が遅いと全員への応答が止まるとき — チャットごとのキュー、上限、キューの外で処理する決済、シャットダウン、アラーム |
実行可能で、このファイルと一緒に同梱されています:
fixtures/update_delivery.py — 正しいハンドラーが守るべき 9 つの不変条件と、それぞれを破る変異版です。python3 fixtures/update_delivery.py --self-test は、再配信されたアップデートの二重処理、オフセットの境目でクラッシュして失われるアップデート、ワーカーの停止によって ACK 済みなのに失われるアップデート、実行されなかった処理について再配信に「重複」と返すケース、完了マークを付ける前のクラッシュによる二重返信、再配信されたアウトボックス行の二重送信、2 つのアップデートによる二重の決済付与、429 による返信の破棄、および決済の二重付与を検出します。標準ライブラリのみを使用します。
fixtures/update_pump.py — 1 つの遅いハンドラーがすべてのチャットをブロックしないようにする 9 つのルールです(ポーリングはハンドラーの完了を待たない、チャットごとの順序、上限、決済レーン、到着時点からのフラッド計測、キュー投入時の置き換えなし、待機中のコールバックへの応答、重複排除、チャットごとの上限)。--self-test では各ルールを順に削除し、失敗を確認します。
使用する API を最初に決める
| 必要なもの | 使用するもの | 理由 |
|---|---|---|
| ユーザーが追加する、参加中のチャットで動作するボット | Bot API — このスキル | HTTP、トークン、電話番号不要、BAN のリスクなし |
| ボットには見えない履歴の閲覧、または人としての操作 | MTProto — telegram-userbots | ユーザーアカウントが必要で、それに伴うあらゆるコストが発生します |
| Telegram 内のウェブインターフェース | Mini App — telegram-miniapps | ページと、検証が必要な initData |
ボットにはできないこと: プライバシーモードを無効にせず、メンションもない状態でグループ内のメッセージを読むこと、追加されていないチャットを見ること、参加前の履歴を読むこと、ユーザーの代理として操作すること、または 20 MB を超えるファイルをダウンロードすることです。これらのいずれかが必要なら、telegram-userbots を読むべきです。まずその利用を思いとどまらせる説明を読んでください。ユーザーアカウントは、ボットトークンとは違って責任を伴うためです。
更新はイベントであり、少なくとも一度は届きます
# aiogram 3.x — the shape, not the framework
async def webhook(update: Update, db) -> web.Response:
await db.inbox_put(update.update_id, raw_body) # INSERT on a primary key,
return web.Response(status=200) # durable BEFORE the ack
# off the request path: a worker takes a pending row, does the work, marks it
# done — and a crashed attempt leaves the row pending for the next sweep
update_idだけが利用できる冪等性キーです。 再配信されても値は変わらず、更新を識別します。ただし、これは識別子であり、単調増加が永続的に保証されるわけではありません。約1週間アイドル状態が続くと、カウンターが新しいランダムな基準値から再開することがあるため、長い間隔をまたいでupdate_idを比較し、「新しい」かどうかを判断してはいけません。更新内のほかの情報では更新を識別できません。1秒違いで届いた同一内容のメッセージは別々のイベントであり、同じイベントが2回配信された場合は1つのイベントです。- 確認応答の前に受信トレイへ保存します。クレームは受領の記録であり、処理の完了ではありません。 主キーに対する
INSERT(SELECTしてからINSERTする方法は使いません。Webhookでは、デフォルトで40のmax_connectionsまで、1つの更新の配信が同時に処理中になる可能性があります)によって永続化された行を書き込み、その後で初めて200を返せます。処理はリクエスト内ではなく、ワーカーリースのもとで実行します。ワーカーが停止すると行は保留状態のまま残り、定期処理によって再試行されます。リクエスト内でクレームしてから処理する方法は、気づきにくいデータ損失の原因です。クラッシュするとクレームが残り、Telegramの再配信に対して「重複」と判断され、更新は失われる一方で、すべてのフィクスチャは正常なままになります。 - 更新の保持期間は24時間で、それを超えることはありません。ボットが1日停止すると更新は失われますが、そのことを知らせるものはありません。キューが空になったという前提ではなく、自分の状態と照合してください。
- 2026-08-25にこのマシン上のTelegramの稼働中ボット8つを測定したところ、
update_idで重複排除しているものは8つ中0つでした。 ロングポーリングでは、「処理完了」と「オフセット確認」の間に初めてクラッシュするまで、この問題は表面化しません。
allowed_updatesはデフォルトで3種類を除外します
await bot.set_webhook(
url=f"{origin}/tg/{SECRET_PATH}",
secret_token=WEBHOOK_SECRET, # 1-256 chars
allowed_updates=["message", "callback_query", "chat_member",
"pre_checkout_query", "my_chat_member"],
drop_pending_updates=False,
)
デフォルトは空のリストであり、このパラメーターを渡さなかった場合の値です。これは「chat_member、message_reaction、message_reaction_countを除くすべての更新タイプ」を意味します。そのため、参加や退出を追跡するボットには何も届かず、リクエストはok: trueを返し、ログにも問題は記録されません。処理するタイプはすべて明示的に指定し、タイプを追加したらsetWebhookを再実行してください。allowed_updatesはハンドラーの設定時ではなく、購読時に設定されます。
Webhookかポーリングか――両方は使わないでください
Webhookが設定されている間は、getUpdatesは動作しません。これが移行時の危険な点です。本番トークンに対してローカルでgetUpdatesを実行すると、どちらが先に状態を変更したかによって、気づかないうちに本番環境の処理を乗っ取るか、失敗します。1つのトークンにつき、コンシューマーは1つです。
- Webhookはコールバックなので、決済Webhookと同じ防御が必要です。
secret_tokenで設定した値とX-Telegram-Bot-Api-Secret-Tokenを定時間比較で検証し、検証に失敗した場合は詳細を示さずに401を返します。URLは秘密ではありません。秘密にすべきなのはヘッダーです。 - すばやく応答し、処理は後で行います。 Telegramは2xx以外の応答や遅い応答に対して再試行します。再試行は同じ
update_idの2回目の配信です。確認応答を返してから処理してください。前述のクレームがあるため、これを安全に行えます。 drop_pending_updates=Trueは判断であり、後片付けではありません。 すでに支払い済みのものも含め、キューにあるすべての更新が破棄されます。- ロングポーリングは開発環境や低トラフィックのボットに適しており、Webhookは公開HTTPSエンドポイントがあり、レイテンシーを重視する場合に適しています。どちらについても
references/updates-and-delivery.mdを参照してください。
制限はエラー経路ではなく、設計上の制約です
| 制限 | 値 | 設計上の意味 |
|---|---|---|
| 1つのチャット | 約1メッセージ/秒 | バーストは許容されますが、その後は拒否されます |
| 1つのグループ | 20メッセージ/分 | メッセージの多いグループボットにはキューが必要です |
| 一括送信、全ユーザー | 約30メッセージ/秒 | 10万人へのブロードキャストには数時間かかり、ループでは済みません |
| 有料ブロードキャスト | 最大1000/秒 | 無料枠の30/秒を超えると、1メッセージあたり0.1 Starsです |
429の場合、レスポンスには秒単位のparameters.retry_afterが含まれます。指定された時間だけ正確に待って、同じ呼び出しを再試行してください。固定のバックオフでは待機時間を無駄にするか、次の制限に引っかかります。ブロードキャストはレートリミッターと再開位置を備えたジョブであり、forループではありません。再開できない場合、ユーザー60,000人目でクラッシュすると最初から再開し、それ以前のユーザー全員に2回メッセージが送られます。
お金:支払いが成功した時点で、支払いが成立します
デジタル商品やサービスの販売には、Telegram Starsのみを使用し、通貨コードはXTRです。これらの販売ではprovider_tokenは空のままにします。
sendInvoice / createInvoiceLink → pre_checkout_query → successful_payment
answer within 10s deliver here
answerPreCheckoutQueryの制限時間は10秒です。 間に合わなければ取引はキャンセルされるため、このハンドラーでは自分の状態に照らした検証だけを行い、時間のかかる処理は何も実行しません。1秒でもかかる可能性のある処理は、successful_paymentの後に行います。- 提供するのは
successful_paymentの時点であり、プリチェックアウトの時点ではありません。 プリチェックアウトは問い合わせであり、支払いはまだ発生していません。 refundStarPaymentがあり、返金は削除ではありません。 カードの返金と同じ方法で、付与したものを取り消し、請求IDをキーに処理します。計算と順序の問題は、stripe-billingが扱うものと同じです。
フロー全体、サブスクリプション、取引台帳については、
references/payments-stars.mdを参照してください。
ファイルの上限は、思っているより低いものです
getFileでのダウンロード:20 MB。 これを超えるとBot APIに拒否され、どのフレームワークでも回避できません。- クラウドBot API経由のアップロード:50 MB。
- ローカルBot APIサーバーを使うと両方の上限がなくなり、コードが受け取るファイルパスも変わります。これはフラグの設定ではなく、インフラに関する決定です —
references/limits-and-files.md。 file_idはURLではなく、ボット間で安定して使えるものでもありません。 自分のボットでのみ有効です。手軽に再送するために保存し、アーカイブ用の参照としては使わないでください。
データベースに保存する情報
| 情報 | 保存場所 | 理由 |
|---|---|---|
既に受信したupdate_id | 自分のデータベース、一意 | 唯一の冪等性キーです |
chat_id、user_id | 自分のデータベース | Telegramはユーザー一覧を提供しません |
| 送信したメッセージ | message_idとともに自分のデータベース | 後で編集するには両方のIDが必要です |
| Starsで購入した利用権 | 請求IDをキーとして自分のデータベース | 返金時に該当する利用権を特定する必要があります |
file_id | キャッシュとして自分のデータベース | 手軽に再送するためのもので、アーカイブ用ではありません |
Telegramはデータベースではなく、ボットを開始したユーザーを一覧表示する方法もありません。そのときに記録しなかった情報は、失われます。
Telegramを利用できなくなる可能性があるユーザー層には、別の連絡経路を用意してください。 ボットは自分で所有するものではない連絡経路です。ユーザーがTelegramへのアクセスを制限されている地域に住んでいる場合や、今後制限される可能性がある場合は、Telegramで連絡が届かなくなる日に備えてください。ユーザーの識別にはchat_idだけでなく独自のIDを使い、ユーザーにとって役立つタイミングで別の連絡先(メールアドレス、ウェブアカウントなど)を尋ね、Telegram以外からもサービスにアクセスできるようにしてください。そうすれば、ブロックされても失うのは連絡経路だけで、ユーザー一覧は残ります。
リリース前の確認事項
- すべてのハンドラーが
update_idに対して冪等です(§ 更新はイベントです)。 - Webhook でシークレットヘッダーを検証し、詳細を含めずに 401 を返します (§ Webhook とポーリング)。
allowed_updatesには処理するすべての種類を指定します — デフォルトは「すべて」ではありません (§allowed_updatesでは3種類が対象外になります)。- 429 の場合は
retry_afterの時間だけ待機し、ブロードキャストを再開できます(§ 制限)。 - 商品は pre-checkout ではなく、
successful_paymentで配信します (§ 支払い)。 - ポーリングではハンドラーの処理完了を待ちません:チャットごとにキューを設け、チャット間には上限を設け、支払いはその対象外にします(
references/high-load.md)。
トリガー
ここに案内するその他のフレーズ:「grammy」、「telegraf」、「python-telegram-bot」、「getUpdates」、 「pre_checkout_query」、「高負荷のボット」、「ボットが遅い」、「ボット API」、「Telegram ボット」、「Telegram ボットの」、「Telegram ボットを」。
0.5.4 では、説明文にあった「Telegram の webhook」、「Telegram Stars」をこちらに移動しました。