Implementazione di un bot Telegram in Python e JavaScript con distribuzione su AWS

E distribuire un nuovo bot Telegram su AWS

Indice

Ecco i miei appunti con un tutorial passo dopo passo su come implementare e distribuire su AWS un bot Telegram. Ho aggiunto un quick start (long polling) e un percorso pronto per la produzione (webhooks), con esempi in Python e Node.js.

robot che lavorano in IT

I bot Telegram sono applicazioni che vengono eseguite sui tuoi server e comunicano con gli utenti tramite Telegram. Registreremo un bot, scriveremo un piccolo programma e lo connetteremo all’API Bot di Telegram.

TL;DR

  1. Crea un bot tramite @BotFather → ottieni il token.
  2. Crea un piccolo’app con python-telegram-bot o Telegraf.
  3. Inizia con long polling localmente; passa a webhooks per la produzione.

Cosa ti servirà

  • Un account Telegram (mobile o desktop)

  • Un terminale di base + editor di codice

  • Uno di questi:

    • Python 3.10+ e pip
    • Node.js 18+ e npm/pnpm/yarn

Crea il tuo bot con BotFather (ottieni il token)

  1. In Telegram, cerca @BotFather e inizia una chat.
  2. Invia /newbot e segui le istruzioni per nominare il tuo bot e scegliere un nome unico (deve terminare con bot), ad esempio example_helper_bot.
  3. BotFather risponde con un token API come 123456:ABC-DEF.... Trattalo come una password; non includerlo in Git. Puoi sempre rigenerarlo tramite BotFather se viene compromesso.

Suggerimento: Il tutorial ufficiale di Telegram passa attraverso questi esatti passaggi e spiega come verificare il token con getMe.


Scegli il tuo stack

Puoi chiamare direttamente l’HTTP Bot API, ma l’uso di una libreria mantenuta è più veloce.

  • Python: python-telegram-bot (asincrono, moderno). ([https://docs.python-telegram-bot.org])
  • Node.js: telegraf (maturità, amico di TypeScript). ([https://telegraf.js.org])

L’API Bot stessa è documentata qui e rappresenta la fonte di verità per metodi come getUpdates e setWebhook.


Quick Start: esegui localmente con long polling

Il long polling è perfetto per lo sviluppo locale: il tuo bot chiede ripetutamente a Telegram nuovi aggiornamenti tramite getUpdates. (In produzione, probabilmente passerai ai webhooks.)

Telegram supporta due modi esclusivi per ricevere aggiornamenti: getUpdates (polling) o webhooks; gli aggiornamenti vengono conservati per un massimo di 24 ore.

Opzione A: Python (con python-telegram-bot)

Installa e struttura

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

Codice: 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("👋 Ciao! Inviami qualcosa e lo ripeterò.")

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()  # long polling

if __name__ == "__main__":
    main()

Esegui:

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

Questo utilizza ApplicationBuilder dalle ultime documentazioni stabili.

Opzione B: Node.js (con telegraf)

Installa e struttura

npm init -y
npm i telegraf

Codice: index.js

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

const bot = new Telegraf(process.env.BOT_TOKEN); // setta la variabile d'ambiente BOT_TOKEN
bot.start((ctx) => ctx.reply('👋 Ciao! Inviami qualcosa e lo ripeterò.'));
bot.on('text', (ctx) => ctx.reply(ctx.message.text));

bot.launch();

// arresto pulito
process.once('SIGINT', () => bot.stop('SIGINT'));
process.once('SIGTERM', () => bot.stop('SIGTERM'));

Esegui:

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

Questo specchio l’esempio ufficiale di Telegraf “Getting started”.


Rendilo utile (comandi e pulsanti)

Aggiungi un comando /help

Python

from telegram.ext import CommandHandler

async def help_cmd(update: Update, context: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("/start – saluta\n/help – questo aiuto")

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

CommandHandler è il modo standard per legare i /comandi.

Node (Telegraf)

bot.help((ctx) => ctx.reply('/start – saluta\n/help – questo aiuto'));

Pulsanti di risposta rapida (tastiera personalizzata)

Python

from telegram import ReplyKeyboardMarkup

async def start(update: Update, context):
    keyboard = [["Help", "About"]]
    await update.message.reply_text(
        "Scegli un'opzione:",
        reply_markup=ReplyKeyboardMarkup(keyboard, resize_keyboard=True)
    )

Node (Telegraf)

const { Markup } = require('telegraf');
bot.start((ctx) => {
  ctx.reply("Scegli un'opzione:", Markup.keyboard([["Help", "About"]]).resize());
});

Produzione: passa ai webhooks

In produzione, Telegram inoltra gli aggiornamenti al tuo endpoint HTTPS. Puoi impostarlo con setWebhook, fornendo opzionalmente un token segreto in modo da poter verificare la richiesta tramite l’intestazione X-Telegram-Bot-Api-Secret-Token.

Imposta un webhook con curl:

TOKEN=123456:ABC-DEF...
WEBHOOK_URL="https://your-domain.com/telegram/${TOKEN}"  # include un percorso segreto
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

Punti chiave dalla specifica:

  • setWebhook richiede un URL HTTPS e supporta secret_token.
  • Mentre è impostato un webhook, non puoi utilizzare getUpdates.
  • I porti supportati includono 443, 80, 88, 8443.

Opzioni del server webhook

  • Node (Telegraf) webhook integrato:
bot.launch({
  webhook: {
    domain: "your-domain.com",
    port: 8080,
    // token segreto opzionale
    secretToken: process.env.WEBHOOK_SECRET
  }
});

Telegraf espone opzioni di primo livello per i webhook e si integra con Express/Fastify/Cloudflare Workers, ecc.

  • Python (python-telegram-bot) PTB funziona bene con framework ASGI/WSGI (FastAPI, Starlette, Flask). Usa ApplicationBuilder().token(...).build(), espone una route POST che alimenta gli aggiornamenti JSON in entrata nell’applicazione e chiama bot.set_webhook(...). Vedi le documentazioni di ApplicationBuilder per i modelli di costruzione. ([docs.python-telegram-bot.org][2])

Testando localmente? Usa un tunnel (es. ngrok) per esporre https://... a Telegram, quindi chiama setWebhook con l’URL pubblico.


Configura i comandi del bot (opzionale ma utile per gli utenti)

Puoi definire l’elenco dei comandi del bot (quello che vedono gli utenti quando digitano /) in due modi:

  • In BotFather tramite /mybots → Modifica Bot → Modifica Comandi, o
  • Programmaticamente con setMyCommands utilizzando la tua libreria o l’API Bot raw.

La guida ufficiale di Telegram “From BotFather to ‘Hello World’” collega a esempi più avanzati di gestione dei comandi se vuoi andare più a fondo.


Considerazioni per la distribuzione

Qualsiasi host in grado di gestire HTTPS funziona:

  • Un piccolo VM (Ubuntu + systemd)
  • Serverless (AWS Lambda/Cloud Functions) con integrazione webhook
  • Container su piattaforme come Fly.io/Render/Heroku-like

Le documentazioni di Telegraf includono esempi per Lambda, GCF, Express, Fastify, ecc.


Checklist di sicurezza e affidabilità

  • Mantieni il token segreto (variabili d’ambiente, gestore segreti). Annullalo in BotFather se compromesso.
  • Utilizza secret_token con i webhooks e verifica l’intestazione X-Telegram-Bot-Api-Secret-Token.
  • Non mescolare polling e webhooks; scegli uno dei due.
  • Gestisci gli errori e i retry: Telegram riperterà le risposte non 2xx dei webhooks. Logga correttamente.
  • Sii attento ai tipi di aggiornamento (messaggi, callback, query inline, pagamenti, ecc.) e iscriviti solo a quelli di cui hai bisogno (allowed_updates).

Utilizzo diretto dell’HTTP API (opzionale)

Puoi chiamare endpoint come:

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

Utilizza GET o POST con JSON o dati form come specificato.


Cosa fare e leggere successivamente

  • Riferimento ufficiale Bot API: metodi, oggetti, limiti, formattazione, pagamenti, modalità inline.
  • Guida ufficiale “From BotFather to ‘Hello World’”: walkthrough più approfondito e esempi in più lingue.
  • Documentazioni python-telegram-bot (stabile): modelli asincroni moderni.
  • Documentazioni Telegraf: ricette rapide, helper per webhooks e tipi TS.

Passaggi per la distribuzione su AWS per la versione Python

Abbiamo due opzioni principali per la distribuzione del bot Telegram su infrastruttura AWS:

  • A) Serverless (API Gateway + Lambda + Secrets Manager) — più economico e semplice da eseguire, ideale per traffico modesto.
  • B) Containerizzato (ECS Fargate + ALB + ACM) — adatto per traffico costante e librerie a lunga durata come python-telegram-bot in modalità webhook.

A) Serverless su AWS (API Gateway + Lambda)

Utilizza questa opzione quando desideri zero server e un costo quasi zero inattivo. Il codice seguente gestisce direttamente i webhooks di Telegram (nessun ciclo di eventi in esecuzione).

  1. Prepara un gestore Lambda minimo (Python)

Crea handler.py:

import json, os, urllib.request

BOT_TOKEN = os.environ["TELEGRAM_BOT_TOKEN"]
SECRET_HEADER = os.environ.get("WEBHOOK_SECRET", "")  # deve corrispondere al secret_token impostato nell'API Bot

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) Verifica l'intestazione segreta di Telegram (impostata quando configuri il webhook)
    if SECRET_HEADER:
        if event.get("headers", {}).get("X-Telegram-Bot-Api-Secret-Token") != SECRET_HEADER:
            return {"statusCode": 401, "body": "segreto non valido"}

    # 2) Analizza l'aggiornamento
    body = event.get("body") or "{}"
    update = json.loads(body)

    message = update.get("message") or update.get("edited_message")
    if not message:
        # ignora gli aggiornamenti non di messaggio (callback_query, inline_query, ecc.) per ora
        return {"statusCode": 200, "body": "ok"}

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

    # 3) Routing semplice
    if text.startswith("/start"):
        reply(chat_id, "👋 Ciao da AWS Lambda! Inviami qualsiasi testo e lo ripeterò.")
    elif text.startswith("/help"):
        reply(chat_id, "/start – saluta\n/help – aiuto\n(Deployed on AWS Lambda)")
    elif text:
        reply(chat_id, text)  # ripeti

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

Questo utilizza chiamate HTTPS dirette all’API Bot per mantenere Lambda leggera e amichevole per i cold start. Puoi espandere il routing in seguito.

  1. Pacchettizza e distribuisci la Lambda
# Layout del progetto
# .
# ├─ handler.py
# └─ requirements.txt   # (lascia vuoto per questo esempio minimo)

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}'

Ruolo IAM (<LambdaExecutionRole>) deve avere AWSLambdaBasicExecutionRole per i log di CloudWatch.

Preferisci archiviare il tuo token in AWS Secrets Manager e caricarlo all’inizializzazione—questo esempio utilizza le variabili d’ambiente per brevità.

  1. Crea un endpoint HTTPS (API Gateway)
# HTTP API (non REST) per latenza inferiore
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)

# Aggiungi una route predefinita 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  # ad esempio, https://abc123.execute-api.ap-somewhere.amazonaws.com

Assicurati che la permesso Lambda venga aggiunta automaticamente; se non lo è:

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. Indirizza Telegram al tuo webhook
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

Invia /start al tuo bot—i messaggi passeranno attraverso API Gateway → Lambda.

  1. Log, retry e aggiornamenti
  • Monitora i log: aws logs tail /aws/lambda/telegram-bot-webhook --follow
  • Telegram riprova quando il tuo endpoint non è 2xx; mantieni le risposte veloci.
  • Per distribuire nuovo codice: rizippa + aws lambda update-function-code --function-name telegram-bot-webhook --zip-file fileb://function.zip.

B) python-telegram-bot containerizzato su ECS Fargate + ALB

Utilizza questa opzione quando desideri l’ergonomia di python-telegram-bot (PTB) con un’app asincrona e connessioni persistenti. Eseguiamo PTB dietro un load balancer HTTPS.

  1. Codice dell’app (FastAPI + webhook 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  # applicazione PTB

async def start_cmd(update: Update, ctx: ContextTypes.DEFAULT_TYPE):
    await update.message.reply_text("🚀 Ciao da 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))
    # No run_polling(); alimentiamo i webhooks manualmente.

@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}  # ignora silenziosamente

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

Entrypoint di 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"]

Costruisci e pubblica:

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. Servizio ECS Fargate dietro un ALB
  • Crea un Gruppo di Sicurezza che permetta l’ingresso 443 sull’ALB; le istanze ECS permettano 8080 dal gruppo di sicurezza dell’ALB.
  • Crea un Load Balancer Applicativo (ALB) con un listener HTTPS (443) e un certificato ACM per il tuo dominio.
  • Gruppo di destinazione: HTTP 8080 (tipo target IP), percorso di controllo della salute / (FastAPI serve 404; puoi aggiungere @app.get("/") per il percorso di salute).

Crea un Task Definition (Fargate) con:

  • Immagine del contenitore: $ECR:latest
  • Mappatura porta: 8080
  • Variabili d’ambiente: TELEGRAM_BOT_TOKEN, WEBHOOK_SECRET
  • Ruolo del task: ruolo base per i log di CloudWatch.

Crea un Servizio ECS:

  • Tipo di avvio Fargate, numero desiderato 1+.
  • Collega al gruppo di destinazione dell’ALB.

Nota il dominio HTTPS pubblico dell’ALB (o usa Route 53 per puntare il tuo nome DNS).

  1. Informati a Telegram del tuo URL webhook
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. Scalare e operare
  • Scalare le istanze ECS su/giù; l’ALB distribuirà il traffico.
  • Ruotare i token in Secrets Manager, aggiorna il servizio con nuove variabili d’ambiente del task.
  • Usa CloudWatch Logs per i log dell’app e log di accesso dell’ALB (S3).

Quale dobbiamo scegliere?

  • Lambda + API Gateway: più semplice, più economico a basso volume; ideale per bot che fanno pochi chiamate al minuto.
  • ECS Fargate + ALB: migliore quando si desidera l’esperienza completa di python-telegram-bot, middleware personalizzati, lavori in background e traffico costante.

Checklist rapida (entrambi gli approcci)

  • Utilizza un endpoint HTTPS + secret_token e verifica l’intestazione X-Telegram-Bot-Api-Secret-Token.
  • Scegli webhook O polling (non entrambi).
  • Conserva la configurazione in Secrets Manager, non nel codice.
  • Aggiungi osservabilità: CloudWatch Logs + metriche (allarmi 5xx).
  • Gestisci solo i tipi di aggiornamento necessari; espandi in seguito.