{"id":143,"date":"2020-05-25T14:39:26","date_gmt":"2020-05-25T19:39:26","guid":{"rendered":"https:\/\/80bits.blog\/?p=143"},"modified":"2020-05-25T14:52:25","modified_gmt":"2020-05-25T19:52:25","slug":"komodo-backend-en-nodejs-para-bot-de-telegram-y-algo-mas","status":"publish","type":"post","link":"https:\/\/80bits.blog\/index.php\/2020\/05\/25\/komodo-backend-en-nodejs-para-bot-de-telegram-y-algo-mas\/","title":{"rendered":"Komodo. Backend en NodeJS para bot de telegram y algo mas."},"content":{"rendered":"\n<p>Reciclando c\u00f3digo y salvando otros pedazos por ah\u00ed que tenia en nodejs, al final organic\u00e9 en un c\u00f3digo simple la implementaci\u00f3n de un bot para telegram.<\/p>\n\n\n\n<p>Para los que no conocen hay una aplicaci\u00f3n llamada <a rel=\"noreferrer noopener\" href=\"https:\/\/telegram.org\/\" target=\"_blank\">Telegram<\/a>, parecida a WhatsApp, pero mejor!!!!.<\/p>\n\n\n\n<p>Pues bien dentro de esta aplicaci\u00f3n 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\u00f3digo al que nombr\u00e9 komodo, (fue lo primero que se me vino a la mente).<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Como Crear un bot de telegram?<\/h2>\n\n\n\n<p>Para crear un bot que hay usar uno que en Telegram nos permite hacer bot, y se llama BotFather, este es muy f\u00e1cil de usar solo hay que interactuar con el y por medio de sus comandos es f\u00e1cil crear un nuevo bot, para no extender este post en este <a rel=\"noreferrer noopener\" href=\"https:\/\/core.telegram.org\/bots\" target=\"_blank\">link<\/a> lo explican mejor.<\/p>\n\n\n\n<p>Lo m\u00e1s importante aqu\u00ed es que, para usar nuestro bot e interactuar con el necesitamos la url del bot que nos proporciona telegram y usar las <a rel=\"noreferrer noopener\" href=\"https:\/\/core.telegram.org\/bots\/api\" target=\"_blank\">apis<\/a> que se proporcionan en la web.<\/p>\n\n\n\n<p>Los bots de telegram tienen much\u00edsimas 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\u00edan los textos.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">C\u00f3digo NodeJS<\/h2>\n\n\n\n<p><a href=\"https:\/\/github.com\/genitalico\/komodo\" target=\"_blank\" rel=\"noreferrer noopener\">Clonar el c\u00f3digo<\/a><\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">git clone <\/pre>\n\n\n\n<p>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\u00e1 revisando los mensajes entrantes, mediante esta <a rel=\"noreferrer noopener\" href=\"https:\/\/core.telegram.org\/bots\/api#getupdates\" target=\"_blank\">api<\/a>.<\/p>\n\n\n\n<p>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\u00e1s que lidiar con un stream muy alto por cada request que le haces a la api, la mejor forma es la segunda opci\u00f3n que tiene telegram y es mediante un <a rel=\"noreferrer noopener\" href=\"https:\/\/core.telegram.org\/bots\/api#setwebhook\" target=\"_blank\">webhook<\/a>, esto una url donde montaras un servicio, en mi caso un servicio en nodejs y telegram redireccionara los mensajes hacia esa api.<\/p>\n\n\n\n<p>Para obtener un servicio donde ejecutar nodejs de forma gratuita, se puede usar por ejemplo <a rel=\"noreferrer noopener\" href=\"https:\/\/azure.microsoft.com\/es-mx\/services\/app-service\/web\/\" target=\"_blank\">azure web apps<\/a>, que te da una instancia gratis (hasta 10) para ejecutar servicios rest.<\/p>\n\n\n\n<p>Este c\u00f3digo de momento solo tiene una funcionalidad que espero poder ir extendiendo con el tiempo, puesto que aun necesito organizar mas c\u00f3digo revuelto que tengo por ah\u00ed, lo \u00fanico que hace es enviar mensajes a un chat o usuario especifico, basado en esta <a rel=\"noreferrer noopener\" href=\"https:\/\/core.telegram.org\/bots\/api#sendmessage\" target=\"_blank\">api<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Ejecutar el c\u00f3digo<\/h3>\n\n\n\n<p>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\u00eda en la cabecera <strong><em>authorization<\/em><\/strong>, esto fue con el fin de que en el futuro pueda dar acceso a mas usuarios y tener m\u00e9tricas de uso para no abusar de la api, si este llega a ser el caso.<\/p>\n\n\n\n<p>Para no usar mongodb, puse un feature flag muy simple en el archivo de configuraci\u00f3n, 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\u00e1cil y r\u00e1pido de utilizar y sin depender de una base de datos.<\/p>\n\n\n\n<p>Esta validaci\u00f3n del token, ya se mediante la base de datos o la variable de tokens se eval\u00faa mediante un middleware que esta en la ruta:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">+ midlewares\/authorization.js<\/pre>\n\n\n\n<p>Dentro hay dos validaciones sencillas que no hacen uso de la autorizaci\u00f3n, uno es el path del \u00edndex y el otro el de telegram update, tal vez en el futuro genere un mejor modelo de validaci\u00f3n de estas rutas.<\/p>\n\n\n\n<p>El servicio en forma local se ejecuta en el puerto 3000.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">PM2 y nodemon<\/h3>\n\n\n\n<p>El servicio esta preparado para ejecutarse con el programa <a rel=\"noreferrer noopener\" href=\"https:\/\/pm2.keymetrics.io\/\" target=\"_blank\">pm2<\/a>, 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\u00e1cil desde el portal del servicio.<\/p>\n\n\n\n<p>Las variables de entorno que se usan son las siguientes:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td class=\"has-text-align-left\" data-align=\"left\"><strong>Nombre<\/strong><\/td><td class=\"has-text-align-left\" data-align=\"left\"><strong>Descripci\u00f3n<\/strong> <\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">NODE_ENV<\/td><td class=\"has-text-align-left\" data-align=\"left\">Entorno de ejecuci\u00f3n, puede ser <em>development<\/em> o <em>production<\/em><\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">TELEGRAM_URL_BOT<\/td><td class=\"has-text-align-left\" data-align=\"left\">La url del bot proporcionada por BotFather<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">MONGO_CS<\/td><td class=\"has-text-align-left\" data-align=\"left\">La connexion de mongodb. Ejemplo: <em>mongodb:\/\/localhost:27017<\/em><\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">MONGO_USER<\/td><td class=\"has-text-align-left\" data-align=\"left\">Usuario para la conexi\u00f3n de mongodb<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">MONGO_PASSWORD<\/td><td class=\"has-text-align-left\" data-align=\"left\">Contrase\u00f1a de usuario para mongodb<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">MONGO_DBNAME<\/td><td class=\"has-text-align-left\" data-align=\"left\">nombre de la base de datos en mongodb<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">MONGO_COLLECTION<\/td><td class=\"has-text-align-left\" data-align=\"left\">nombre de la colecci\u00f3n mongodb<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">FIXED_TOKENS<\/td><td class=\"has-text-align-left\" data-align=\"left\">tokens fijos en caso de no usar la conexi\u00f3n a la base de datos.<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">F_USE_MONGO<\/td><td class=\"has-text-align-left\" data-align=\"left\">Puede tener los valores: <em>true<\/em> para que se use la conexi\u00f3n mongodb, o <em>false<\/em> para que se usen los tokens fijos<\/td><\/tr><tr><td class=\"has-text-align-left\" data-align=\"left\">TG_UPDATE<\/td><td class=\"has-text-align-left\" data-align=\"left\">path donde llegaran las actualizaciones de telegram, este debe ser secreto<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Ejecutar pm2:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">pm2 start ecosystem.config.js --env production<\/pre>\n\n\n\n<p>El comando anterior ejecutara el servicio en modo producci\u00f3n y el c\u00f3digo actual que no se conecta a ninguna base de datos. Es preciso que cambien los tokens que est\u00e1n fijos ya que son bastante simples y solo son para pruebas r\u00e1pidas.<\/p>\n\n\n\n<p>Ejecutar con nodemon:<\/p>\n\n\n\n<p><a rel=\"noreferrer noopener\" href=\"https:\/\/nodemon.io\/\" target=\"_blank\">Nodemon<\/a> es un paquete muy \u00fatil para cuando quieres hacer pruebas locales ya que cualquier cambio que hagas en el c\u00f3digo lo detecta y reinicia el proceso nuevamente, puede ver m\u00e1s sobre \u00e9l en la pagina oficial del paquete.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">nodemon --config dev.config.json<\/pre>\n\n\n\n<p>En este caso tengo un archivo de configuraci\u00f3n para nodemon, este no existe en el repositorio de c\u00f3digo por que es solo para uso local y personal, pero pueden crear uno de manera muy sencilla copiando este texto.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{\n\"env\": {\n\"TELEGRAM_URL_BOT\": \"\",\n\"MONGO_CS\": \"\",\n\"MONGO_USER\": \"\",\n\"MONGO_PASSWORD\": \"\",\n\"MONGO_DBNAME\": \"\",\n\"MONGO_COLLECTION\": \"\",\n\"FIXED_TOKENS\": \"abc,1abc\",\n\"F_USE_MONGO\": \"false\",\n\"TG_UPDATE\": \"update123\"\n}\n}<\/pre>\n\n\n\n<p>Este archivo lo \u00fanico que hace es facilitar escribir las variables de entorno antes mencionadas y ejecutar nodemon.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Enviar mensajes a telegram<\/h3>\n\n\n\n<p>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\u00f3n con el dentro de la app, la segunda es que se necesita un c\u00f3digo de chat que este es dado por el bot y con el que telegram identifica la conversaci\u00f3n con el usuario.<\/p>\n\n\n\n<p>Ya mencione que el fin de este c\u00f3digo 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\u00f3digo en cuesti\u00f3n la ruta del controlador es esta.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{server}\/telegrambot\/update123<\/pre>\n\n\n\n<p>Esta ruta se genera con la variable de entorno <em>TG_UPDATE<\/em> por seguridad, ya que nadie deber\u00eda saber esta ruta mas que telegram, as\u00ed desde la variable de entorno podr\u00e1n agregar un string aleatorio.<\/p>\n\n\n\n<p>El fichero se encuentra en:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">+ routes\/telegramUpdate.js<\/pre>\n\n\n\n<p>El archivo tiene un m\u00e9todo POST para obtener los datos que telegram env\u00eda, pueden ver el modelo de datos que telegram env\u00eda <a rel=\"noreferrer noopener\" href=\"https:\/\/core.telegram.org\/bots\/api#update\" target=\"_blank\">aqu\u00ed<\/a>.<\/p>\n\n\n\n<p>Hay unas lineas muy simples que lo unico que detecta es el comando de telegram <em>\/code<\/em>, este comando lo que hace es que si en la conversaci\u00f3n el usuario escribe <em>\/code<\/em>, el bot enviara el chatId de la conversaci\u00f3n, lo cual es importante para poder usarlo y despu\u00e9s enviar mensajes al usuario.<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">+ routes\/telegramBot.js<\/pre>\n\n\n\n<p>En este archivo se encuentra el m\u00e9todo POST que enviara los mensajes al usuario, haciendo uso de chatId, la ruta en el api es la siguiente:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{server}\/telegrambot\/sendMessage\/:chatId<\/pre>\n\n\n\n<p>El body del mensaje solo esta compuesto por dos variables: <\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{\n\"header\": \"Titulo\",\n\"body\": \"Descripcion\"\n}<\/pre>\n\n\n\n<p>En este punto, se tiene que enviar la cabecera <em>authorization<\/em> con el token valido, ya sea puesto en la variable de entorno o en una base de datos mongodb, de lo contrario se recibir\u00e1 un mensaje http 401 unauthorized.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Script cURL, para enviar un mensaje al bot<\/h4>\n\n\n\n<p>Suponiendo que el chatId sea 123, si quieres tener un script que env\u00ede un mensaje y tal vez automatizarlo con alg\u00fan proceso como cron en linux un ejemplo podr\u00eda ser este:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">#!\/bin\/bash\ncurl --location --request POST 'http:\/\/localhost:3000\/telegrambot\/sendMessage\/123' \\\n--header 'Authorization: abc' \\\n--header 'Content-Type: application\/json' \\\n--data-raw '{\n\"header\": \"Titulo\",\n\"body\": \"Descripcion\"\n}'<\/pre>\n\n\n\n<p>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 <a rel=\"noreferrer noopener\" href=\"https:\/\/core.telegram.org\/bots\/api#getupdates\" target=\"_blank\">api <em>getUpdates<\/em><\/a> de telegram.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\">Enviar mensajes a canales y grupos<\/h4>\n\n\n\n<p>Actualmente el c\u00f3digo soporta que un bot pueda enviar mensajes a canales y grupos, esto es muy \u00fatil si quieres automatizar el env\u00edo de mensajes a tu audiencia, solo hay que cumplir con los requisitos de telegram para esto.<\/p>\n\n\n\n<p>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:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{server}\/telegrambot\/sendMessage\/nombredetucanal<\/pre>\n\n\n\n<p>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 <em>\/code<\/em>, y el bot les enviara el chatId del grupo que por lo general todos empiezan en negativo, ejemplo:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{server}\/telegrambot\/sendMessage\/-1234<\/pre>\n\n\n\n<p>Por el momento el bot solo env\u00eda mensajes, en el futuro espero poder implementar otras opciones mas.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">WebHooks Gitlab<\/h2>\n\n\n\n<p>Como el bot, hace mucho tiempo lo escrib\u00ed por una simple raz\u00f3n, y este que necesitaba algo que me notificara los commits de un proyecto en gitlab, el c\u00f3digo tiene un controlador que hace precisamente eso, hace uso de los <a rel=\"noreferrer noopener\" href=\"https:\/\/docs.gitlab.com\/ee\/user\/project\/integrations\/webhooks.html\" target=\"_blank\">webhooks<\/a> de gitlab para enviar mensajes.<\/p>\n\n\n\n<p>Actualmente solo tiene dos implementados, <em>Push Events<\/em> y <em>Pipeline Events<\/em>, para hacer uso de ellos el servicio tiene la siguiente ruta:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">{server}\/gitlab\/webhooks\/:chatId<\/pre>\n\n\n\n<p>Para saber que tipo de hook es, gitlab env\u00eda una cabecera que se llama <em>X-Gitlab-Event<\/em>:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><tbody><tr><td>X-Gitlab-Event<\/td><td>Descripci\u00f3n <\/td><\/tr><tr><td>Push Hook<\/td><td>Eventos push, commit<\/td><\/tr><tr><td>Pipeline Hook<\/td><td>Evento pipeline<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p>Tambi\u00e9n env\u00eda una cabecera llamada <em>X-Gitlab-Token<\/em>, esto es por si necesitas un token de autorizaci\u00f3n para el env\u00edo del hook, en este caso se usa el mismo que en la cabecera authorization, y el c\u00f3digo verifica si existe algo ah\u00ed o en la de gitlab y procede con el permiso si el token es valido.<\/p>\n\n\n\n<p>Y eso es todo.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Pendientes a desarrollar<\/h2>\n\n\n\n<ul class=\"wp-block-list\"><li>Envio de im\u00e1genes<\/li><li>Mejor implementaci\u00f3n de permisos sin token<\/li><li>Mejora en la b\u00fasqueda de comandos para telegram<\/li><li>M\u00e9tricas de uso por token, guardar esto en mongodb<\/li><li>Implementar todos los webhooks de gitlab<\/li><li>Implementar los hooks de GitHub.<\/li><li>Validaciones del modelo sendMessage. Pienso usar <a rel=\"noreferrer noopener\" href=\"https:\/\/github.com\/hapijs\/joi\" target=\"_blank\">happijs<\/a><\/li><li>Incorporar el paquete <a rel=\"noreferrer noopener\" href=\"https:\/\/www.npmjs.com\/package\/helmet\" target=\"_blank\">Helmet<\/a>. Para aplicar buenas practicas que especifica la documentaci\u00f3n de expressjs<\/li><\/ul>\n\n\n\n<p>Se libre de hacer un fork del c\u00f3digo, y enviarme mejoras si gustas..<\/p>\n\n\n\n<p><a href=\"https:\/\/unsplash.com\/@rocknrollmonkey\" target=\"_blank\" rel=\"noreferrer noopener\">Foto de portada.<\/a> <\/p>\n","protected":false},"excerpt":{"rendered":"<p>C\u00f3digo para bot en telegram, envian mensajes simples a un usuario y envia mensajes haciendo uso de los webhooks de gitlab<\/p>\n","protected":false},"author":1,"featured_media":153,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_uf_show_specific_survey":0,"_uf_disable_surveys":false,"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[13,30,28,20,29,31],"tags":[38,37,36,33,34,35,32,5],"class_list":["post-143","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dev","category-gitlab","category-js","category-lenguajes-de-programacion","category-nodejs","category-telegram","tag-azure","tag-backend","tag-bot","tag-gitlab","tag-javascript","tag-js","tag-nodejs","tag-telegram"],"jetpack_featured_media_url":"https:\/\/80bits.blog\/wp-content\/uploads\/2020\/05\/rock-n-roll-monkey-FTfjMijq-Ws-unsplash.jpg","jetpack-related-posts":[],"jetpack_sharing_enabled":true,"jetpack_likes_enabled":true,"_links":{"self":[{"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/posts\/143","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/comments?post=143"}],"version-history":[{"count":37,"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/posts\/143\/revisions"}],"predecessor-version":[{"id":182,"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/posts\/143\/revisions\/182"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/media\/153"}],"wp:attachment":[{"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/media?parent=143"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/categories?post=143"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/80bits.blog\/index.php\/wp-json\/wp\/v2\/tags?post=143"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}