Crea el bot con @BotFather
Abre un chat con @BotFather (revisa la marca azul de verificación), envía
/newbot, elige un nombre visible y un username que termine enbot. BotFather te responde con un token.Figura 2. Creación del bot con @BotFather (ilustración). El último mensaje contiene el token: trátalo como una contraseña. Crea el proyecto
mkdir mi-bot && cd mi-bot npm init -y npm pkg set type=module npm install grammytype=modulete permite usarimportyawaiten el nivel superior.Guarda el token fuera del código
printf 'BOT_TOKEN=123456789:replace-me\n' > .env printf '.env\nnode_modules\n' > .gitignorePega tu token real en
.env. Si alguna vez se filtra, usa/revokeen BotFather para obtener uno nuevo.Escribe el bot
bot.jsimport { Bot } from "grammy"; const token = process.env.BOT_TOKEN; if (!token) throw new Error("BOT_TOKEN is missing (see .env)"); const bot = new Bot(token); bot.command("start", (ctx) => ctx.reply(`Hola, ${ctx.from?.first_name ?? "!"} Send /help.`)); bot.command("help", (ctx) => ctx.reply("/start - greet\n/help - this list\n/ping - check the bot")); bot.command("ping", (ctx) => ctx.reply("pong")); // Echo any other text message bot.on("message:text", (ctx) => ctx.reply(`You said: ${ctx.message.text}`)); // Never crash on a handler error; never log the token bot.catch((err) => console.error("handler error:", err.error?.message ?? err.message)); await bot.api.setMyCommands([ { command: "start", description: "Start the bot" }, { command: "help", description: "Show commands" }, { command: "ping", description: "Check the bot is alive" }, ]); bot.start({ onStart: (me) => console.log(`@${me.username} running with long polling`) }); process.once("SIGINT", () => bot.stop()); process.once("SIGTERM", () => bot.stop());Los manejadores se evalúan en orden: primero los comandos y luego el de texto genérico.
bot.catchevita que un update con error detenga el bot.Ejecútalo y pruébalo desde tu teléfono
node --env-file=.env bot.jsFigura 3. Salida esperada al iniciar el bot (ilustración). Abre tu bot por su username, presiona Iniciar y prueba
/ping. Detenlo con Ctrl+C.Mantenlo encendido
Tu laptop no va a estar prendida siempre. Despliega el mismo código en un hosting que corra 24/7: sigue el tutorial de Railway (sin servidor que administrar) o el tutorial de VPS en Vultr (control total). Compara opciones en el comparador de hosting.
Solución de problemas
- 401 Unauthorized: el token es incorrecto o fue revocado. Cópialo de nuevo desde BotFather.
- 409 Conflict: otro proceso está haciendo polling con el mismo token, o hay un webhook configurado. Detén la otra copia; si había webhook, llama a deleteWebhook antes de start().
- El bot no responde en grupos: por defecto el modo de privacidad solo entrega comandos y respuestas al bot. Cámbialo con /setprivacy en BotFather solo si de verdad necesitas todos los mensajes.
Documentación oficial: primeros pasos de grammY, Telegram Bot API.