Webエンジニア向けプログラミング解説動画をYouTubeで配信中!
▶ チャンネル登録はこちら

【ITニュース解説】Implement Telegram Bot Long Polling in PHP for Local Development

2026年09月18日に「Dev.to」が公開したITニュース「Implement Telegram Bot Long Polling in PHP for Local Development」について初心者にもわかりやすく解説しています。

作成日: 更新日:

ITニュース概要

ローカルでのTelegram Bot開発には、Webhookの代わりにロングポーリングが便利だ。PHPでgetUpdatesメソッドを使い、ローカルスクリプトがTelegramから直接更新情報を取得する。重複処理を防ぐには、offset管理やDBで処理済みIDを記録する工夫が必要。本番環境ではWebhooksが推奨される。

ITニュース解説

Telegramボットを開発する際、ボットがユーザーからのメッセージを受け取る方法は主に二つある。一つは「ウェブフック」という方法で、ユーザーがメッセージを送ると、Telegramのサーバーが開発者が指定したURLに直接そのメッセージを送信する仕組みだ。もう一つは「ロングポーリング」という方法で、開発者のプログラムが定期的にTelegramのサーバーに「新しいメッセージはありますか?」と問い合わせに行き、メッセージがあれば受け取る仕組みである。

ローカル環境、つまり自分のPCでボットを開発する場合、ウェブフックを使うには自分のPCのプログラムをインターネット上に公開する必要がある。通常、これは自宅のインターネット回線やルーターの設定をいじるか、NgrokやCloudflare Tunnelsのような特別なツールを使って、一時的にインターネットからアクセスできる状態にする必要があるため、初心者には少し複雑な作業だ。 そこで、この手間を省き、より簡単にローカルで開発を進めるために「ロングポーリング」が役立つ。ロングポーリングは、自分のPCで動くプログラムが直接Telegramのサーバーに問い合わせに行くため、外部にPCのポートを公開する必要がない。しかし、ただ単純にメッセージを問い合わせ続けるだけだと、PCのCPUを無駄に使ってしまったり、メッセージの取りこぼしがあったり、同じメッセージを何度も処理してしまったりといった問題が発生する可能性がある。この記事では、これらの問題を解決し、堅牢に動作するロングポーリングの実装方法を解説する。

ロングポーリングを正しく実装するには、Telegram APIのgetUpdatesというメソッドが持つ三つの重要な設定項目(パラメータ)を理解する必要がある。 一つ目はoffsetだ。これは「どのメッセージから受け取るか」を指定する識別子である。Telegramはメッセージ一つ一つにupdate_idというユニークな番号を付けている。プログラムがメッセージを受け取り、処理を完了したら、Telegramに「このメッセージまでは受け取りましたよ」と伝える必要がある。そのためには、最後に処理したメッセージのupdate_idに1を足した値を次のoffsetとしてgetUpdatesメソッドに渡す。これにより、Telegramはすでに受け取ったメッセージを再度送ってくることを防ぐことができる。 二つ目はtimeoutだ。これは「新しいメッセージが来るまで、Telegramがどれくらいの時間接続を維持して待ってくれるか」を秒数で指定する。例えばこれを30秒に設定すると、新しいメッセージがすぐになくても、Telegramのサーバーは30秒間接続を切らずに待機してくれる。この間、プログラムは頻繁に「メッセージはありますか?」と問い合わせる必要がなくなるため、無駄な通信を減らし、効率的に動作させることができる。 三つ目はlimitだ。これは「一度に取得するメッセージの最大数」を指定する。1から100までの範囲で設定でき、一度に大量のメッセージが来た場合でも、この数を超えてまとめて取得できる。

実際にロングポーリングを実装するPHPスクリプトの例を見てみよう。このスクリプトはbot.phpという名前で、コマンドライン上で実行され続けるプログラムとして設計されている。まず、Telegramボットのトークン(ボットを識別するための秘密の鍵)を環境変数から取得する。これにより、コードの中に直接トークンを書き込まずに済み、セキュリティが高まる。 スクリプトは無限ループに入り、TelegramのgetUpdates APIを継続的に呼び出し続ける。この通信にはPHPのcurlという機能を使う。curlはインターネット上の他のサーバーと通信するための標準的なツールだ。offsettimeoutlimitの各パラメータを正しく設定してAPIリクエストを構築する。特に重要なのは、curl自体のタイムアウト設定を、Telegram APIのtimeoutよりも少し長くすることだ。これにより、Telegramがメッセージを待っている間にcurlが先にタイムアウトしてしまうことを防ぐ。 通信中には様々なエラーが発生する可能性がある。例えば、ネットワークが一時的に切断されたり、Telegramサーバーからエラー応答が返ってきたりするかもしれない。スクリプトはこれらのエラー(curlエラー、HTTPエラー、JSON解析エラー、Telegram APIからのエラーなど)を検出し、エラーが発生した場合は少し待ってから再試行するように実装されている。 Telegramから新しいメッセージ(更新)が届くと、スクリプトはそれを一つずつ処理する。処理が完了したら、そのメッセージのupdate_idに1を足した値を新しいoffsetとして記録し、次のAPI呼び出しに備える。これにより、同じメッセージを重複して受け取ることがなくなる。

しかし、このoffsetの仕組みだけでは、まだ重複処理の問題が残る可能性がある。もし、スクリプトが一括で10個のメッセージを受け取り、そのうち5個を処理したところで、何らかの原因でスクリプトがクラッシュしてしまったとしよう。次の起動時、スクリプトはクラッシュ前のoffsetの値を使ってgetUpdatesを呼び出すため、Telegramは同じ10個のメッセージを再び送ってくることになる。これでは、すでに処理したはずの5個のメッセージも再度処理されてしまう可能性がある。 この「重複処理」を防ぐためには、「冪等性」(べきとうせい)という考え方が重要になる。これは「同じ操作を何度繰り返しても、結果が常に同じになる」という意味だ。ボット開発においては、あるメッセージを複数回受け取っても、そのメッセージに対する処理は一度だけ行われるようにすることを目指す。 具体的な解決策として、処理済みのupdate_idを永続的な場所に記録する方法がある。例えば、SQLiteのような軽量なデータベースファイルを使うことができる。スクリプトがメッセージを受け取ったら、まずそのupdate_idがデータベースに記録されているかを確認する。もし記録されていれば、それは以前に処理済みなのでスキップする。記録されていなければ、データベースにそのupdate_idを記録してから、実際の処理ロジックを実行する。これにより、スクリプトが途中で停止しても、再開後に同じメッセージが送られてきた際に、以前に処理したメッセージは正しくスキップされる。

ロングポーリングはローカル開発には非常に便利だが、実際のサービスとして本番環境で運用する際には、いくつかの点で課題がある。 まず「並行処理」の問題だ。ロングポーリングは通常、単一のプログラムが順番にメッセージを処理するため、同時にたくさんのユーザーからメッセージが来ると、処理が追いつかなくなる可能性がある。一方、ウェブフックはWebサーバー(NginxやApacheなど)と連携し、PHP-FPMのような仕組みを使うことで、何千ものメッセージを同時に受け取って並行して処理できる。 次に「リソース消費」だ。ロングポーリングのスクリプトは常に実行され続け、ネットワーク接続を維持するため、システムリソースを消費する。また、スクリプトが停止しないように監視する仕組み(Supervisorなど)も別途必要になる。ウェブフックの場合、メッセージが来たときだけプログラムが起動し、処理が終わればすぐに終了するため、リソースをより効率的に使える。 最後に「レイテンシ」、つまりメッセージが届くまでの時間だ。ウェブフックはメッセージが送られた瞬間にTelegramから直接プログラムに通知されるため、ほぼリアルタイムでメッセージを受け取れる。しかし、ロングポーリングでは、timeoutで設定した時間が経過するか、新しいメッセージが来るまでプログラムは待機するため、わずかながら遅延が発生する可能性がある。

これらの理由から、本番環境でTelegramボットを運用する際には、通常ウェブフック方式が推奨される。ウェブフックを設定するには、setWebhookというAPIメソッドを使って、Telegramにボットがメッセージを受け取るためのURLを登録する。さらに、送られてくるリクエストが本当にTelegramから来たものかを検証するために、secret_tokenという秘密の文字列をHTTPヘッダーに含めることでセキュリティを強化することが重要となる。

関連コンテンツ

関連IT用語