Estos días he visto que han surgido muchos medios informativos distintos para dar información del COVID-19: programas especiales en medios de comunicación, redes sociales, aplicaciones de mensajería, plubicidad… Hace un tiempo estuve «trasteando» con los bots de Telegram y ahora que tengo un poco de tiempo me animé a hacer uno simple que nos devolviera la información relevante sobre las estadísticas del virus y así ver como de sencillo es este proceso.

Componentes de la aplicación:

  • Java JDK 8 en adelante
  • Spring Boot 2.2.5
  • Librerías externas:
    • Jsoup. Para scrapear la información del COVID-19.
    • TelegramBots. Librería de acceso a la API de Bots de Telegram. (La usaremos a través de su propio starter para spring-boot)
    • Spring Caché. La usaremos a partir de su starter spring-boot-starter-cache.

Resumen:

Tendremos una aplicación java simple, que por un lado conectará con una base externa para recuperar información oficial del COVID-19, scrapeará la información necesaria y la almacenará internamente.

Esta información se actualizará cada media hora y mientras permanecerá cacheada ya que no cambiará.

Por otro lado, usaremos la API de Telegram a través de su librería más popular (TelegramBots) para crear un listener de nuestro bot que nos permita leer peticiones del mismo y responder con la información actualizada.

Lo vemos con un caso de uso de manera general:

Manolo, accede a su Telegram y solicita información al bot mediante uno de los comandos disponibles. Este comando llegará a la aplicación JAVA que mirará en sus datos de caché si tiene las estadísticas del COVID-19, en caso de tenerlos se lo devolverá a telegram que se encargará de enviarselos al usuario que lo pidió. En caso de no tener esos datos en caché, iremos a una fuente de datos de información, extraeremos esa información y la guardaremos en nuestra caché. Luego vaciaremos la caché cada media hora para que la información se actualice regularmente.

Tenéis el código fuente aquí: https://github.com/juanla/covid19-telegram-bot

Y la demo (mientras esté la pandemia activa): https://telegram.me/covid19_es_info_bot

Requisitos. Dar de alta el BOT de Telegram

Hablaremos con el BOT padre de Telegram (@BotFather) para crear nuestro BOT y que nos de nuestro TOKEN. El BOT padre de Telegram nos va a pedir el nombre del mismo y el nombre-friendly para telegram (username). Una vez proporcionados esos datos tendremos nuestro Token.

Con esto ya podemos ir y hablar con nuestro BOT, tambíen Botfather nos permitirá realizar configuraciones sobre el BOT (acceso a grupos, privacidad, Nombre, descripción, imagen de perfil

Genial, pues con este simple paso ya tendríamos todo listo para comenzar este pequeño desarrollo.

Configuración de la aplicación

Antes de comenzar, configuramos nuestro Boot Service de manera que quedará configurado el módulo de caché y scheduling, que nos permitirán cachear la información y limpiarla de la caché cada cierto periodo de tiempo.

@EnableCaching
@EnableScheduling
@SpringBootApplication
public class Covid19TgBotApplication {
	public static void main(String[] args) {
		ApiContextInitializer.init();
		SpringApplication.run(Covid19TgBotApplication.class, args);
	}
}

Formato de nuestra información

La información la almacenaremos en un objeto Java con la siguiente estructura COVIDInfo.java:

  • Hora de actualización
  • Total casos confirmados (A nivel mundial)
  • Total casos de fallecimientos (A nivel mundial)
  • Total casos de recuperados (A nivel mundial)
  • Listado de información por pais (Contendra Nombre del país, contagiados, fallecidos y recuperados)

Además de estas propiedades yo he añadido 2 métodos más para devolver la información formateada, una para el resumen con la información global, que devolverá un Listado de String (cada String corresponde con un mensaje por si toda la información global excediera el máximo permitido por telegram) y un resumen con la información en España.

Servicio de Obtención de información

Este servicio, tendrá 2 funciones:

Obtener la información de la fuente oficial y almacenarla en caché

A través de la librería JSoup conectaremos a la fuente de donde extraeremos la información, y tras parsearla la convertimos en un objeto COVIDInfo.java. Este método además quedará marcado como cacheable y asociado a la caché covidInfo.

@Cacheable("covidInfo")
@Override
public COVIDInfo getUpdatedInfo() {
	LOGGER.info("Actualizamos la información del COVID ya que no hay nada en caché");
	try {
		final Document doc = Jsoup.connect(covidInfoSource).get();
		return this.scrapeWebPage(doc);
	} catch (IOException e) {
		LOGGER.error("Error recuperando la información", e);
	}
	return null;		
}

Vaciar la caché cada media hora.

Para ir limpiando la caché cada cierto tiempo, en este caso 30 minutos, crearemos otro método y lo marcaremos con @Schedule usando la propiedad fixedRate e indicandole el tiempo cada cuanto queremos que se ejecute en milisegundos. Este método simplemente eliminará el contenido de la caché covidInfo.

Tenemos que asegurarnos haber inyectado el CacheManager previamente en la clase.

@Autowired
private CacheManager cacheManager;

@Scheduled(fixedRate = 1_800_000)
public void cleanCovidCache() {
    cacheManager.getCache("covidInfo").invalidate();
}

Con estos simples métodos tenemos lista la mayor parte de la lógica de nuestra aplicación. Ya sólo queda terminar la parte del BOT de Telegram.

Codificación BOT Telegram

Como vimos al comienzo, usaremos la librería TelegramBots que nos facilitará la integración del listener del BOT para Telegram. Para ello partiremos de crear nuestro Bot extendiendo de la clase org.telegram.telegrambots.bots.TelegramLongPollingBot para usar longpolling a la hora de obtener nuestras respuestas.

Con Long polling, cuando el cliente nos solicite información el servidor acumulará estas peticiones de manera de que podrá servirlas cuando tenga esa información, evitando responder directamente con una respuesta vacía o indicando que aún no tiene datos. Telegram nos permite crear y configurar colas de este tipo aunque nosotros usaremos los valores por defecto.

Nuestra clase Bot, nos va a solicitar que implementemos los siguientes métodos:

  • public void onUpdateReceived(Update update)
  • public String getBotUsername()
  • public String getBotToken()

De estos 3 el método que tiene la «chicha» es el primero, los otros 2 métodos simplemente devolverán el nombre de usuario y el token del propio BOT.

Nuestro método onUpdateReceived será invocado por telegram cuando haya alguna actualización (Update) que pueda interesarle a nuestro BOT (a modo de Listener). Estas Updates contienen toda la información que necesitarás sobre el tipo de acción que se ha producido donde está tu BOT: Edición de un mensaje, encuestas, nuevo mensaje, etc..

La lógica que implementaremos en nuestro onUpdateReceived quedará como la siguiente:

  • Obtendremos la información del COVID-19 (Ya sea de caché o de la web oficial, en esta parte nos abstraemos de eso)
  • Comprobamos que el update tenga un mensaje.
  • Obtenemos la respuesta asociada a ese comando (cuando digo comando, me refiero a la cadena de texto que ha introducido el usuario en el chat con el BOT, no tiene que ser un comando de telegram necesariamente)
  • Devolvemos la respuesta.
@Override
	public void onUpdateReceived(Update update) {
		LOGGER.info("Se ha recibido una update");
		
		final COVIDInfo covidInfo = infoService.getUpdatedInfo();
		
		if (update.hasMessage() && update.getMessage().hasText()) {
			
	        final Long chatId = update.getMessage().getChatId();
	        final String command = update.getMessage().getText();
	        
	        //Obtenemos la respuesta asociada al comando
	        final List<String> responseList = this.getResponse(command, covidInfo);	
	        for(String response : responseList) {
	        	final SendMessage message = new SendMessage() 
		                .setChatId(chatId)
		                .setParseMode("HTML")
		                .setText(response);
		        try {
		            execute(message); //Devolvemos mensaje al usuario
		        } catch (TelegramApiException e) {
		            LOGGER.error("Hubo un problema al formar la respuesta", e);
		        }
	        }
	    }
	}

Yo en este caso, tengo un enumerado con el listado de comandos, para luego mediante un switch elegir la respuesta adecuada (incluida la de no tiendo tu comando). Habrás podido observar que el método getResponse que nos hemos creado devuelve un listado de String. Esto se debe a que Telegram tiene un máximo de 4096 caracteres por mensaje (A día de hoy). En mis pruebas al sacar estadísticas lo sobrepasaba por lo que tuve que trocearlo y devolver un listado de «trozos» que acaban devolviendo varios mensajes en caso de ser necesario.

Esta parte es muy individual según la actividad del bot por lo que no comentaré el código (Arriba está el enlace a github). Aún así si tenéis alguna pregunta os contestaré en los comentarios.

Una vez finalizado, ya podemos compilar, desplegar e ir al chat con el bot y hablarle. Comenzamos por un simple /start obligatorio pero será capaz de contestar a todo lo que vayamos configurando.

Bot Desplegado

Os dejo unas capturas del bot en funcionamiento

Es un tipo de aplicación muy interesante, que además de informativa se le pueden dar usos automáticos de atención al cliente y comerciales que posiblemente en un futuro den mucho que hablar.

Cómo hacer un BOT de Telegram con Java para informar del COVID-19

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Social media & sharing icons powered by UltimatelySocial