Implementazione di un bot Telegram in Python e JavaScript con distribuzione su AWS
E distribuire un nuovo bot Telegram su AWS
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.
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
- Crea un bot tramite @BotFather → ottieni il token.
- Crea un piccolo’app con python-telegram-bot o Telegraf.
- 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
- Python 3.10+ e
Crea il tuo bot con BotFather (ottieni il token)
- In Telegram, cerca @BotFather e inizia una chat.
- Invia
/newbot
e segui le istruzioni per nominare il tuo bot e scegliere un nome unico (deve terminare conbot
), ad esempioexample_helper_bot
. - 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 supportasecret_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). UsaApplicationBuilder().token(...).build()
, espone una route POST che alimenta gli aggiornamenti JSON in entrata nell’applicazione e chiamabot.set_webhook(...)
. Vedi le documentazioni diApplicationBuilder
per i modelli di costruzione. ([docs.python-telegram-bot.org][2])
Testando localmente? Usa un tunnel (es.
ngrok
) per esporrehttps://...
a Telegram, quindi chiamasetWebhook
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’intestazioneX-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).
- 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.
- 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à.
- 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"
- 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.
- 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.
- 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
- 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
- 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).
- 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"]'
- 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’intestazioneX-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.
Link utili
- https://docs.python-telegram-bot.org
- https://telegraf.js.org
- https://core.telegram.org/bots/tutorial
- https://core.telegram.org/bots/api
- Panoramica AWS CDK, esempi TypeScript e Python e considerazioni sulle prestazioni
- Prestazioni AWS Lambda: JavaScript vs Python vs Golang
- Lambda a strati con AWS SAM e Python
- AWS SAM + AWS SQS + Python PowerTools
- Scheda Python
- uv - Nuovo gestore di pacchetti, progetti e ambienti Python
- Installare Node.js