Bot de Telegram

Agregar globo de texto a imagen con bot de Telegram

Hoy vamos a ver cómo agregar un globo o bocadillo a cualquier imagen con un bot de Telegram usando Node y JavaScript para dicha tarea.

Una cosa será el bot de Telegram y otra el procesamiento de la imagen. Podemos usar ambas cosas por separado y mejorar cada una de ellas o combinarlas.

Bot de Telegram – Agregar globo de texto o bocadillo a imagen para memes

Técnicamente hablando vamos a escuchar si el usuario envía una imagen en Telegram (ya sea en grupos o en privado) y una vez que la envíe veremos el texto que la acompaña.

En caso de que el texto coincida con la orden vamos a combinar las 2 imágenes, redimensionando el globo o bocadillo según sea el caso y después vamos a responder con esa imagen ya modificada.

Nota: esto fue hecho por diversión para hacer memes y comentar rápidamente en Facebook o similares, aunque se pueden sacar varias cosas de aquí.

Requisitos

Vamos a usar Node y los paquetes dotenv (variables del entorno), node-telegram-bot-api para el bot de Telegram y también sharp para la modificación de imágenes.

También vas a necesitar tu token de bot para Telegram en caso de que quieras probar el proyecto.

Una vez que lo tengas necesitas crear el archivo .env según el archivo .env.example y colocar tu token ahí.

En cuanto a los paquetes ya van en el package.json solo tienes que hacer un npm install.

Escuchando petición de usuario

Vamos a usar bot.on para escuchar cuando el usuario envíe una imagen. Si lo hace vamos a ver el caption que la acompaña para ver si nos está pidiendo que le agreguemos un globo de texto:

bot.on('photo', async (msg) => {
 const caption = msg.caption;
 if (!caption || !msg.photo || msg.photo.length <= 0) {
  return;
 }
 const chatId = msg.chat.id;
 const opciones = [
  "hazle un globo de texto",
  "hacer un globo de texto",
  "haga un globo de texto",
  "poner un globo de texto",
  "ponga un globo de texto",
 ];
 for (const opcion of opciones) {
  if (caption.toLowerCase().includes(opcion)) {
   // Aquí ponemos el globo de texto
  }
 }
});

Si el caption contiene (ya sea al inicio, centro o final) alguna de las opciones de la línea 7 entonces vamos a agregar el bocadillo a la imagen.

Agregar globo de texto a imagen con Node y sharp

A continuación veamos la función que permitirá agregar el speech bubble a la imagen usando JavaScript del lado del servidor y un Telegram bot.

const agregarGloboDeTextoYDevolverBuffer = async (nombreEntrada, porcentaje) => {
 const entrada = sharp(nombreEntrada);
 const informacionEntrada = await entrada.metadata();
 const ALTURA_GLOBO = parseInt((porcentaje * informacionEntrada.height) / 100);
 const globo = sharp(NOMBRE_IMAGEN_GLOBO)
  .resize({ width: informacionEntrada.width, height: ALTURA_GLOBO, fit: "fill" })
 return entrada
  .extend({ top: ALTURA_GLOBO, background: { r: 255, g: 255, b: 255, } })
  .composite([{ input: await globo.toBuffer(), left: 0, top: 0 }])
  .toBuffer();
}

En la línea 5 cargamos el globo de texto (o sea, la imagen que vamos a poner encima de la original) y lo redimensionamos para que sea del mismo largo que la imagen.

Luego extendemos la imagen original rellenándola de blanco y con composite combinamos ambas imágenes colocando el globo en la esquina superior izquierda, es decir, en left y top 0.

Regresamos un búfer de la imagen ya que la librería así lo permite. De este modo no necesitamos guardarla de manera temporal en el almacenamiento o cosas similares.

Enviando imagen de vuelta al chat

Ahora ya tenemos la imagen modificada como un búfer y ya sabemos que el usuario solicitó la modificación, queda enviársela usando el chatId con la función bot.sendPhoto:

const mejorFoto = msg.photo[msg.photo.length - 1]; // al momento  de escribir esto, la foto con mejor calidad estaba en la última posición
const nombreFotoDescargada = await bot.downloadFile(mejorFoto.file_id, "./");
const fotoConGloboDeTexto = await agregarGloboDeTextoYDevolverBuffer(nombreFotoDescargada, 15);
fs.unlink(nombreFotoDescargada, () => {
  bot.sendPhoto(chatId, fotoConGloboDeTexto);
});

Por cierto, en este caso estoy descargando la imagen con bot.downloadFile ya que no encontré la manera de obtenerla como búfer, así que en este caso sí estoy creando una imagen temporal pero la elimino después con fs.unlink.

Poniendo todo junto

El código completo queda como se ve a continuación, pero si lo quieres junto con el package.json y actualizado te recomiendo que vayas a GitHub ya que si hago actualizaciones las haré ahí:

const sharp = require('sharp')
const fs = require("fs");
require("dotenv").config();
const NOMBRE_IMAGEN_GLOBO = "globo.png";

// Función de recuerdo. No sirve para nada pero es la primera que escribí para probar esta cosa
const agregarGloboDeTexto = async (porcentaje) => {
 const entrada = sharp("./esther.jpeg");
 const informacionEntrada = await entrada.metadata();
 const ALTURA_GLOBO = parseInt((porcentaje * informacionEntrada.height) / 100);
 const globo = sharp("./globo.png")
  .resize({ width: informacionEntrada.width, height: ALTURA_GLOBO, fit: "fill" })
 entrada
  .extend({ top: ALTURA_GLOBO, background: { r: 255, g: 255, b: 255, } })
  .composite([{ input: await globo.toBuffer(), left: 0, top: 0 }])
  .toFile('./kangta.new.jpg', function (err) {
   if (err) console.log(err);
  })
}

const agregarGloboDeTextoYDevolverBuffer = async (nombreEntrada, porcentaje) => {
 const entrada = sharp(nombreEntrada);
 const informacionEntrada = await entrada.metadata();
 const ALTURA_GLOBO = parseInt((porcentaje * informacionEntrada.height) / 100);
 const globo = sharp(NOMBRE_IMAGEN_GLOBO)
  .resize({ width: informacionEntrada.width, height: ALTURA_GLOBO, fit: "fill" })
 return entrada
  .extend({ top: ALTURA_GLOBO, background: { r: 255, g: 255, b: 255, } })
  .composite([{ input: await globo.toBuffer(), left: 0, top: 0 }])
  .toBuffer();
}
const TelegramBot = require('node-telegram-bot-api');

const token = process.env.TELEGRAM_BOT_TOKEN;

const bot = new TelegramBot(token, { polling: true });

bot.on('photo', async (msg) => {
 const caption = msg.caption;
 if (!caption || !msg.photo || msg.photo.length <= 0) {
  return;
 }
 const chatId = msg.chat.id;
 const opciones = [
  "hazle un globo de texto",
  "hacer un globo de texto",
  "haga un globo de texto",
  "poner un globo de texto",
  "ponga un globo de texto",
 ];
 for (const opcion of opciones) {
  if (caption.toLowerCase().includes(opcion)) {
   const mejorFoto = msg.photo[msg.photo.length - 1]; // al momento  de escribir esto, la foto con mejor calidad estaba en la última posición
   const nombreFotoDescargada = await bot.downloadFile(mejorFoto.file_id, "./");
   const fotoConGloboDeTexto = await agregarGloboDeTextoYDevolverBuffer(nombreFotoDescargada, 15);
   fs.unlink(nombreFotoDescargada, () => {
    bot.sendPhoto(chatId, fotoConGloboDeTexto);
   });
  }
 }
});

Recuerda que se ejecuta con node index.js y puedes correrlo incluso localmente en Windows aunque también se puede usar en un servidor.

Aquí te dejo con más bots de Telegram que he creado.

Estoy aquí para ayudarte 🤝💻


Estoy aquí para ayudarte en todo lo que necesites. Si requieres alguna modificación en lo presentado en este post, deseas asistencia con tu tarea, proyecto o precisas desarrollar un software a medida, no dudes en contactarme. Estoy comprometido a brindarte el apoyo necesario para que logres tus objetivos. Mi correo es parzibyte(arroba)gmail.com, estoy como@parzibyte en Telegram o en mi página de contacto

No te pierdas ninguno de mis posts 🚀🔔

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.
parzibyte

Programador freelancer listo para trabajar contigo. Aplicaciones web, móviles y de escritorio. PHP, Java, Go, Python, JavaScript, Kotlin y más :) https://parzibyte.me/blog/software-creado-por-parzibyte/

Entradas recientes

Creador de credenciales web – Aplicación gratuita

Hoy te voy a presentar un creador de credenciales que acabo de programar y que…

1 semana hace

Desplegar PWA creada con Vue 3, Vite y SQLite3 en Apache

Ya te enseñé cómo convertir una aplicación web de Vue 3 en una PWA. Al…

2 semanas hace

Arquitectura para wasm con Go, Vue 3, Pinia y Vite

En este artículo voy a documentar la arquitectura que yo utilizo al trabajar con WebAssembly…

2 semanas hace

Vue 3 y Vite: crear PWA (Progressive Web App)

En un artículo anterior te enseñé a crear un PWA. Al final, cualquier aplicación que…

2 semanas hace

Errores de Comlink y algunas soluciones

Al usar Comlink para trabajar con los workers usando JavaScript me han aparecido algunos errores…

2 semanas hace

Esperar promesa para inicializar Store de Pinia con Vue 3

En este artículo te voy a enseñar cómo usar un "top level await" esperando a…

2 semanas hace

Esta web usa cookies.