Reciclando código y salvando otros pedazos por ahí que tenia en nodejs, al final organicé en un código simple la implementación de un bot para telegram.
Para los que no conocen hay una aplicación llamada Telegram, parecida a WhatsApp, pero mejor!!!!.
Pues bien dentro de esta aplicación se pueden usar bots para distintos fines, los hay para buscar gif, videos, buscar en la wikipedia entre muchos otros, y lo mejor es que cualquiera y de manera muy sencilla puede crear su propio bot, para este fin es que he desarrollado este código al que nombré komodo, (fue lo primero que se me vino a la mente).
Como Crear un bot de telegram?
Para crear un bot que hay usar uno que en Telegram nos permite hacer bot, y se llama BotFather, este es muy fácil de usar solo hay que interactuar con el y por medio de sus comandos es fácil crear un nuevo bot, para no extender este post en este link lo explican mejor.
Lo más importante aquí es que, para usar nuestro bot e interactuar con el necesitamos la url del bot que nos proporciona telegram y usar las apis que se proporcionan en la web.
Los bots de telegram tienen muchísimas funcionalidades, desde el mas simple como interactuar con el usuario de uno a uno, hasta interactuar en grupos como por ejemplo moderador de textos o sugerir algo para los integrantes mientras se envían los textos.
Código NodeJS
git clone
Hay dos formas de que nuestro bot tenga interactividad o por lo menos reciba mensajes, uno es mediante un long polling, esto es que un proceso este cada cierto tiempo estará revisando los mensajes entrantes, mediante esta api.
Este proceso antes mencionado es un poco lento, y desde mi punto de vista complejo, ya que si el bot tiene muchos mensajes seguidos tendrás que lidiar con un stream muy alto por cada request que le haces a la api, la mejor forma es la segunda opción que tiene telegram y es mediante un webhook, esto una url donde montaras un servicio, en mi caso un servicio en nodejs y telegram redireccionara los mensajes hacia esa api.
Para obtener un servicio donde ejecutar nodejs de forma gratuita, se puede usar por ejemplo azure web apps, que te da una instancia gratis (hasta 10) para ejecutar servicios rest.
Este código de momento solo tiene una funcionalidad que espero poder ir extendiendo con el tiempo, puesto que aun necesito organizar mas código revuelto que tengo por ahí, lo único que hace es enviar mensajes a un chat o usuario especifico, basado en esta api.
Ejecutar el código
El servicio tiene un dependencia con mongodb, que en este caso y para este release solo usa la base de datos para obtener el token que se envía en la cabecera authorization, esto fue con el fin de que en el futuro pueda dar acceso a mas usuarios y tener métricas de uso para no abusar de la api, si este llega a ser el caso.
Para no usar mongodb, puse un feature flag muy simple en el archivo de configuración, donde en lugar de usar mongodb para buscar el token valido, usa una variable de entorno donde puedes especificar tokens separados por comas, para que sea mas fácil y rápido de utilizar y sin depender de una base de datos.
Esta validación del token, ya se mediante la base de datos o la variable de tokens se evalúa mediante un middleware que esta en la ruta:
+ midlewares/authorization.js
Dentro hay dos validaciones sencillas que no hacen uso de la autorización, uno es el path del índex y el otro el de telegram update, tal vez en el futuro genere un mejor modelo de validación de estas rutas.
El servicio en forma local se ejecuta en el puerto 3000.
PM2 y nodemon
El servicio esta preparado para ejecutarse con el programa pm2, si es el caso de que usaste un servicio de azure, entonces pm2 no es necesario puesto que las variables de entorno se pueden configurar de manera fácil desde el portal del servicio.
Las variables de entorno que se usan son las siguientes:
Nombre | Descripción |
NODE_ENV | Entorno de ejecución, puede ser development o production |
TELEGRAM_URL_BOT | La url del bot proporcionada por BotFather |
MONGO_CS | La connexion de mongodb. Ejemplo: mongodb://localhost:27017 |
MONGO_USER | Usuario para la conexión de mongodb |
MONGO_PASSWORD | Contraseña de usuario para mongodb |
MONGO_DBNAME | nombre de la base de datos en mongodb |
MONGO_COLLECTION | nombre de la colección mongodb |
FIXED_TOKENS | tokens fijos en caso de no usar la conexión a la base de datos. |
F_USE_MONGO | Puede tener los valores: true para que se use la conexión mongodb, o false para que se usen los tokens fijos |
TG_UPDATE | path donde llegaran las actualizaciones de telegram, este debe ser secreto |
Ejecutar pm2:
pm2 start ecosystem.config.js --env production
El comando anterior ejecutara el servicio en modo producción y el código actual que no se conecta a ninguna base de datos. Es preciso que cambien los tokens que están fijos ya que son bastante simples y solo son para pruebas rápidas.
Ejecutar con nodemon:
Nodemon es un paquete muy útil para cuando quieres hacer pruebas locales ya que cualquier cambio que hagas en el código lo detecta y reinicia el proceso nuevamente, puede ver más sobre él en la pagina oficial del paquete.
nodemon --config dev.config.json
En este caso tengo un archivo de configuración para nodemon, este no existe en el repositorio de código por que es solo para uso local y personal, pero pueden crear uno de manera muy sencilla copiando este texto.
{ "env": { "TELEGRAM_URL_BOT": "", "MONGO_CS": "", "MONGO_USER": "", "MONGO_PASSWORD": "", "MONGO_DBNAME": "", "MONGO_COLLECTION": "", "FIXED_TOKENS": "abc,1abc", "F_USE_MONGO": "false", "TG_UPDATE": "update123" } }
Este archivo lo único que hace es facilitar escribir las variables de entorno antes mencionadas y ejecutar nodemon.
Enviar mensajes a telegram
Para que el bot pueda enviarle un mensaje a un usuario de Telegram, se tienen que cumplir unas condiciones, la primera es que el usuario haga uso del bot, esto es que genere una conversación con el dentro de la app, la segunda es que se necesita un código de chat que este es dado por el bot y con el que telegram identifica la conversación con el usuario.
Ya mencione que el fin de este código es no usar long polling y para ello se especifica a telegram el webhook hacia donde tiene que redirigir los mensajes, este webhook no es mas que la url del servicio a donde todo tiene que llegar, para el código en cuestión la ruta del controlador es esta.
{server}/telegrambot/update123
Esta ruta se genera con la variable de entorno TG_UPDATE por seguridad, ya que nadie debería saber esta ruta mas que telegram, así desde la variable de entorno podrán agregar un string aleatorio.
El fichero se encuentra en:
+ routes/telegramUpdate.js
El archivo tiene un método POST para obtener los datos que telegram envía, pueden ver el modelo de datos que telegram envía aquí.
Hay unas lineas muy simples que lo unico que detecta es el comando de telegram /code, este comando lo que hace es que si en la conversación el usuario escribe /code, el bot enviara el chatId de la conversación, lo cual es importante para poder usarlo y después enviar mensajes al usuario.
+ routes/telegramBot.js
En este archivo se encuentra el método POST que enviara los mensajes al usuario, haciendo uso de chatId, la ruta en el api es la siguiente:
{server}/telegrambot/sendMessage/:chatId
El body del mensaje solo esta compuesto por dos variables:
{ "header": "Titulo", "body": "Descripcion" }
En este punto, se tiene que enviar la cabecera authorization con el token valido, ya sea puesto en la variable de entorno o en una base de datos mongodb, de lo contrario se recibirá un mensaje http 401 unauthorized.
Script cURL, para enviar un mensaje al bot
Suponiendo que el chatId sea 123, si quieres tener un script que envíe un mensaje y tal vez automatizarlo con algún proceso como cron en linux un ejemplo podría ser este:
#!/bin/bash curl --location --request POST 'http://localhost:3000/telegrambot/sendMessage/123' \ --header 'Authorization: abc' \ --header 'Content-Type: application/json' \ --data-raw '{ "header": "Titulo", "body": "Descripcion" }'
Un mensaje se puede enviar en local siempre y cuando se cumplan las condiciones de tener la url del bot de telegram y el chatId, si no cuentas con un servidor en la nube y quieres saber el chatId, simplemente escribe algo en el chat y puede obtener el chatId mediante la api getUpdates de telegram.
Enviar mensajes a canales y grupos
Actualmente el código soporta que un bot pueda enviar mensajes a canales y grupos, esto es muy útil si quieres automatizar el envío de mensajes a tu audiencia, solo hay que cumplir con los requisitos de telegram para esto.
Para enviar mensajes a un canal, el bot tiene que estar dentro del canal y ser administrador y en lugar de un chatId, tienen que poner el nombre del canal, ejemplo:
{server}/telegrambot/sendMessage/nombredetucanal
Para enviar mensaje a un grupo el bot tiene que estar igual dentro del grupo, no es necesario que sea administrador pero si que pueda enviar mensajes, y tienen que saber el chatId del grupo, esto lo pueden obtener de la misma manera que en un chat, con el comando /code, y el bot les enviara el chatId del grupo que por lo general todos empiezan en negativo, ejemplo:
{server}/telegrambot/sendMessage/-1234
Por el momento el bot solo envía mensajes, en el futuro espero poder implementar otras opciones mas.
WebHooks Gitlab
Como el bot, hace mucho tiempo lo escribí por una simple razón, y este que necesitaba algo que me notificara los commits de un proyecto en gitlab, el código tiene un controlador que hace precisamente eso, hace uso de los webhooks de gitlab para enviar mensajes.
Actualmente solo tiene dos implementados, Push Events y Pipeline Events, para hacer uso de ellos el servicio tiene la siguiente ruta:
{server}/gitlab/webhooks/:chatId
Para saber que tipo de hook es, gitlab envía una cabecera que se llama X-Gitlab-Event:
X-Gitlab-Event | Descripción |
Push Hook | Eventos push, commit |
Pipeline Hook | Evento pipeline |
También envía una cabecera llamada X-Gitlab-Token, esto es por si necesitas un token de autorización para el envío del hook, en este caso se usa el mismo que en la cabecera authorization, y el código verifica si existe algo ahí o en la de gitlab y procede con el permiso si el token es valido.
Y eso es todo.
Pendientes a desarrollar
- Envio de imágenes
- Mejor implementación de permisos sin token
- Mejora en la búsqueda de comandos para telegram
- Métricas de uso por token, guardar esto en mongodb
- Implementar todos los webhooks de gitlab
- Implementar los hooks de GitHub.
- Validaciones del modelo sendMessage. Pienso usar happijs
- Incorporar el paquete Helmet. Para aplicar buenas practicas que especifica la documentación de expressjs
Se libre de hacer un fork del código, y enviarme mejoras si gustas..