Crear un Bot de Telegram con IA en Node.js (Tutorial Completo 2026)
Crea un bot de Telegram con inteligencia artificial usando Node.js, la API de OpenAI o Claude y grammy. Respuestas automáticas, comandos, memoria de conversación y deploy gratuito.
Tabla de contenidos
Los bots de Telegram son sorprendentemente útiles: asistente personal, bot de soporte para tu producto, automatizaciones… Y con IA integrada son mucho más potentes que los bots de comandos de antes.
En este tutorial construyes un bot completo desde cero en menos de una hora.
Lo que vas a construir
- Bot de Telegram que responde a mensajes con IA (OpenAI o Claude)
- Memoria de conversación por usuario (recuerda el contexto)
- Comandos:
/start,/clear(limpiar historial),/help - Límite de mensajes para no arruinarte con la API
- Deploy gratuito en Railway
Requisitos previos
- Node.js 18+ instalado
- Cuenta en Telegram
- API Key de OpenAI o Anthropic (o DeepSeek que es más barata)
Paso 1: Crear el bot en Telegram
Abre Telegram y habla con @BotFather:
/newbot
BotFather te pedirá:
- Un nombre para el bot (ej: “Mi Asistente IA”)
- Un username (debe terminar en
bot, ej:mi_asistente_ia_bot)
Te dará un token como este: 7291834756:AAFxyz123...
Guárdalo, es tu clave para controlar el bot.
Paso 2: Configurar el proyecto
mkdir telegram-bot-ia
cd telegram-bot-ia
npm init -y
npm install grammy openai dotenv
grammy es la mejor librería para bots de Telegram en 2026 (más moderno que node-telegram-bot-api).
Crea la estructura:
telegram-bot-ia/
├── src/
│ ├── bot.js
│ ├── ai.js
│ └── memoria.js
├── .env
├── .env.example
└── package.json
Crea el .env:
# .env
TELEGRAM_TOKEN=7291834756:AAFxyz123...
OPENAI_API_KEY=sk-...
# Opcional: limitar uso
MAX_MENSAJES_POR_USUARIO=50
MAX_TOKENS_RESPUESTA=500
Crea el .env.example (para el repo, sin secrets):
TELEGRAM_TOKEN=tu_token_aqui
OPENAI_API_KEY=tu_api_key_aqui
Añade al .gitignore:
node_modules/
.env
Paso 3: El módulo de IA
// src/ai.js
import OpenAI from 'openai'
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
})
const SISTEMA_PROMPT = `Eres un asistente útil y conciso.
Respondes siempre en español.
Tus respuestas son directas y al punto.
Si no sabes algo, lo dices honestamente.`
/**
* @param {Array<{role: string, content: string}>} historial
* @returns {Promise<string>}
*/
export async function preguntarIA(historial) {
const mensajes = [
{ role: 'system', content: SISTEMA_PROMPT },
...historial
]
const respuesta = await openai.chat.completions.create({
model: 'gpt-4o-mini', // barato y rápido
messages: mensajes,
max_tokens: parseInt(process.env.MAX_TOKENS_RESPUESTA || '500'),
temperature: 0.7,
})
return respuesta.choices[0].message.content
}
Si prefieres usar Claude (Anthropic):
// src/ai.js (versión Claude)
import Anthropic from '@anthropic-ai/sdk'
const client = new Anthropic({
apiKey: process.env.ANTHROPIC_API_KEY
})
export async function preguntarIA(historial) {
const mensajes = historial.map(m => ({
role: m.role === 'user' ? 'user' : 'assistant',
content: m.content
}))
const respuesta = await client.messages.create({
model: 'claude-haiku-4-5', // el más barato de Claude
max_tokens: parseInt(process.env.MAX_TOKENS_RESPUESTA || '500'),
system: SISTEMA_PROMPT,
messages: mensajes,
})
return respuesta.content[0].text
}
O DeepSeek (compatible con la API de OpenAI):
// src/ai.js (versión DeepSeek — más barata)
import OpenAI from 'openai'
const openai = new OpenAI({
apiKey: process.env.DEEPSEEK_API_KEY,
baseURL: 'https://api.deepseek.com'
})
export async function preguntarIA(historial) {
const mensajes = [
{ role: 'system', content: SISTEMA_PROMPT },
...historial
]
const respuesta = await openai.chat.completions.create({
model: 'deepseek-chat',
messages: mensajes,
max_tokens: 500,
})
return respuesta.choices[0].message.content
}
Paso 4: La memoria de conversación
// src/memoria.js
// Almacena el historial por usuario en memoria RAM
// Se pierde al reiniciar, pero es suficiente para empezar
const historiales = new Map()
const MAX_MENSAJES = 20 // Mantener solo los últimos 20 mensajes por usuario
/**
* Obtiene el historial de un usuario
* @param {number} userId
* @returns {Array<{role: string, content: string}>}
*/
export function obtenerHistorial(userId) {
if (!historiales.has(userId)) {
historiales.set(userId, [])
}
return historiales.get(userId)
}
/**
* Añade un mensaje al historial
* @param {number} userId
* @param {'user' | 'assistant'} rol
* @param {string} contenido
*/
export function añadirMensaje(userId, rol, contenido) {
const historial = obtenerHistorial(userId)
historial.push({ role: rol, content: contenido })
// Mantener solo los últimos N mensajes (ventana deslizante)
if (historial.length > MAX_MENSAJES) {
historial.splice(0, historial.length - MAX_MENSAJES)
}
}
/**
* Limpia el historial de un usuario
* @param {number} userId
*/
export function limpiarHistorial(userId) {
historiales.set(userId, [])
}
/**
* Cuenta los mensajes del usuario en las últimas 24h
* Para limitar uso — versión simple en memoria
*/
const contadoresDiarios = new Map()
export function puedeEnviarMensaje(userId) {
const max = parseInt(process.env.MAX_MENSAJES_POR_USUARIO || '50')
const ahora = Date.now()
const dia = 24 * 60 * 60 * 1000
if (!contadoresDiarios.has(userId)) {
contadoresDiarios.set(userId, { count: 0, resetAt: ahora + dia })
}
const contador = contadoresDiarios.get(userId)
// Resetear si pasó un día
if (ahora > contador.resetAt) {
contador.count = 0
contador.resetAt = ahora + dia
}
if (contador.count >= max) return false
contador.count++
return true
}
Paso 5: El bot principal
// src/bot.js
import 'dotenv/config'
import { Bot } from 'grammy'
import { preguntarIA } from './ai.js'
import { obtenerHistorial, añadirMensaje, limpiarHistorial, puedeEnviarMensaje } from './memoria.js'
const bot = new Bot(process.env.TELEGRAM_TOKEN)
// /start — Mensaje de bienvenida
bot.command('start', async (ctx) => {
const nombre = ctx.from?.first_name || 'amigo'
await ctx.reply(
`¡Hola, ${nombre}! 👋\n\n` +
`Soy un asistente con IA. Escríbeme cualquier cosa y te respondo.\n\n` +
`Comandos disponibles:\n` +
`/clear — Borrar historial de conversación\n` +
`/help — Ver esta ayuda`
)
})
// /help — Ayuda
bot.command('help', async (ctx) => {
await ctx.reply(
`*Cómo usarme:*\n\n` +
`Escríbeme cualquier mensaje y te respondo con IA.\n` +
`Recuerdo el contexto de nuestra conversación.\n\n` +
`*Comandos:*\n` +
`/clear — Borra nuestra conversación y empezamos de cero\n` +
`/start — Mensaje de bienvenida`,
{ parse_mode: 'Markdown' }
)
})
// /clear — Limpiar historial
bot.command('clear', async (ctx) => {
limpiarHistorial(ctx.from.id)
await ctx.reply('Historial borrado. ¡Empecemos de cero! 🧹')
})
// Mensajes de texto — la lógica principal
bot.on('message:text', async (ctx) => {
const userId = ctx.from.id
const textoUsuario = ctx.message.text
// Ignorar comandos que no manejamos
if (textoUsuario.startsWith('/')) return
// Verificar límite de mensajes
if (!puedeEnviarMensaje(userId)) {
await ctx.reply(
'⚠️ Has alcanzado el límite diario de mensajes. Vuelve mañana.'
)
return
}
// Mostrar "escribiendo..." mientras procesa
await ctx.replyWithChatAction('typing')
try {
// Añadir mensaje del usuario al historial
añadirMensaje(userId, 'user', textoUsuario)
// Obtener historial completo y preguntar a la IA
const historial = obtenerHistorial(userId)
const respuesta = await preguntarIA(historial)
// Guardar respuesta en historial
añadirMensaje(userId, 'assistant', respuesta)
// Enviar respuesta
await ctx.reply(respuesta, { parse_mode: 'Markdown' })
} catch (error) {
console.error('Error al llamar a la IA:', error)
// Quitar el último mensaje del historial si falló
const historial = obtenerHistorial(userId)
historial.pop()
await ctx.reply(
'❌ Hubo un error al procesar tu mensaje. Inténtalo de nuevo.'
)
}
})
// Manejar errores del bot
bot.catch((err) => {
console.error('Error en el bot:', err)
})
// Arrancar el bot
bot.start()
console.log('🤖 Bot iniciado correctamente')
Añade el type: module al package.json:
{
"name": "telegram-bot-ia",
"version": "1.0.0",
"type": "module",
"scripts": {
"start": "node src/bot.js",
"dev": "node --watch src/bot.js"
},
"dependencies": {
"dotenv": "^16.0.0",
"grammy": "^1.30.0",
"openai": "^4.0.0"
}
}
Paso 6: Probarlo localmente
node src/bot.js
# 🤖 Bot iniciado correctamente
Abre Telegram, busca tu bot por el username y escríbele. Debería responder con IA.
Paso 7: Añadir funcionalidades extra
Comandos personalizados
// Añade esto en bot.js — ejemplo: /resume que resume texto
bot.command('resume', async (ctx) => {
const texto = ctx.message.text.replace('/resume', '').trim()
if (!texto) {
await ctx.reply('Uso: /resume [texto a resumir]')
return
}
await ctx.replyWithChatAction('typing')
// Usar la IA con un prompt específico (sin historial)
const respuesta = await preguntarIA([
{ role: 'user', content: `Resume esto en 3 puntos clave: ${texto}` }
])
await ctx.reply(`📝 *Resumen:*\n\n${respuesta}`, { parse_mode: 'Markdown' })
})
Responder a imágenes (Vision)
bot.on('message:photo', async (ctx) => {
await ctx.replyWithChatAction('typing')
// Obtener la foto en mayor resolución
const foto = ctx.message.photo.at(-1)
const file = await ctx.api.getFile(foto.file_id)
const fotoUrl = `https://api.telegram.org/file/bot${process.env.TELEGRAM_TOKEN}/${file.file_path}`
const caption = ctx.message.caption || '¿Qué hay en esta imagen?'
const respuesta = await openai.chat.completions.create({
model: 'gpt-4o-mini',
messages: [{
role: 'user',
content: [
{ type: 'text', text: caption },
{ type: 'image_url', image_url: { url: fotoUrl } }
]
}],
max_tokens: 500
})
await ctx.reply(respuesta.choices[0].message.content)
})
Paso 8: Deploy gratuito en Railway
-
Sube el código a GitHub (sin el
.env) -
Ve a railway.app y crea una cuenta
-
Crea un nuevo proyecto → “Deploy from GitHub repo”
-
Selecciona tu repositorio
-
En Variables, añade:
TELEGRAM_TOKEN= tu tokenOPENAI_API_KEY= tu API key
-
Railway detecta automáticamente Node.js y ejecuta
npm start
Tu bot estará online 24/7 de forma gratuita (Railway da $5/mes gratis, suficiente para un bot pequeño).
Alternativas gratuitas:
- Render: plan gratuito, se duerme tras 15min de inactividad (el bot no nota esto porque usa polling)
- Fly.io: 3 máquinas gratuitas para siempre
- Hetzner VPS: €4/mes, total control, no hay gratis pero es lo más barato de pago
Resumen del código final
telegram-bot-ia/
├── src/
│ ├── bot.js ← Lógica del bot y comandos
│ ├── ai.js ← Llamadas a OpenAI/Claude/DeepSeek
│ └── memoria.js ← Historial por usuario + límites
├── .env ← Secrets (no subir a git)
├── .env.example ← Template para el repo
├── .gitignore
└── package.json
Con menos de 200 líneas de código tienes un bot de Telegram con IA, memoria de conversación y límites de uso. A partir de aquí puedes añadir:
- Base de datos con SQLite para persistir el historial
- Pagos con Stripe para cobrar por el acceso
- Webhooks en vez de polling para mayor eficiencia
- Comandos adicionales según tu caso de uso
El código completo está pensado para ser el punto de partida, no el destino final.