localhost で Telegram ボット Webhook をテストするには、パブリック HTTPS トンネルでローカル サーバーを公開し、その URL で setWebhook を呼び出し、リクエストごとに Telegram のシークレット トークン ヘッダーを検証します。 これにより、実際のメッセージが得られます。コードを変更するたびにデプロイすることなく、コールバッククエリとメンバーシップの更新を行うことができます。完全なループは次のとおりです。ボット ハンドラーを実行し、npx portpreview 3000 を開始し、結果の URL を登録し、ボットにメッセージを送信し、リクエストをローカルで検査します。
Telegram がアップデートをローカルホストに直接送信できない理由
Telegram のボット API は、Telegram インフラストラクチャからインターネットで到達可能な URL に Webhook 更新を送信します。 localhost、127.0.0.1、およびプライベート LAN アドレスは、そのインフラストラクチャからルーティングできません。 localhost トンネル は、パブリック アドレスで HTTPS を終了し、変更されていない HTTP リクエストをローカル ポートに転送します。
Telegram ボットは、getUpdates を介したロングポーリングまたは Webhook という 2 つの相互に排他的な方法で更新を受信できます。 公式 setWebhook リファレンス には、発信 Webhook が設定されている間は getUpdates が利用できないと記載されています。ポーリングプロセスがまだ実行中の場合は、Webhook フローを判断する前にポーリングプロセスを停止してください。
ローカル Webhook エンドポイントを構築する
この Express の例では、ハンドラーを意図的に小さくしています。更新に触れる前に共有秘密をチェックし、すぐに確認して、作業を応答パスの外に移動します。
import express from 'express';
import crypto from 'node:crypto';
const app = express();
app.use(express.json({ limit: '1mb' }));
function sameSecret(received = '', expected = '') {
const a = Buffer.from(received);
const b = Buffer.from(expected);
return a.length === b.length && crypto.timingSafeEqual(a, b);
}
app.post('/webhooks/telegram', (req, res) => {
const received = req.get('x-telegram-bot-api-secret-token') || '';
if (!sameSecret(received, process.env.TELEGRAM_WEBHOOK_SECRET)) {
return res.sendStatus(401);
}
const update = req.body;
res.sendStatus(200);
queueMicrotask(() => handleUpdate(update));
});
app.listen(3000, () => console.log('Listening on http://localhost:3000'));
Telegram は JSON シリアル化された Update を送信します。 HMAC ベースのプロバイダーとは異なり、Telegram の secret_token 機能は本文に署名しません。選択した値が X-Telegram-Bot-Api-Secret-Token に設定されます。このトークンは、送信者が Webhook の登録時に使用された値を知っていることを証明しますが、ペイロード ダイジェストは提供しません。 TLS は転送中のリクエストを保護します。
HTTPS でエンドポイントを公開する
- アプリを起動し、GET が 404 を返した場合でも、
curl -i http://localhost:3000/webhooks/telegramがサーバーに到達することを確認します。 - 2 番目の端末を開いて、
npx portpreview 3000. を実行します。
- パブリック HTTPS オリジンをコピーし、
/webhooks/telegram. を追加します
- Telegram が更新を配信している間、トンネル プロセスを実行し続けます。
Bot API は HTTPS Webhook URL を受け入れます。 Telegram には、ポート 443、80、88、および 8443 での Webhook サポートが記載されています。マネージド トンネルのパブリック エンドポイントは、転送されたローカル プロセスが 3000 をリッスンする場合でも、通常 443 を使用します。
Telegram Webhook を安全に登録します
文字、数字、アンダースコア、またはハイフンのみを含むランダムなシークレットを作成します。電報では 1 ~ 256 文字を使用できます。ボット トークンをこの値として再利用しないでください。
export TELEGRAM_WEBHOOK_SECRET="$(openssl rand -hex 32)"
curl -sS -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/setWebhook" \
-d "url=https://YOUR-TUNNEL.portpreview.dev/webhooks/telegram" \
-d "secret_token=${TELEGRAM_WEBHOOK_SECRET}" \
-d 'allowed_updates=["message","callback_query"]' \
-d "drop_pending_updates=true"
allowed_updates はノイズを軽減し、ボットが処理する更新タイプのみをリストする必要があります。 drop_pending_updates=true は、新しいローカル セッションを開始するときに便利ですが、キューに入れられた更新を永久に破棄するため、これらのイベントが重要な場合は省略してください。 Telegram の アップデート ドキュメント では、message、callback_query、my_chat_member などのフィールドについて説明しています。
コードをデバッグする前に登録を確認
getWebhookInfo を使用して、構成エラーをハンドラーエラーから分離します:
curl -sS "https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/getWebhookInfo"
url、pending_update_count、last_error_message、last_error_dateを確認してください。 URL が空の場合は、登録が成功しなかったことを意味します。保留数の増加は通常、Telegram が接続できないか、エンドポイントが 2xx 以外のステータスを返したことを意味します。登録後、ボットにダイレクト メッセージを送信します。チャットを開いただけでは必ずしも更新が作成されるわけではありません。
再試行を行わずに更新を処理します
遅い作業の前に確認してください
リクエストが認証され永続的に受け入れられたらすぐに 2xx レスポンスを返します。データベースのエクスポート、AI 呼び出し、サードパーティ API は非同期で実行する必要があります。 Telegram は、2xx 以外の応答後に失敗した要求を再試行するため、同期作業が遅いと重複が作成される可能性があります。
update_id で重複排除
すべてのアップデートには update_id が含まれます。処理された ID を有効期限付きで保存するか、一意のデータベース キーを強制します。再試行では、2 回目の支払い受領書を送信したり、重複したチケットを作成したり、同じコールバックを 2 回実行したりしてはなりません。
すべての更新タイプを明示的にモデル化する
すべてのアップデートに message.text が含まれるわけではありません。コールバック ボタンは callback_query に到着します。チャンネルの投稿とメンバーシップの変更には他のフィールドがあります。現在の最上位フィールドに分岐し、未知の型をスローではなく有効な no-ops として扱います。
ローカル Telegram ボット テストのセキュリティ ルール
- 最初に秘密ヘッダーを検証します。 機密フィールドのログを記録または解析する前に、欠落値または不正確な値を拒否します。
- URL やログにトークンを含めないでください。 登録コマンドの Bot API トークンは資格情報です。共有システム上のシェル履歴を回避し、BotFather を通じて公開トークンをローテーションします。
- 推測不可能なルートと秘密を使用します。 ルートは多層防御です。シークレットヘッダーは実際のアプリケーションチェックです。
- キャプチャされたデータを制限します。 メッセージには、名前、ユーザー名、電話番号、ファイル、プライベートな会話テキストを含めることができます。完了したら、ログを編集し、ローカル キャプチャを削除します。
- 開発中は認証を無効にしないでください。 パブリック トンネルはパブリックです。ローカルコードは本番環境と同じチェックを実行する必要があります。
アクセス制御とデータ保持の実践については、より広範なローカルホスト トンネル セキュリティ ガイドを参照してください。
Telegram Webhook の一般的な障害のトラブルシューティング
Telegram が証明書または接続エラーを報告しました
ローカル HTTP ターゲットではなく、トンネルの HTTPS URL を使用します。トンネルがアクティブであり、URL が変更されていないことを確認します。代わりに独自の自己署名証明書を提供する場合、Telegram では公開証明書をファイルとしてアップロードする必要があります。マネージド TLS エンドポイントはそのセットアップを回避します。
エンドポイントは 401
を返しますsetWebhook に渡されたシークレットを、プロセスで使用される環境変数と比較します。ヘッダー名では大文字と小文字が区別されませんが、プロキシまたはミドルウェアはカスタム ヘッダーを削除できます。シークレット値を出力せずに受信ヘッダーを検査します。
リクエストが届きません
getWebhookInfo を実行し、登録されたパスがルートと正確に一致することを確認し、トンネルのローカル接続をブロックするファイアウォールがないことを確認します。最近ポーリングを使用した場合は、Webhook URL が設定されていることを確認してください。ボットにメッセージを送信して、実際の更新をトリガーします。
アップデートが繰り返し届く
ログのステータスと応答時間。リクエスト受信後の例外により、意図した 200 が 500 に変わる可能性があります。速やかに 200 を返し、処理を冪等にし、デバッグ中にプロバイダーの再試行を待つのではなく、制御された Webhook リプレイ を使用してください。
テキストだけでなくコールバッククエリとファイルをテストする
有用なボット テスト マトリックスは、message.text 以外の内容もカバーしています。キャプション付きの写真を送信し、連絡先を共有し、メッセージを編集し、インライン キーボード ボタンを押します。コールバック クエリの場合は、すぐに answerCallbackQuery を呼び出し、クライアントが進行状況インジケーターの表示を停止してから、遅い作業を個別に実行します。ファイルの更新には識別子が含まれます。バイトのダウンロードは 2 番目の Bot API 操作であり、Webhook 応答を遅らせることはできません。
サニタイズされたアップデートから作成されたフィクスチャを単体テスト用に保持しますが、サポートされている各タイプの少なくとも 1 つのテスト用に完全なトランスポート パスを保持します。フィクスチャは、ディスパッチャがペイロードを理解していることを証明します。実際のトンネル配信では、登録、TLS、ヘッダー、本文解析、確認動作も証明されます。新しい allowed_updates エントリを追加するときは、setWebhook を再度呼び出して、getWebhookInfo が意図した構成を反映していることを確認します。
ローカルセッション後にWebhookを削除します
curl -sS -X POST \
"https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/deleteWebhook" \
-d "drop_pending_updates=false"
Webhook を削除すると、getUpdates に戻ることができます。次回のセッションでトンネル URL が変更された場合は、再度 setWebhook に電話してください。追加の診断については、一般的なローカル Webhook デバッグ ワークフロー.
