Laravelを中心とした新しいガイドでは、Webhookのセキュリティ確保、Redisを使用した冪等性(べきとうせい)の実現、そして重い処理のキューへの移行により、Telegramボットのタイムアウト、アクションの重複、または高負荷時のクラッシュを防ぐ方法を開発者に示しています。
TelegramのWebhookに単純なルート以上のものが必要な理由
Telegramは、ユーザーの各インタラクションを、開発者が指定したURLへのHTTP POSTとしてボットに送信します。プラットフォームは数秒以内に 200 OK が返されることを期待しており、それ以上かかるとリクエストが再試行されます。リトライが発生すると、同じ update_id が複数回届く可能性があり、ボットがリクエスト内でデータベースへの書き込みや外部APIの呼び出しを行っている場合、データの重複、メッセージの二重送信、またはレースコンディション(競合状態)を引き起こす可能性があります。1秒間に数十から数百のメッセージを処理するボットにとって、このレイテンシはすぐに大きな障害となります。
1. シークレットヘッダーでエンドポイントを保護する
Telegramは、すべてのWebhookコールに X-Telegram-Bot-Api-Secret-Token ヘッダーを追加します。このヘッダーをサーバーに保存されているシークレットと比較します(タイミング攻撃を避けるためにPHPの hash_equals 関数を使用してください)。Telegram以外からのリクエストはすべて拒否します。Laravelでは、このチェックをミドルウェアに配置することで、コントローラーがペイロードに触れる前に検証を実行できます。
2. Redisを使用して各アップデートを冪等にする
送信されるすべてのペイロードには、一意の update_id が含まれています。このガイドでは、そのIDを NX (set-if-not-exists) フラグを使用してRedisに書き込むことを推奨しています。この操作は最初の発生時のみ成功します。リトライが発生した場合は、キーが既に存在することを確認できるため、Webhookはすぐに 200 OK を返し、Telegramに対してアップデートが処理されたことを通知できます。Redisはメモリ上で動作するため、このチェックによるレイテンシは事実上ゼロであり、キーは重複を検出するために保持されます。
3. 重い処理をバックグラウンドジョブに委ねる
高速なRedisチェックを行ったとしても、ボットのビジネスロジック(データベースへの書き込み、サードパーティAPIの呼び出し、メッセージの構成など)をWebhookリクエスト内で実行すべきではありません。Webhookの検証が完了し、update_id が保存されたら、すぐにLaravelのキュー付きジョブ(queued job)をディスパッチしてください。HTTPレスポンスを即座に返し、ワーカーが自身のペースでジョブを処理できるようにします。これにより、ボットはTelegramのレスポンス期限内に収まり、プラットフォームによるリトライを防ぐことができます。
本番環境向けの調整
- レート制限を遵守する。 ボットが1秒あたりの制限を超えると、Telegramは
Retry-Afterヘッダーとともに 429 Too Many Requests を返します。キューワーカーはそのヘッダーを読み取り、失敗したジョブを再試行する前に一時停止する必要があります。 - 返信する前に永続化する。 ユーザーデータはまずデータベースに書き込みます。コミットが成功した後にのみ、ボットは確認メッセージを送信するようにしてください。この順序を守ることで、ユーザーにメッセージは届いたものの、対応するレコードが作成されないといった事態を防げます。
- コールバックペイロードを軽量化する。 Telegramは
callback_dataを 64バイト に制限しています。大きなデータ(blob)はデータベースに保存し、ボタンのペイロードには制限内に収まるよう参照IDのみを渡すようにしてください。
結論: リクエストの認証、Redisによる重複排除、そしてキューへの処理のオフロードを行うことで、Laravel開発者は、即座に回答し、レート制限を守り、ユーザー需要の増加に合わせてクリーンにスケールできるTelegramボットを構築できるようになります。
