Cuándo encajan los Workers
Los Workers se activan por petición: son ideales para bots de webhooks (webhooks de Telegram, interacciones HTTP de Discord, Events API de Slack, WhatsApp Cloud API) y para tareas programadas. No están pensados para mantener una conexión al Gateway de Discord durante horas; usa interacciones HTTP o, si de verdad necesitas un socket de larga duración, un Durable Object.
Preparar el proyecto
npm create cloudflare@latest my-bot -- --type hello-world --ts
cd my-bot
npx wrangler secret put TELEGRAM_BOT_TOKEN
npx wrangler secret put TG_WEBHOOK_SECRET
npx wrangler kv namespace create STATE
# wrangler.toml
name = "my-bot"
main = "src/index.ts"
compatibility_date = "2026-10-01"
[[kv_namespaces]]
binding = "STATE"
id = "<id que imprime el comando create>"
[triggers]
crons = ["7 * * * *"] # minuto 7 de cada hora, UTC
Los secretos se cifran y se exponen como env.NOMBRE; nunca aparecen en el código ni en wrangler.toml.
Un worker completo para el webhook de Telegram
export interface Env { TELEGRAM_BOT_TOKEN: string; TG_WEBHOOK_SECRET: string; STATE: KVNamespace; }
async function tg(env: Env, method: string, body: unknown) {
const r = await fetch(`https://api.telegram.org/bot${env.TELEGRAM_BOT_TOKEN}/${method}`, {
method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(body),
});
if (!r.ok) console.log(JSON.stringify({ level: "warn", method, status: r.status }));
return r;
}
export default {
async fetch(req: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
const url = new URL(req.url);
if (url.pathname !== "/telegram" || req.method !== "POST") return new Response("not found", { status: 404 });
if (req.headers.get("X-Telegram-Bot-Api-Secret-Token") !== env.TG_WEBHOOK_SECRET) return new Response("forbidden", { status: 403 });
const update: any = await req.json();
const seen = `u:${update.update_id}`;
if (await env.STATE.get(seen)) return new Response("dup");
await env.STATE.put(seen, "1", { expirationTtl: 86400 });
const msg = update.message;
if (msg?.text) {
ctx.waitUntil(handle(env, msg)); // responde ya a Telegram y sigue trabajando
}
return new Response("ok");
},
async scheduled(_event: ScheduledEvent, env: Env, ctx: ExecutionContext) {
ctx.waitUntil(sendDueReminders(env));
},
};
async function handle(env: Env, msg: any) {
const text = String(msg.text).slice(0, 1000);
if (text === "/start") return tg(env, "sendMessage", { chat_id: msg.chat.id, text: "¡Hola! Envía /help para ver lo que puedo hacer." });
if (text === "/stop") { await env.STATE.delete(`user:${msg.from.id}`); return tg(env, "sendMessage", { chat_id: msg.chat.id, text: "Tus datos se han borrado." }); }
return tg(env, "sendMessage", { chat_id: msg.chat.id, text: "Comando desconocido. Prueba /help." });
}
Despliega y registra el webhook:
npx wrangler deploy
curl -s "https://api.telegram.org/bot$TELEGRAM_BOT_TOKEN/setWebhook" \
-d "url=https://my-bot.<tu-subdominio>.workers.dev/telegram" -d "secret_token=$TG_WEBHOOK_SECRET"
KV tiene consistencia eventual, así que esta comprobación de duplicados es de mejor esfuerzo. Para un procesamiento estrictamente único usa un Durable Object o una tabla D1 con restricción de unicidad.
Elegir almacenamiento
- KV: almacén clave-valor global, lecturas rápidas, escrituras con consistencia eventual. Preferencias, cachés, claves de deduplicación.
- D1: SQLite serverless. Datos relacionales, recordatorios, registros de auditoría, restricciones de unicidad.
- Durable Objects: un objeto de un solo hilo con almacenamiento fuertemente consistente por clave. Máquinas de estado por chat, limitadores de tasa, WebSockets.
- R2: almacenamiento de objetos para archivos y multimedia, sin costes de salida.
Trabajo en segundo plano con Queues
ctx.waitUntil sirve para unos segundos de trabajo posterior. Para tareas más pesadas (llamadas a IA, difusiones masivas, reintentos), envía un mensaje a una Queue desde el manejador del webhook y procésalo en un worker consumidor. Las colas ofrecen lotes, reintentos automáticos y una cola de mensajes fallidos, y mantienen rápida la respuesta del webhook.
Interacciones de Discord en Workers
Los Workers admiten Ed25519 en la Web Crypto API, así que puedes verificar las firmas de Discord sin dependencias. Responde al PING, contesta al momento los comandos rápidos y, en los lentos, devuelve una respuesta diferida (tipo 5) y termina el trabajo en waitUntil o en una Queue llamando al webhook de seguimiento.
Límites a planificar
- El tiempo de CPU por petición está limitado (mucho menos en el plan gratuito); esperar a un
fetchno cuenta como CPU. - Las subpeticiones por invocación tienen tope; agrupa las llamadas salientes.
- Los cuerpos de petición y la memoria están acotados; envía archivos grandes a R2 en streaming en lugar de cargarlos en memoria.
- Los Cron Triggers se ejecutan en UTC; prueba las expresiones con el generador de cron.
Consulta las cifras vigentes en la documentación de Workers, porque cambian según el plan.
Observabilidad
Usa wrangler tail para ver registros en vivo durante el desarrollo, activa Workers Logs para conservarlos y registra objetos JSON con ID de evento, hash del ID de chat y resultado. Nunca registres el token del bot ni el contenido completo de los mensajes.