Creación de un bot de Telegram en PHP

Siempre defiendo que Telegram es la mejor app de mensajería que existe a día de hoy, no solo porque de verdad se preocupen por la seguridad y privacidad de las comunicaciones de sus usuarios (no como otros), ni por la calidad de sus apps frente a las de sus competidores, sino porque entre muchas otras cosas, mantienen un ritmo muy alto de actualizaciones que hacen que cada poco tiempo tengamos disponibles nuevas versiones con nuevas y mejores funcionalidades.

En este artículo hablaré de una de sus últimas funcionalidades, concretamente de la API que la gente de Telegram ha liberado para su uso público, y que permite a cualquier programador crear bots para interactuar con otros usuarios a través de conversaciones de Telegram.

Y no solo a programadores, sino que gracias a algunas herramientas (como por ejemplo paquebot.io) cualquier persona con un mínimo de interés y ganas de trastear un poco puede hacerlo.

Pero esto es un blog técnico, no una web de noticias para lusers, así que déjate de asistentes, GUIs y demás mariconadas y vamos a programar...

  1. Qué es un bot?
  2. Introducción a la API de Telegram Bot
  3. Registro de un nuevo bot
  4. Formas de obtener los mensajes de Telegram
  5. Programación del bot
  6. Referencias

Qué es un bot?

Antes de explicar cómo hacerlos, hay que entender qué son exactamente los bots.

La Wikipedia define los bots como programas informáticos que imitan el comportamiento de un humano, es decir, como mi exnovia.

Por lo tanto, si estamos hablando de un bot para Telegram, en realidad estamos hablando de un programa con el que podríamos hablar del mismo modo que haríamos con cualquier persona, como si de una conversación normal entre personas se tratase, solo que uno de los interlocutores sería un programa informático.

Por poner un ejemplo práctico y fácilmente entendible incluso para vosotros, humanos, podríamos hacer un bot en PHP que estuviera en uno de nuestros servidores, y desde la app de Telegram podríamos abrir una conversación con él, enviándole comandos para ver (por ejemplo) el número de registros que hay en una tabla de una base de datos, la cantidad de espacio en disco libre que nos queda, o cualquier otra cosa que os imaginéis. Nosotros le preguntamos, y el nos contesta.

Telegram únicamente nos brinda (que no es poco) una serie de URL's (llamadas endpoints) a través de las cuales podemos enviar o recibir datos de una conversación de la app de Telegram.

Es decir, el funcionamiento del bot y lo que se pueda hacer con él depende únicamente de ti, Telegram solo hace de intermediario en la comunicación entre el usuario y el bot, encriptando toda la transmisión con su genial protocolo MTProto para que tu no tengas que preocuparte de la comunicación, sino del contenido de la misma.

La comunicación entre nuestro bot y la API de Telegram se hace a través del lenguaje de marcado JSON. Nosotros enviamos un JSON con los comandos y parámetros necesarios para lo que queramos hacer, y ellos nos devuelven otro JSON con el resultado.

Vista la teoría, vamos con un poco de práctica...

Introducción a la API de Telegram Bot

Como he comentado en el punto anterior, la API de Telegram Bot está formada por una serie de URL's (de ahora en adelante las llamaré endpoints) en el servidor de Telegram a través de las cuales podemos obtener y enviar datos utilizando JSON.

Éstos datos pueden ser mensajes de una conversación, información sobre el usuario que está hablando con el bot, una imagen enviada a través de Telegram, etc.

Todos éstos endpoints tienen que ser llamados a través de HTTPS por cuestiones obvias de seguridad, y en todas las llamadas debemos enviar el token de nuestro bot, es decir, el identificador que Telegram le haya asignado a nuestro bot... porque lo primero que hay que hacer para programar un bot en Telegram es decirle a Telegram que queremos programar un bot. En ese momento ellos nos asignarán un identificador único que les permitirá saber que es nuestro bot y no otro el que les está hablando (ya veremos más adelante cómo obtener éste token, no os preocupéis ahora por esto).

Para que todo esto os quede más claro, vamos con un ejemplo: Existe un endpoint llamado getMe que devuelve algunos datos de nuestro bot, y es el que se suele utilizar para comprobar que nuestro bot está registrado correctamente.

Las URL's de los endpoints de la API de Telegram están formadas por:

  • Protocolo a utilizar: https://
  • Servidor de la API de Telegram Bot: api.telegram.org/
  • La palabra bot seguida del token que nos hayan asignado al registrarlo: bot13724466:AAETkFQjiQGlLeJMchaZr2Kfjih3V3h6uua
  • El comando al que estemos llamando: getMe

Es decir, según nuestro ejemplo la URL a la que tendríamos que llamar tendría este aspecto: https://api.telegram.org/bot13724466:AAETkFQjiQGlLeJMchaZr2Kfjih3V3h6uua/getMe

Si hacemos una llamada a dicho endpoint, el servidor de Telegram nos devolverá un JSON con los datos de nuestro propio bot:

{
    "ok": true,
    "result": {
        "id": 13724466,
        "first_name": "pornoHARDWARE.com",
        "username": "PornoHardwareBot"
    }
}

Ahora que hemos visto más o menos como funciona, vamos a registrar nuestro bot para que nos asignen un token.

Registro de un nuevo bot

¿Qué mejor forma de iniciarse en la programación de un bot que usando uno de ellos para poder empezar a programar el nuestro? Eso es lo que debieron pensar los cachondos que hay detrás de Telegram, por lo que para poder registrar nuestros bots y que se nos asigne un token debemos utilizar un bot llamado Botfather.

Botfather es un bot desde el cual podemos solicitar el token para un nuevo bot, cambiar la descripción de un bot que ya hayamos registrado, cambiar el username de nuestro bot, su imagen de perfil, etc.

Solo tenemos que abrir nuestra app de Telegram desde el móvil o desde nuestro ordenador y comenzar una conversación con el usuario @bothfather:

Nada más empezar, nos indica la lista de comandos disponibles:

  • /newbot
  • /token
  • /revoke
  • /setname
  • /setdescription
  • /setabouttext
  • /setuserpic
  • etc...

Ya os habréis dado cuenta de que los comandos que se usan para hablar con los bots llevan la barra / delante, verdad?

En el caso de los comandos de Botfather, todos tienen un nombre bastante autodescriptivo, pero aún así explicaré algunos un poco para aquellos que vengáis de la LOGSE:

/newbot

Con éste comando le decimos al bot de Telegram que queremos registrar un nuevo bot. Botfather comenzará entonces una serie de preguntas para registrar nuestro bot, como por ejemplo el nombre común del bot, su nombre de usuario (nickname), etc. Para el nombre común del bot podemos utilizar lo que queramos, pero para el nombre de usuario hay que tener en cuenta que todos los bots de Telegram deben terminar en bot o en _bot.

Para nuestro ejemplo utilizaremos como nombre común pornoHARDWARE.com y como nombre de usuario PornoHardwareBot.

Cuando hayáis terminado de registrar el bot, Botfather os responderá con el token que os haya asignado, el cual debéis guardar para poder utilizar la API de Telegram Bot.

A partir de este punto, vuestro bot estará registrado y nadie salvo vosotros podrá utilizar el nombre de usuario que hayáis elegido (hay un máximo de 20 bots por persona (supongo que serán 20 bots por cada usuario de Telegram) por lo que si se os va la mano registrando bots (como me pasó a mi el día que salió éste tema xDD) botfather se encargará de avisaros de que ya no podéis registrar más (con una genial cita de la aún más genial película El Padrino, cuyo título original es Godfather, de ahí la parodia de Botfather):

No puedo hacer eso. Vienes a mi preguntando por más de 20 bots, pero no lo pides con respeto, no me ofreces tu amistad, ni siquiera me llamas botfather...

/token

Este comando genera un nuevo token para el bot que le indiquemos a Botfather. Obviamente solo podemos cambiar el token de aquellos bots que hayamos registrado nosotros.

/revoke

Invalida el token actual del bot que le indiquemos, de forma que el bot deje de tener acceso inmediatamente. Supongo que es por seguridad, por si alguna vez alguien nos roba el token y tenemos que generar uno nuevo.

/setname

Especifica el nombre común del usuario del bot que le indiquemos. Éste nombre es el que le aparecerá a los usuarios cuando, por ejemplo, nuestro bot les envíe una notificación push.

/setdescription

Por defecto, la primera vez que un usuario abre una conversación con nuestro bot, le aparecerá el mensaje que indiquemos aquí. Se usa como mensaje de bienvenida, así como para explicar brevemente a sus usuarios en qué consiste nuestro bot.

Es recomendable que nos tomemos un tiempo en establecer una descripción adecuada, con un texto claro y simple que explique a los usuarios qué pueden hacer con nuestro bot.

/setabouttext

Parecido al anterior, pero aquí indicamos un breve texto sobre el autor del bot (nosotros).

/setuserpic

Establece la imagen que le enviemos como icono de nuestro bot. La imagen debe ser lo suficientemente grande como para que al aumentarla de tamaño se siga viendo bien (dependiendo de donde se muestro, a veces Telegram redimensiona la imagen y si no tiene la resolución necesaria, ésta se verá muy pixelada).

Tiene que ser cuadrada, y recomiendo que tenga al menos unas dimensiones de 250px x 250px, aunque cuanto mayor sea, mejor (Telegram se encargará de reducir la imagen según sus necesidades).

/setcommands

Indica la lista de comandos que tendrá nuestro bot. Aunque no pongamos aquí todos los comandos que tengamos, éstos funcionarán igual. Supongo que ésta opción es solo para uso interno suyo (de Telegram), estadísticos o para mostrar la lista en alguna parte... pero vamos, que no hace falta que los indiquéis todos aquí para que funcionen.

/setjoingroups

Con este comando podemos establecer si nuestro bot puede participar en conversaciones de grupo o solo en conversaciones privadas con un usuario.

A no ser que tengamos una buena razón para hacerlo (por motivos de seguridad o privacidad), no veo porqué habríamos de limitar el funcionamiento de nuestro bot únicamente a conversaciones privadas.

/setprivacy

Este comando es importante, ya que establece algunas políticas de seguridad para nuestro bot.

Podemos activar o desactivar el modo privacidad, lo que provoca que nuestro bot NO recibirá ningún mensaje que le enviemos en aquellas conversaciones de grupo en las que esté, a excepción de:

  • Los mensajes que empiecen por /.
  • Los mensajes que mencionen al bot a través de @nombrebot.
  • Los mensajes que sean respuestas a los propios mensajes enviados por el bot, es decir, cuando respondamos a uno de los mensajes del bot.
  • Los mensajes de servicio, es decir, los mensajes que indican que fulanito ha sido añadido o eliminado del grupo, y cosas así.

De esta forma podemos añadir nuestro bot a una conversación de grupo donde hay más gente sin que le lleguen al bot todos los mensajes que se digan en dicha conversación a partir de ese momento.

Una vez que hayamos registrado nuestro bot, tengamos el token correspondiente y hayamos configurado la descripción, imagen, modo privacidad, etc. con los comandos anteriores, podemos empezar con la programación del bot... aunque os recomiendo que llegados a éste punto, abráis vuestro cliente de Telegram e intentéis abrir una conversación con vuestro recién registrado bot.

Solo tenéis que abrir una nueva conversación, y cuando os pida que seleccionéis el destinatario de dicha conversación, tecleáis el nombre de vuestro bot... y una vez os aparezca en la lista de contactos que coinciden con dicho nombre, lo seleccionáis y empezáis a hablar con el (todavía no os contestará nada, pero podéis probar a decirle /start o /help o cualquier otra cosa).

Formas de obtener los mensajes de Telegram

Para este ejemplo vamos a utilizar PHP (que por mucho que le joda a los talibanes de Python, es un lenguaje cojonudo... al igual que éste último, por supuesto) pero podríamos hacer nuestro bot con cualquier lenguaje de programación que pudiera hacer llamadas a una dirección HTTPS, aunque según cómo lo diseñemos, podría ser necesario poder recibirlas también... tener esto en cuenta, aunque ya lo explicaré un pelín más adelante.

Como la API que Telegram ha publicado está formada por un conjunto de endpoints accesibles por HTTPS, podríamos programar nuestro bot con código propio, sin depender de ninguna librería de terceros, tan solo realizando peticiones con cURL, fopen o cualquier otro wrapper HTTP, pero haremos un código mucho más limpio y mantenible si utilizamos alguna de las librerías de PHP para Telegram Bot de código libre que existen.

Hay muchas de éstas librerías, pero después de probar algunas de ellas, la que más me ha gustado ha sido TelegramBot/API: Native PHP Wrapper for Telegram Bot API.

No solo me ha parecido la más sencilla y completa que he visto, sino que yo mismo he colaborado con algunos pull requests con pequeñas nuevas funcionalidades y bugfixes (que espero que su autor haya aceptado antes de que termine éste artículo xDD).

Antes de empezar a programar el bot, debéis tener claro de qué forma vamos a obtener los mensajes de los usuarios que hablen con nuestro bot a través de Telegram.

Existen 2 formas de hacer esto, y ambas son distintas e incompatibles entre si:

  • getUpdates: Que seamos nosotros los que le pidamos a Telegram cada cierto tiempo que nos envíe los mensajes nuevos que haya desde la última vez que se los pedimos.
  • Webhook: Que sea Telegram quien haga una petición a nuestro bot cada vez que nos envíen un mensaje con el contenido del mismo.

Como podéis ver, en el primer caso (getUpdates)seríamos nosotros los que pediríamos a Telegram un listado de los mensajes que hubieran enviado los usuarios a nuestro bot, y en el segundo caso (Webhook) sería Telegram quien se conectaría a nuestro bot cada vez que recibiera un mensaje, enviándoselo casi en tiempo real.

Ambas formas pueden ser útiles según las necesidades de nuestro bot, y debemos pensar en ellas a la hora de diseñarlo, ya que o utilizamos una o utilizamos otra, pero no podemos usar las dos.

Para la mayoría de los casos, y a no ser que vayáis a tener un tráfico brutal de peticiones o tengáis problemas de concurrencia, creo que la mejor opción es la segunda, ya que no tendréis que llevar la cuenta de qué mensajes habéis procesado ya, sino que Telegram os irá enviando uno a uno cada nuevo mensaje que los usuarios escriban a vuestro bot casi en el mismo instante en el que los escriban (y e incluso si vuestro bot está caído, reintentarán el envío unas cuantas veces pasado unos segundos/minutos). Telegram llama a ésta forma de obtener los mensajes Webhook.

Sin embargo, si estimáis que el número de mensajes que vais a recibir podría ser ingente o podríais tener problemas si os llegaran varios mensajes al mismo tiempo (lo que provocaría varias instancias de vuestro bot levantadas al mismo tiempo), entonces es mejor que uséis la primera opción y seáis vosotros los que vayáis pidiendo a Telegram la actualización de mensajes de forma controlada por vosotros. Telegram llama a ésta forma de obtener los mensajes getUpdates, ya que es así como se llama el endpoint encargado de hacerlo.

Vamos a ver cómo sería el código en cada caso.

getUpdates

Como ya vimos anteriormente, los endpoints de la API de Telegram Bot están formados por URL + token + comando, y dado que el comando en éste caso se llama getUpdates, la URL a la que deberíamos acceder para que Telegram nos devuelva los mensajes recibidos por nuestro bot sería: https://api.telegram.org/bot13724466:AAETkFQjiQGlLeJMchaZr2Kfjih3V3h6uua/getUpdates

Opcionalmente, el endpoint getUpdates acepta 3 parámetros:

  • offset: ID del primer mensaje a devolver. Debe ser como mínimo uno más que el último ID de mensaje recibido. Es decir, si ahora pedimos el 150, el siguiente debe ser al menos el 151.
  • limit: Número de mensaje a devolver (puede ser un valor de 0 a 100).
  • timeout: Tiempo máximo de espera en segundos para esperar la respuesta del servidor de Telegram.

Si todo ha ido bien, al hacer ésta llamada obtendremos un JSON con los últimos mensajes recibidos por el bot (aquellos mensajes que le enviamos en el punto anterior al terminar de registrar el bot mediante botfather). En éste caso, deberías obtener un JSON parecido a éste:

getUpdates.jsondownload
{
  "ok": true,
  "result": [
    {
      "update_id": 546,
      "message": {
        "message_id": 638,
        "from": {
          "id": 353453,
          "first_name": "BhEaN",
          "username": "BhEaN"
        },
        "chat": {
          "id": 353453,
          "first_name": "BhEaN",
          "username": "BhEaN"
        },
        "date": 1439679501,
        "text": "\/start"
      }
    },
    {
      "update_id": 578,
      "message": {
        "message_id": 640,
        "from": {
          "id": 353453,
          "first_name": "BhEaN",
          "username": "BhEaN"
        },
        "chat": {
          "id": 353453,
          "first_name": "BhEaN",
          "username": "BhEaN"
        },
        "date": 1439679516,
        "text": "prueba de mensaje"
      }
    }
  ]
}

En éste JSON se puede ver 2 mensajes: uno con el id 546 y el otro con el 578 (es solo un ejemplo con datos inventados). Ambos mensajes fueron enviados por el usuario id 353453 con nombre BhEaN, y con los textos /start y prueba de mensaje respectivamente.

Pero como ya he dicho antes, si en lugar de hacer la petición HTTPS nosotros directamente lo hacemos a través de la API de PHP que os recomendé, el código PHP quedaría así de simple:

getUpdates.phpdownload
<?php

include('TelegramBot/Api/BotApi.php');
include('TelegramBot/Api/Exception.php');
include('TelegramBot/Api/InvalidArgumentException.php');
include('TelegramBot/Api/BaseType.php');
include('TelegramBot/Api/TypeInterface.php');
include('TelegramBot/Api/Message.php');
include('TelegramBot/Api/User.php');

// Inicializar bot con el token
define(TOKEN, '13724466:AAETkFQjiQGlLeJMchaZr2Kfjih3V3h6uua');
$bot = new \TelegramBot\Api\BotApi(TOKEN);

// Obtener actualizaciones de mensajes
try
    $aUpdates = $bot->getUpdates();
catch (\TelegramBot\Api\Exception $e)
    echo 'ERROR!: ' . $e->getMessage());
catch (\TelegramBot\Api\InvalidArgumentException $e)
    echo 'ERROR!: ' . $e->getMessage());

// Recorrer resultados
foreach ($aUpdates as $aUpdate) {

    $oMessage = $aUpdate['message'];

    // Aquí ya podemos acceder a los datos de cada mensaje devuelto
    echo 'ID del mensaje: ' . $oMessage->getMessageId();
    echo 'Contenido del mensaje: ' . $oMessage->getText();

}

Una vez que hayamos obtenido y procesado los mensajes según las necesidades de nuestro bot, haremos las siguientes llamadas a getUpdates indicando como parámetro offset el último MessageId que hayamos obtenido + 1.

Webhook

A diferencia del método getUpdates, cuando utilizamos Webhook no hace falta llevar la cuenta de qué mensajes llevamos procesados para ir solicitando solo los que no, ya que será Telegram quien nos vaya enviado cada nuevo mensaje de forma individual.

Solo tenemos que decirle a Telegram la URL de nuestro bot, y ellos se encargarán de hacer una petición a dicha URL por cada mensaje que los usuarios envíen a nuestro bot... pero ojo! hay algo MUY importante que hay que tener en cuenta a la hora de utilizar Webhook, y es que de la misma forma que cuando nosotros hacemos una llamada a la API de Telegram debemos hacerlo utilizando HTTPS, Telegram también utilizará HTTPS cuando haga una llamada a nuestro bot, lo que significa que éste deberá estar alojado en un servidor que utilice SSL. Y es lógico... porque para qué se iba a molestar Telegram en encriptar los mensajes con su súper algoritmo MProto, proteger la privacidad de sus usuarios, implementar mecanismos de seguridad en los bots públicos, etc. si luego nosotros vamos a enviar todas las comunicaciones de nuestros bots a través de protocolos inseguros?.

Lo malo es que no solo nuestro bot debe estar alojado en un servidor que utilice certificados SSL, sino que dichos certificados deben ser certificados válidos: NO sirven certificados SSL autofirmados por nosotros, caducados o emitidos a un dominio distinto del nuestro, así que si no tenemos un certificado SSL válido nos tocará pasar por caja y comprar uno.

Pero no os preocupéis, comprar un certificado SSL ya no significa tener que soltar varios cientos de euros al año, sino que gracias a los Dioses existen sitios donde poder comprar certificados SSL de bajo coste (pero perfectamente válidos) por unos 5 € al año, como por ejemplo el certificado SSL positive de Comodo en DonDominio.com (que es el mismo que utilizo en pornoHARDWARE.com y en muchas otras de mis webs).

Ahora que éste punto ha quedado claro, vamos a decirle a Telegram en qué URL está nuestro bot para que nos envíe los mensajes.

Para eso existe un método en la API de Telegram Bot llamado setWebhook, el cual tiene un único parámetro, que es precisamente la URL a la que debe llamar Telegram cuando un usuario envíe un mensaje a nuestro bot. Si nuestro bot estuviera alojado en la URL https://nuestroserver.com/telegrambot.php (por ejemplo), la URL de la API de Telegram a la que tendríamos que llamar podría ser algo parecido a: https://api.telegram.org/bot13724466:AAETkFQjiQGlLeJMchaZr2Kfjih3V3h6uua/setWebhook?url=https://nuestroserver.com/telegrambot.php

Si accedemos a esa URL y Telegram registra nuestro comando correctamente, deberíamos obtener un JSON similar a éste:

setWebhook_ok.jsondownload
{
  "ok": true,
  "result": true,
  "description": "Webhook was set"
}

A partir de éste momento, Telegram enviará a la URL que le hayamos especificado cada uno de los mensajes que los usuarios envíen a nuestro bot. Y también a partir de éste momento ya no podremos usar el método getUpdates (como ya os dije, o getUpdates o Webhook, no ambos).

Si intentamos llamar al método getUpdates habiendo activado el Webhook obtendremos un JSON de error como éste:

setWebhook_ko.jsondownload
{
  "ok": false,
  "error_code": 409,
  "description": "Error: Conflict: another webhook is active"
}

Si en algún momento quisiéramos cancelar el Webhook (porque queremos volver a utilizar getUpdates o por cualquier otro motivo) solo tenemos que volver a hacer la misma llamada a la API de Telegram que hicimos para registrarlo, pero ésta vez sin especificar ninguna URL.

En nuestro caso sería algo así como: https://api.telegram.org/bot13724466:AAETkFQjiQGlLeJMchaZr2Kfjih3V3h6uua/setWebhook

Lo que nos devolvería un JSON parecido a éste:

removeWebhook_ok.jsondownload
{
  "ok": true,
  "result": true,
  "description": "Webhook was deleted"
}

Si en lugar de hacer las llamadas a éstas URL's directamente utilizamos desde código PHP la API de Telegram que os he recomendado, registrar el Webhook con código PHP sería algo así:

telegrambot_register_webhook.phpdownload
<?php

include('TelegramBot/Api/BotApi.php');
include('TelegramBot/Api/Exception.php');
include('TelegramBot/Api/InvalidArgumentException.php');
include('TelegramBot/Api/BaseType.php');

// Inicializar bot con el token
define(TOKEN, '13724466:AAETkFQjiQGlLeJMchaZr2Kfjih3V3h6uua');
$bot = new \TelegramBot\Api\BotApi(TOKEN);

// Activar Webhook
define(URL_BOT, 'https://nuestroserver.com/telegrambot.php');
$result = $bot->setWebhook(URL_BOT);

if ($result)
        echo 'Webhook registrado correctamente';
else
        echo 'Error al registrar el Webhook';

No tiene misterio, verdad? Utilizar la API de Telegram es bastante sencillo e intuitivo, y aún más si lo hacemos a través de la librería de PHP.

No parece que sea muy buena idea dejar que cualquiera pueda acceder a nuestro bot directamente (es decir, que cualquiera pueda hacer una llamada HTTPS directamente hasta nuestro bot en lugar de ser Telegram quien la haga), por lo que una de las cosas que recomiendan hacer es utilizar como nombre de nuestro bot el propio token, ya que se supone que es algo que nadie excepto Telegram y nosotros mismos conoce. Me refiero a que en lugar de llamar al archivo PHP que contiene el código de nuestro bot telegrambot.php lo llamemos AAETkFQjiQGlLeJMchaZr2Kfjih3V3h6uua.php, por ejemplo.

Ahora que ya habéis visto cómo se usa, lo suyo es que veamos un ejemplo de código que reciba los mensajes de Telegram y haga una cosa u otra en función de qué mensaje (comando) sea el que nos envían.

Programación del bot

Básicamente voy a hacer un programa en PHP utilizando la librería de acceso a la API de Telegram Bot que admita una serie de comandos y responda al usuario una cosa u otra según nos haya enviado un comando u otro.

Para no complicar el tema, los comandos harán cosas muy sencillas, como por ejemplo mostrar la fecha o la hora.

La forma de recibir los mensajes de Telegram será a través del método Webhook, de forma que no tendremos que preocuparnos por qué mensajes hemos procesado ya, sino que Telegram nos los irá entregando uno a uno en cada petición.

Doy por hecho que si estáis leyendo éste artículo sabéis programar en PHP, por lo que espero que el código sea lo suficientemente autoexplicativo para que incluso vosotros, simples humanos, lo entendáis.

El funcionamiento del programa será más o menos éste:

  • Se lee la entrada de datos (que será el JSON que Telegram nos enviará con cada mensaje) usando el wrapper de entrada de PHP php://input.
  • Mediante una expresión regular se identifica qué comando (mensaje) nos ha escrito el usuario.
  • Preparamos una respuesta u otra mediante un switch case, dependiendo de la funcionalidad que queramos dar con nuestro bot.
  • Si el comando es /fecha, respondemos al usuario con la fecha actual.
  • Si el comando es /hora, respondemos al usuario con la hora actual.
  • Si todo ha ido bien, respondemos al usuario con el texto obtenido en el paso anterior.

Aparte de /fecha y /hora, que son los 2 comandos cuya funcionalidad vamos a implementar, también permitiremos los comandos /start y /help, ya que Telegram por defecto ejecuta el comando /start la primera vez que empezamos a hablar con el bot (por lo que lo usaremos para dar la bienvenida al usuario), y también es recomendable tener un /help para que el usuario pueda consultar la lista de comandos que permitimos en el bot.

El código del bot podría ser algo así:

telegrambot_example01.phpdownload
<?php

openlog('TelegramBot', LOG_PID | LOG_PERROR, LOG_LOCAL0);

include('TelegramBot/Api/BotApi.php');
include('TelegramBot/Api/Exception.php');
include('TelegramBot/Api/InvalidArgumentException.php');
include('TelegramBot/Api/BaseType.php');
include('TelegramBot/Api/TypeInterface.php');
include('TelegramBot/Api/Types/ArrayOfArrayOfPhotoSize.php');
include('TelegramBot/Api/Types/ArrayOfPhotoSize.php');
include('TelegramBot/Api/Types/Audio.php');
include('TelegramBot/Api/Types/Chat.php');
include('TelegramBot/Api/Types/Contact.php');
include('TelegramBot/Api/Types/Document.php');
include('TelegramBot/Api/Types/ForceReply.php');
include('TelegramBot/Api/Types/GroupChat.php');
include('TelegramBot/Api/Types/Location.php');
include('TelegramBot/Api/Types/Message.php');
include('TelegramBot/Api/Types/PhotoSize.php');
include('TelegramBot/Api/Types/ReplyKeyboardHide.php');
include('TelegramBot/Api/Types/ReplyKeyboardMarkup.php');
include('TelegramBot/Api/Types/Sticker.php');
include('TelegramBot/Api/Types/User.php');
include('TelegramBot/Api/Types/UserProfilePhotos.php');
include('TelegramBot/Api/Types/Video.php');

syslog(LOG_DEBUG, '[' . getmypid() . '] - Peticion WebHook recibida desde ' . $_SERVER['REMOTE_ADDR']);

// Inicializar bot con el token
define(TOKEN, '13724466:AAETkFQjiQGlLeJMchaZr2Kfjih3V3h6uua');
$bot = new \TelegramBot\Api\BotApi(TOKEN);

$returnArray = true;
$rawData = file_get_contents('php://input');
$response = json_decode($rawData, $returnArray);

if (empty($rawData)) {
    syslog(LOG_ERR, '[' . getmypid() . '] Peticion incorrecta: no hay datos');
    exit(1);
}

try {
    $oMessage = \TelegramBot\Api\Types\Message::fromResponse($response['message']);
} catch (\TelegramBot\Api\InvalidArgumentException $e) {
    syslog(LOG_ERR, '[' . getmypid() . '] Error obteniendo mensajes (argumento invalido): ' . $e->getMessage() . ' - Rawdata: ' . $rawData);
}

$oUserFrom = $oMessage->getFrom();
$oChat = $oMessage->getChat();

$msg = 'Comando recibido [' . $oMessage->getText() . '] del usuario [' . $oUserFrom->getFirstName() . '] el [' . date('Y/m/d H:i:s', $oMessage->getDate()) . ']';
syslog(LOG_DEBUG, '[' . getmypid() . '] ' . $msg);

// Obtener comando (y sus posibles parametros)
$regExp = '#^(\/[a-zA-Z0-9\/]+?)(\ .*?)$#i';
$tmp = preg_match($regExp, $oMessage->getText(), $aResults);
if (isset($aResults[1])) {
    $cmd = trim($aResults[1]);
    $cmd_params = trim($aResults[2]);
} else {
    $cmd = trim($oMessage->getText());
    $cmd_params = '';
}

// Reply message data
$msg = array();
$msg['chat_id'] = $oChat->getId();
$msg['text'] = null;
$msg['disable_web_page_preview'] = true;
$msg['reply_to_message_id'] = $oMessage->getMessageId();
$msg['reply_markup'] = null;

// Comandos
switch ($cmd) {
    case '/start':
        $msg['text']  = 'Hola ' . $oUserFrom->getFirstName() . '!' . PHP_EOL;
        $msg['text'] .= '¿Qué puedo hacer por ti? Puedes ver una lista de las opciones disponibles con el comando /help';
        $msg['reply_to_message_id'] = null;
        break;

    case '/help':
        $msg['text']  = 'Los comandos disponibles son:' . PHP_EOL;
        $msg['text'] .= '/start Inicializa el bot';
        $msg['text'] .= '/fecha Muestra la fecha actual';
        $msg['text'] .= '/hora Muestra la hora actual';
        $msg['text'] .= '/help Muestra esta ayuda';
        $msg['reply_to_message_id'] = null;
        break;

    case '/fecha':
        $msg['text']  = 'La fecha actual es ' . date('d/m/Y');
        break;

    case '/hora':
        $msg['text']  = 'La hora actual es ' . date('H:i:s');
	break;

    default:
        $msg['text']  = 'Lo siento ' . $oUserFrom->getFirstName() . ', pero [' . $cmd . '] no es un comando válido.' . PHP_EOL;
        $msg['text'] .= 'Prueba /help para ver la lista de comandos disponibles';
        break;
}

try {
    syslog(LOG_DEBUG, '[' . getmypid() . '] Enviando mensaje de respuesta (' . strlen($msg['text']) . ' bytes) al usuario');
    $bot->sendMessage($msg['chat_id'], $msg['text'], $msg['disable_web_page_preview'], $msg['reply_to_message_id'], $msg['reply_markup']);
} catch (\TelegramBot\Api\Exception $e) {
    syslog(LOG_ERR, '[' . getmypid() . '] ERROR Exception al enviar el mensaje: ' . $e->getMessage());
    exit(1);
} catch (\TelegramBot\Api\InvalidArgumentException $e) {
    syslog(LOG_ERR, '[' . getmypid() . '] ERROR InvalidArgumentException al enviar el mensaje: ' . $e->getMessage());
    exit(1);
}

syslog(LOG_DEBUG, '[' . getmypid() . '] > Ejecucion del bot terminada correctamente!');

exit(0);

Os recomiendo que leáis detenidamente la documentación de la API de Telegram Bot para conocer en profundidad los métodos disponibles, así como los parámetros que podéis usar con ellos, los tipos de mensajes que puede enviaros los usuarios (textos, imágenes, audio, localizaciones, etc) y todas las demás cosas que Telegram os ofrece para programar bots.

Una vez que pongáis éste código en vuestro servidor y hayáis registrado el Webhook correspondiente (como vimos en el paso anterior), ya podréis empezar a hablar con el bot desde vuestro teléfono, y veréis lo divertido que puede llegar a ser hablar con uno de vuestros propios programas (y créeme, si estas leyendo éste blog a éstas horas, probablemente esa sea la conversación más larga que vayas a mantener en tu vida sin que tu interlocutor acabe pensando que estas mal de la cabeza, pedazo de friki! xDDD).

Ah! Casi se me olvida: puedes utilizar tu propio sistema de estadísticas para hacer el seguimiento y monitorización del uso de tu bot, o puedes utilizar alguno de los sistemas que ya estan apareciendo específicamente diseñados para este tipo de bots, como por ejemplo Botan.io (basado en el proveedor de estadísticas Yandex.AppMetrika).

Espero que os haya resultado útil el artículo, y que os sirva al menos como base para empezar a programar bots en Telegram, ya que cuando yo empecé a hacerlo apenas había ninguna referencia, ejemplo o documentación alternativa a la oficial, que aunque es cojonuda, a veces (sobre todo al principio, cuando aún no tienes claro el funcionamiento de los bots) puede resultar insuficiente.

Alaaaaaaaaaaaaaaaaaaaaaaa

Referencias