PythonおよびJavaScriptを使用したTelegram Botの実装とAWSへのデプロイ

そして、新しいテレグラムボットをAWSに展開することにしました。

目次

以下は、Telegramボットを実装およびAWSにデプロイするためのステップバイステップのチュートリアルのノートです。Telegramボットを実装およびAWSにデプロイ

私は、ロングポーリングを使用したクイックスタートと、プロダクション用のウェブフックを使用したパスを追加し、PythonおよびNode.jsでの例を提供しています。

IT業界で働くロボット

Telegramボットは、あなたのサーバー上で動作し、ユーザーとTelegramを通じてやり取りするアプリケーションです。私たちは、ボットを登録し、小さなプログラムを書くことで、TelegramのBot APIに接続します。

TL;DR

  1. @BotFather経由でボットを作成し、トークンを取得します。
  2. python-telegram-botまたはTelegrafを使用して小さなアプリを構築します。
  3. ローカルではロングポーリングから始め、プロダクションではウェブフックに切り替えます。

必要なもの

  • Telegramアカウント(モバイルまたはデスクトップ)

  • 基本的なターミナル + コードエディタ

  • 以下のいずれか:

    • **Python 3.10+**およびpip
    • **Node.js 18+**およびnpm/pnpm/yarn

BotFatherを使用してボットを作成する(トークンを取得)

  1. Telegramで**@BotFather**を検索し、チャットを開始します。
  2. /newbotを送信し、ボットの名前を指定し、一意のユーザー名(botで終わる必要があります)を選択します。例:example_helper_bot
  3. BotFatherは123456:ABC-DEF...のようなAPIトークンを返します。これはパスワードのように扱い、Gitにコミットしないでください。漏洩した場合は、BotFather経由でいつでも再生成できます。

ヒント:Telegramの公式チュートリアルでは、これらの正確な手順とgetMeを使用してトークンを検証する方法が説明されています。


スタックを選択する

Bot APIの生のHTTPを直接呼び出すこともできますが、メンテナンスされているライブラリを使用する方がはやいです。

  • Python: python-telegram-bot(非同期、現代的)。([https://docs.python-telegram-bot.org])
  • Node.js: telegraf(成熟、TypeScript対応)。([https://telegraf.js.org])

Bot API自体はここに文書化されており、getUpdatessetWebhookなどのメソッドの真の情報源です。


クイックスタート:ローカルでロングポーリングを使用して実行

ロングポーリングはローカル開発に最適です:ボットはgetUpdatesを使用してTelegramに新しいアップデートを繰り返し尋ねます。(プロダクションでは、おそらくウェブフックに切り替えるでしょう。)

Telegramは2つの排他的な方法でアップデートを受信することができ、getUpdates(ポーリング)またはウェブフックです。アップデートは最大24時間保持されます。

オプションA: Python(python-telegram-botを使用)

インストールと構成

python -m venv .venv && source .venv/bin/activate  # Windows: .venv\Scripts\activate
pip install python-telegram-bot

コード: bot.py

import os
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes, filters

TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]  # export TELEGRAM_BOT_TOKEN=123:ABC

async def start(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("👋 こんにちは!何か送ってください、私はそれをエコーします。")

async def echo(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(update.message.text)

def main():
    app = ApplicationBuilder().token(TOKEN).build()
    app.add_handler(CommandHandler("start", start))
    app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
    app.run_polling()  # ロングポーリング

if __name__ == "__main__":
    main()

実行方法:

export TELEGRAM_BOT_TOKEN=123456:ABC-DEF...
python bot.py

これは現在の安定版ドキュメントからApplicationBuilderを使用しています。

オプションB: Node.js(telegrafを使用)

インストールと構成

npm init -y
npm i telegraf

コード: index.js

const { Telegraf } = require('telegraf');

const bot = new Telegraf(process.env.BOT_TOKEN); // BOT_TOKEN環境変数を設定
bot.start((ctx) => ctx.reply('👋 こんにちは!何か送ってください、私はそれをエコーします。'));
bot.on('text', (ctx) => ctx.reply(ctx.message.text));

bot.launch();

// グレースフルな停止
process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));

実行方法:

export BOT_TOKEN=123456:ABC-DEF...
node index.js

これは公式Telegrafの「Getting started」例と一致しています。


役に立つようにする(コマンドとボタン)

/helpコマンドを追加

Python

from telegram.ext import CommandHandler

async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("/start – あいさつ\n/help – このヘルプ")

app.add_handler(CommandHandler("help", help_cmd))

CommandHandler/commandsをバインドする標準的な方法です。

Node (Telegraf)

bot.help((ctx) => ctx.reply('/start – あいさつ\n/help – このヘルプ'));

クイックリプライボタン(カスタムキーボード)

Python

from telegram import ReplyKeyboardMarkup

async def start(update: Update, context):
    keyboard = [["Help", "About"]]
    await update.message.reply_text(
        "オプションを選んでください:",
        reply_markup=ReplyKeyboardMarkup(keyboard, resize_keyboard=True)
    )

Node (Telegraf)

const { Markup } = require('telegraf');
bot.start((ctx) => {
  ctx.reply("オプションを選んでください:", Markup.keyboard([["Help", "About"]]).resize());
});

プロダクション: ウェブフックに切り替える

プロダクションでは、TelegramはあなたのHTTPSエンドポイントにプッシュします。これはsetWebhookを使用して設定でき、オプションで秘密トークンを提供して、X-Telegram-Bot-Api-Secret-Tokenヘッダー経由でリクエストを検証できます。

curlを使用してウェブフックを設定する:

TOKEN=123456:ABC-DEF...
WEBHOOK_URL="https://your-domain.com/telegram/${TOKEN}"  # 秘密のパスを含める
SECRET="my-secret-42"

curl -X POST "https://api.telegram.org/bot${TOKEN}/setWebhook" \
  -d url="${WEBHOOK_URL}" \
  -d secret_token="${SECRET}" \
  -d drop_pending_updates=true

仕様からの要点:

  • setWebhookHTTPS URLを必要とし、secret_tokenをサポートします。
  • ウェブフックが設定されている間、getUpdatesを使用することはできません
  • サポートされるポートには443, 80, 88, 8443が含まれます。

ウェブフックサーバーのオプション

  • Node (Telegraf) 内蔵ウェブフック:
bot.launch({
  webhook: {
    domain: "your-domain.com",
    port: 8080,
    // オプションの秘密ヘッダー
    secretToken: process.env.WEBHOOK_SECRET
  }
});

Telegrafはウェブフックのオプションを一等クラスで提供し、Express/Fastify/Cloudflare Workersなどと統合しています。

  • Python (python-telegram-bot) PTBはASGI/WSGIフレームワーク(FastAPI, Starlette, Flask)とよく動作します。ApplicationBuilder().token(...).build()を使用し、インバウンドJSONアップデートをアプリケーションにフィードするPOSTルートを公開し、bot.set_webhook(...)を呼び出してください。ApplicationBuilderドキュメントを参照して構築パターンを確認してください。([docs.python-telegram-bot.org][2])

ローカルでテスト?ngrokなどのトンネルを使用してTelegramにhttps://...を公開し、setWebhookに公開URLを指定してください。


ボットコマンドの設定(オプションですがユーザーにとって便利)

ボットコマンドのリスト(ユーザーが/をタイプしたときに表示される)を定義できます:

  • BotFather経由で**/mybots → Edit Bot → Edit Commands**を使用して、または
  • ライブラリまたは生のBot APIを使用してsetMyCommandsでプログラム的に設定します。

Telegramの公式「BotFatherからHello Worldへ」ガイドは、さらに詳しいコマンド処理と例にリンクしています。


デプロイの考慮事項

任意のHTTPS対応ホストが動作します:

  • 小さなVM(Ubuntu + systemd)
  • サーバーレス(AWS Lambda/Cloud Functions)とウェブフック統合
  • Fly.io/Render/Herokuのようなプラットフォーム上のコンテナ

TelegrafのドキュメントにはLambda、GCF、Express、Fastifyなどの例が含まれています。


セキュリティと信頼性のチェックリスト

  • トークンを秘密に保つ(環境変数、シークレットマネージャー)。漏洩した場合はBotFatherで無効化してください。
  • secret_tokenを使用してウェブフックを設定し、X-Telegram-Bot-Api-Secret-Tokenヘッダーを検証してください。
  • ポーリングとウェブフックを混ぜない。1つずつ選択してください。
  • エラーとリトライを処理:Telegramは非2xxのウェブフック応答をリトライします。適切にログを出力してください。
  • アップデートタイプに注意(メッセージ、コールバック、インラインクエリ、支払いなど)と、必要に応じてのみサブスクライブしてください(allowed_updates)。

HTTP APIを直接使用する(オプション)

以下のようなエンドポイントを呼び出すことができます:

https://api.telegram.org/bot<token>/getMe
https://api.telegram.org/bot<token>/getUpdates
https://api.telegram.org/bot<token>/sendMessage

仕様に従ってGETまたはPOSTを使用し、JSONまたはフォームデータを使用してください。


次にすることと読むべき内容

  • 公式Bot APIリファレンス: メソッド、オブジェクト、制限、フォーマット、支払い、インラインモード。
  • 公式「BotFatherからHello Worldへ」ガイド: 詳しいウォークスルーと多言語の例。
  • python-telegram-bot ドキュメント(安定版): 現代的な非同期パターン。
  • Telegraf ドキュメント: クイックレシピ、ウェブフックヘルパー、TSタイプ。

PythonバージョンのAWSデプロイステップ

TelegramボットをAWSインフラにデプロイするための2つの主要なオプションがあります:

  • A) サーバーレス(API Gateway + Lambda + Secrets Manager) — 最も安価で簡単、軽量なトラフィックに最適。
  • B) コンテナ化(ECS Fargate + ALB + ACM) — 定常的なトラフィックと長時間実行されるライブラリ(python-telegram-botのウェブフックモード)に最適なプロダクショングレード。

A) AWS上のサーバーレス(API Gateway + Lambda)

ゼロサーバーとほぼゼロアイドルコストを望む場合に使用してください。以下のコードはTelegramウェブフックを直接処理します(長時間実行されるイベントループはありません)。

  1. 最小限のLambdaハンドラ(Python)を準備

handler.pyを作成:

import json, os, urllib.request

BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
SECRET_HEADER = os.environ.get("WEBHOOK_SECRET", "")  # Bot API setWebhookのsecret_tokenと一致する必要があります

API_BASE = f"https://api.telegram.org/bot{BOT_TOKEN}"

def reply(chat_id: int, text: str):
    data = json.dumps({"chat_id": chat_id, "text": text}).encode()
    req = urllib.request.Request(f"{API_BASE}/sendMessage", data=data, headers={"Content-Type": "application/json"})
    with urllib.request.urlopen(req) as resp:
        return resp.read()

def lambda_handler(event, context):
    # 1) Telegramの秘密ヘッダーを検証(ウェブフックを設定するときにセットされます)
    if SECRET_HEADER:
        if event.get("headers", {}).get("X-Telegram-Bot-Api-Secret-Token") != SECRET_HEADER:
            return {"statusCode": 401, "body": "invalid secret"}

    # 2) アップデートを解析
    body = event.get("body") or "{}"
    update = json.loads(body)

    message = update.get("message") or update.get("edited_message")
    if not message:
        # 今は非メッセージアップデート(callback_query, inline_queryなど)を無視
        return {"statusCode": 200, "body": "ok"}

    chat_id = message["chat"]["id"]
    text = (message.get("text") or "").strip()

    # 3) シンプルなルーティング
    if text.startswith("/start"):
        reply(chat_id, "👋 AWS Lambdaからの挨拶!何かテキストを送ってください、私はそれをエコーします。")
    elif text.startswith("/help"):
        reply(chat_id, "/start – あいさつ\n/help – ヘルプ\n(AWS Lambdaでデプロイ)")
    elif text:
        reply(chat_id, text)  # エコー

    return {"statusCode": 200, "body": "ok"}

これはBot APIに直接HTTP呼び出しを行うことでLambdaをスリムかつコールドスタートに優しくしています。ルーティングを後で拡張できます。

  1. Lambdaのパッケージングとデプロイ
# プロジェクト構成
# .
# ├─ handler.py
# └─ requirements.txt   # (この最小限の例では空にします)

zip -r function.zip handler.py
aws lambda create-function \
  --function-name telegram-bot-webhook \
  --runtime python3.11 \
  --role arn:aws:iam::<ACCOUNT_ID>:role/<LambdaExecutionRole> \
  --handler handler.lambda_handler \
  --timeout 10 \
  --memory-size 256 \
  --zip-file fileb://function.zip \
  --environment Variables='{TELEGRAM_BOT_TOKEN=<TOKEN_FROM_BOTFATHER>,WEBHOOK_SECRET=my-secret-42}'

IAMロール<LambdaExecutionRole>)はCloudWatch LogsのためにAWSLambdaBasicExecutionRoleが必要です。

トークンをAWS Secrets Managerに保存し、初期化時に読み込むのが好ましいですが、この例では簡潔にするために環境変数を使用しています。

  1. HTTPSエンドポイントの作成(API Gateway)
# HTTP API(RESTではなく)で低レイテンシーを実現
API_ID=$(aws apigatewayv2 create-api \
  --name telegram-webhook \
  --protocol-type HTTP \
  --target arn:aws:lambda:<REGION>:<ACCOUNT_ID>:function:telegram-bot-webhook \
  --query 'ApiId' --output text)

# デフォルトルートPOST /webhookを追加
aws apigatewayv2 create-integration \
  --api-id $API_ID \
  --integration-type AWS_PROXY \
  --integration-uri arn:aws:lambda:<REGION>:<ACCOUNT_ID>:function:telegram-bot-webhook \
  --payload-format-version 2.0

aws apigatewayv2 create-route \
  --api-id $API_ID \
  --route-key "POST /webhook" \
  --target "integrations/$(
    aws apigatewayv2 get-integrations --api-id $API_ID --query 'Items[0].IntegrationId' --output text
  )"

aws apigatewayv2 create-deployment --api-id $API_ID
GW_URL=$(aws apigatewayv2 get-apis --query "Items[?ApiId=='$API_ID'].ApiEndpoint" --output text)
echo $GW_URL  # 例: https://abc123.execute-api.ap-somewhere.amazonaws.com

Lambdaへのアクセス権限が自動で追加されていることを確認してください。なければ:

aws lambda add-permission \
  --function-name telegram-bot-webhook \
  --statement-id apigw \
  --action lambda:InvokeFunction \
  --principal apigateway.amazonaws.com \
  --source-arn "arn:aws:execute-api:<REGION>:<ACCOUNT_ID>:$API_ID/*/*/webhook"
  1. Telegramにウェブフックを設定
TOKEN=<TOKEN_FROM_BOTFATHER>
WEBHOOK_URL="$GW_URL/webhook"
SECRET="my-secret-42"

curl -X POST "https://api.telegram.org/bot${TOKEN}/setWebhook" \
  -d url="${WEBHOOK_URL}" \
  -d secret_token="${SECRET}" \
  -d drop_pending_updates=true

ボットに/startを送信してください—メッセージはAPI Gateway → Lambdaを介して流れます。

  1. ログ、リトライ、アップデート
  • ログを確認: aws logs tail /aws/lambda/telegram-bot-webhook --follow
  • Telegramはあなたのエンドポイントが2xxでない場合にリトライします。応答を速く保ってください。
  • 新しいコードをロールアウトするには: 再ZIP + aws lambda update-function-code --function-name telegram-bot-webhook --zip-file fileb://function.zip

B) ECS Fargate + ALB 上のコンテナ化された python-telegram-bot

python-telegram-bot(PTB)のエラゴノミクス(PTB)を使用したい場合、適切な非同期アプリと永続接続を持つ場合に使用してください。PTBはHTTPSロードバランサーの後ろで実行されます。

  1. アプリコード(FastAPI + PTBウェブフック)

app.py:

import os, asyncio
from fastapi import FastAPI, Request, Header
from telegram import Update
from telegram.ext import ApplicationBuilder, CommandHandler, MessageHandler, ContextTypes, filters

TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
SECRET = os.environ.get("WEBHOOK_SECRET", "")

app = FastAPI()
tg_app = None  # PTBアプリケーション

async def start_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("🚀 ECS Fargate + PTBからの挨拶!")

async def echo(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text(update.message.text)

@app.on_event("startup")
async def on_startup():
    global tg_app
    tg_app = ApplicationBuilder().token(TOKEN).build()
    tg_app.add_handler(CommandHandler("start", start_cmd))
    tg_app.add_handler(MessageHandler(filters.TEXT & ~filters.COMMAND, echo))
    # run_polling()は実行しない。ウェブフックを手動でフィードする。

@app.post("/telegram/webhook")
async def telegram_webhook(request: Request, x_telegram_bot_api_secret_token: str | None = Header(default=None)):
    if SECRET and (x_telegram_bot_api_secret_token != SECRET):
        return {"ok": True}  # 静かに無視

    data = await request.json()
    update = Update.de_json(data, tg_app.bot)
    await tg_app.process_update(update)
    return {"ok": True}

uvicornエントリポイント: uvicorn app:app --host 0.0.0.0 --port 8080

  1. Dockerfile
FROM python:3.11-slim
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE=1 PYTHONUNBUFFERED=1
RUN pip install --no-cache-dir fastapi uvicorn[standard] python-telegram-bot==21.*
COPY app.py .
EXPOSE 8080
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"]

ビルド&プッシュ:

aws ecr create-repository --repository-name telegram-ptb || true
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
REGION=<REGION>
ECR="$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com/telegram-ptb"

aws ecr get-login-password --region $REGION | docker login --username AWS --password-stdin "$ACCOUNT_ID.dkr.ecr.$REGION.amazonaws.com"
docker build -t telegram-ptb .
docker tag telegram-ptb:latest $ECR:latest
docker push $ECR:latest
  1. ECS Fargateサービス(ALBの後ろ)
  • セキュリティグループを作成し、ALBの443へのインバウンドを許可し、ECSタスクがALB SGから8080を許可するようにします。
  • アプリケーションロードバランサー(ALB)を、HTTPSリスナー(443)ACM証明書で作成します。
  • ターゲットグループ: HTTP 8080(IPターゲットタイプ)、ヘルスチェックパス /(FastAPIは404を返します。@app.get("/")ヘルスルートを追加できます)。

タスク定義(Fargate)を作成:

  • コンテナイメージ: $ECR:latest
  • ポートマッピング: 8080
  • 環境変数: TELEGRAM_BOT_TOKEN, WEBHOOK_SECRET
  • タスクロール: 基本的なCloudWatchロギング。

ECSサービスを作成:

  • ランチタイプ Fargate、デジレッドカウント 1+。
  • ALBターゲットグループにアタッチ。

ALBのHTTPSドメイン(またはRoute 53を使用してDNS名をポイント)を確認してください。

  1. TelegramにウェブフックURLを通知
TOKEN=<TOKEN_FROM_BOTFATHER>
SECRET="my-secret-42"
WEBHOOK_URL="https://your.domain.com/telegram/webhook"

curl -X POST "https://api.telegram.org/bot${TOKEN}/setWebhook" \
  -d url="${WEBHOOK_URL}" \
  -d secret_token="${SECRET}" \
  -d drop_pending_updates=true \
  -d allowed_updates='["message","edited_message","callback_query"]'
  1. スケールと運用
  • ECSタスクをスケールアップ/ダウン。ALBはトラフィックを分散します。
  • Secrets Managerでトークンをローテートし、サービスに新しいタスク環境変数を更新します。
  • CloudWatch LogsでアプリログとALBアクセスログ(S3)を使用して観測性を追加します。

どちらを選ぶべきか?

  • Lambda + API Gateway: 最もシンプルで、低トラフィックでは最も安価。1分あたり数回の呼び出しを行うボットに最適。
  • ECS Fargate + ALB: python-telegram-botのフル体験、カスタムミドルウェア、バックグラウンドジョブ、定常的なトラフィックを望む場合に最適。

クイックチェックリスト(両方のアプローチ)

  • HTTPSエンドポイント + **secret_token**を使用し、X-Telegram-Bot-Api-Secret-Tokenヘッダーを検証してください。
  • ウェブフックまたはポーリングのどちらかを選択してください(両方同時に使用しないでください)。
  • 設定はSecrets Managerに保存し、コードに保存しないでください。
  • 観測性を追加:CloudWatch Logs + メトリクス(5xxアラーム)。
  • 必要なアップデートタイプのみを処理し、後で拡張してください。

有用なリンク