9/8/2025 | Entrada nº 179 | Dentro de Herramientas

¿Cómo crear una RSS a partir de un canal de Telegram?

Crea en tu servidor web una RSS de tu canal Telegram para facilitar su uso en tus propias webs y el seguimiento por tus lectores más allá de Telegram.

Creamos una carpeta en un lugar accesible por URL, en nuestro ejemplo en la correspondiente a tudominio.com/feed

Iniciamos el proyecto:

npm init -y

Cargamos dependencias:

npm install node-telegram-bot-api rss

Creamos index.js, el programa que va a crear rss.xml en tudominio.com/feed/rss.xml

// index.js (Versión Mejorada)

// 1. Importamos las librerías necesarias
const TelegramBot = require('node-telegram-bot-api');
const RSS = require('rss');
const fs = require('fs');
const path = require('path');

// 2. Configuración del Bot y del Canal
const TOKEN = process.env.TELEGRAM_TOKEN || 'TOKEN DE TU ROBOT EN EL CANAL';
const CHANNEL_ID = process.env.TELEGRAM_CHANNEL_ID || '@TUCANAL';

// 3. Configuración del Feed RSS
const feed = new RSS({
  title: 'Feed del canal xx en Telegram',
  description: 'Últimas publicaciones del canal XX con enlaces.',
  feed_url: 'http://TUDOMINIO.COM/feed/rss.xml',
  site_url: 'http://TUDOMINIO.COM',
  language: 'es',
  pubDate: new Date(),
  ttl: '60',
});

const feedPath = path.join(__dirname, 'rss.xml');

// --- FUNCIONES AUXILIARES ---

// Expresión regular para URLs (http/https)
const urlRegex = /(https?:\/\/[^\s]+)/g;

// Extrae el primer enlace encontrado en el texto
function findFirstUrl(text) {
  if (!text) return null;
  const matches = text.match(urlRegex);
  return matches ? matches[0] : null;
}

// Extrae la primera frase completa (hasta el primer '.' o '?')
function getFirstSentence(text) {
  if (!text) return 'Nuevo post';
  // Buscar primeros índices de '.' y '?'
  const dotIndex = text.indexOf('.');
  const questionIndex = text.indexOf('?');
  let endIndex = -1;

  if (dotIndex !== -1 && questionIndex !== -1) {
    endIndex = Math.min(dotIndex, questionIndex);
  } else if (dotIndex !== -1) {
    endIndex = dotIndex;
  } else if (questionIndex !== -1) {
    endIndex = questionIndex;
  }

  if (endIndex === -1) {
    return text.substring(0, 150);
  }
  return text.substring(0, endIndex + 1);
}

// Elimina todas las URLs del texto
function removeUrls(text) {
  if (!text) return '';
  return text.replace(urlRegex, '').trim();
}

// Elimina el carácter '#' pero conserva la palabra
function removeHashtags(text) {
  if (!text) return '';
  return text.replace(/#(\w+)/g, '$1');
}

// Genera y guarda el feed RSS
function generateFeed(item) {
  feed.item(item);
  const xml = feed.xml({ indent: true });
  try {
    fs.writeFileSync(feedPath, xml);
    console.log(`✅ Feed RSS actualizado: ${item.title}`);
  } catch (err) {
    console.error('❌ Error al escribir RSS:', err);
  }
}

// Iniciar bot y escuchar el canal
console.log('🤖 Bot iniciado.');
const bot = new TelegramBot(TOKEN, { polling: true });

bot.on('channel_post', async (msg) => {
  if (msg.chat.id.toString() !== CHANNEL_ID.toString() && `@${msg.chat.username}` !== CHANNEL_ID) {
    return;
  }

  const raw = msg.text || msg.caption || '';
  // Limpiar hashtags antes de procesar
  const text = removeHashtags(raw);
  const link = findFirstUrl(text);
  if (!link) {
    console.log('-> Ignorado (sin enlace)');
    return;
  }

  // Obtener la primera frase como rawTitle
  const rawTitle = getFirstSentence(text);
  // Determinar título: quitar solo el punto final, no el '?' de preguntas
  let title;
  if (rawTitle.endsWith('.')) {
    title = rawTitle.slice(0, -1);
  } else {
    title = rawTitle;
  }

  // Descripción: quitar la frase del rawTitle y URLs
  let descText = text.replace(rawTitle, '').trim();
  let descHtml = removeUrls(descText).replace(/\n/g, '<br>');

  // Agregar soporte de imagen o vídeo
  try {
    if (msg.photo) {
      const fileId = msg.photo[msg.photo.length - 1].file_id;
      const linkPhoto = await bot.getFileLink(fileId);
      descHtml += `<br><br><img src="${linkPhoto}" alt="Imagen" style="max-width:100%;">`;
    } else if (msg.video) {
      const fileId = msg.video.file_id;
      const linkVideo = await bot.getFileLink(fileId);
      descHtml += `<br><br><p><a href="${linkVideo}">🎬 Ver vídeo</a></p>`;
    }
  } catch (err) {
    console.error('❌ Error fichero:', err.message);
  }

  // Crear item y actualizar RSS
  const item = {
    title,
    description: descHtml,
    url: link,
    guid: msg.message_id,
    date: new Date(msg.date * 1000),
    custom_elements: [{ 'content:encoded': { _cdata: descHtml } }]
  };

  generateFeed(item);
});

bot.on('polling_error', (err) => {
  console.error('❌ Polling error:', err.code, err.message);
});

Probamos que el programa funciona corriendo index.js y subiendo una noticia al canal sin parar el programa

cd /var/www/html/maximalismo/centro/feed/
node index.js

Paramos el programa.

Instalamos pm2 para no usar cron y desde la carpeta del proyecto lo ponemos en marcha

npm install pm2 -g
# Desde la carpeta del proyecto
pm2 start index.js --name "telegram-rss-bot"

Y ahora tendría que aparecer la RSS en: http://tudominio.com/feed/rss.xml

Si quisiera reiniciar pm2 porque modifico el código u otra cosa

pm2 restart telegram-rss-bot
Fin del artículo
Envíanos tus comentarios usando nuestro buzón en Telegram