Vamos montar uma funcionalidade no nosso BOT do Discord para que ele possa ler um feed de notícias e postar as novidades em um dos nossos canais de texto!
O primeiro passo é ter um feed de notícias para fazer a leitura. Como exemplo, vamos utilizar o feed aqui do nosso blog 4Future:
const feedURL = "https://4future.com.br/index.php/feed/"
Ao colocar esse endereço no seu navegador, cairá numa página com os dados do feed. Vamos montar uma aplicação que fará a leitura desse feed.
Instale a biblioteca RSSParser na sua aplicação
npm i rss-parser@latest
Agora basta executar o leitor do feed dessa maneira, passando a URL como parâmetro:
const feed = await new RSSParser().parseURL(feedUrl);
for (const item of feed.items) {
// Realizar a leitura aqui! Coloque a sua lógica de negócio
}
Nesse loop para fazer a leitura dos itens, você pode colocar a lógica que desejar. No nosso caso esse código está dentro do nosso BOT do discord e estamos trabalhando com um banco de dados MongoDB. A ideia é verificar se o post do blog já foi lido anteriormente. Se não foi lido, o bot pega os dados do post e envia como uma mensagem para um de nossos canais de texto.
Vamos verificar se o post já foi lido anteriormente. Se sim, faz a leitura do próximo post. Caso contrário, irá prosseguir para salvar no DB:
//...etc
const blogPost = await this.blogPostRepository.readOneByLink({
link: item.link as string,
});
if (blogPost) continue;
const newBlogPost = this.buildBlogPost(item);
await this.blogPostRepository.create({
blogPost: newBlogPost,
});
//...etc
Depois basta fazer a leitura do item do feed e mandar a mensagem para o canal apropriado:
const channelId = env.discord.blogChannelID;
if (!channelId)
throw new Error("The blogChannelID config is not set correctly.");
const url = item.link as string;
const title = item.title as string;
const messagePayload = `⭐ 4Future tem post novo!\n➡️ [${title}](${url})`;
const channel = client.channels.cache.get(channelId!);
if (channel && channel.isTextBased()) {
channel.send(messagePayload);
}
Aqui está o trecho do código que faz toda essa lógica:
Obs: Está super-comentado com o intuito didático, nossos códigos só têm comentários em trechos onde é estritamente necessário!
// >> The env file with the configurations
import * as env from "../../../../config/env/environment.js";
// >> This is the discord client (see discordX docs)
import { client } from "../../../../config/discordX.js";
// >> The Blog models to interact with the database
import {
IBlogPostCreate,
IBlogPostRepository,
} from "../../repository/index.js";
// >> The RSS parsed package we used
import RSSParser from "rss-parser";
export class BlogCheckFeedUseCase {
// Interface to decouple the database logic from this useCase:
constructor(private blogPostRepository: IBlogPostRepository) {}
// Entry point:
async exec(): Promise<void> {
const feedUrl = "https://4future.com.br/index.php/feed/";
await this.readFeed(feedUrl);
return;
}
async readFeed(url: string): Promise<void> {
//1. Read the 4Future feed to obtain all the latest blog posts
const feed = await new RSSParser().parseURL(url);
for (const item of feed.items) {
// 2. Check if the blog posts is already in the database
const blogPost = await this.blogPostRepository.readOneByLink({
link: item.link as string,
});
if (blogPost) continue;
// 3. If not, save it in the database to that it won't be sent again
const newBlogPost = this.buildBlogPost(item);
await this.blogPostRepository.create({
blogPost: newBlogPost,
});
// 4. And create and send the message
await this.sendMessage(item);
// 5. Wait 1 minute before sending the next message
await new Promise((resolve) => setTimeout(resolve, 60000));
}
return;
}
/** Get the channel Id and sends the formated message */
async sendMessage(item: RSSParser.Item): Promise<boolean> {
// Obtain the channelId from the configurations
const channelId = env.discord.blogChannelID;
if (!channelId)
throw new Error("The blogChannelID config is not set correctly.");
// Setup the message to send to the text channel:
const url = item.link as string;
const title = item.title as string;
const messagePayload = `⭐ 4Future tem post novo!\n➡️ [${title}](${url})`;
// Send the message
const channel = client.channels.cache.get(channelId!);
if (channel && channel.isTextBased()) {
channel.send(messagePayload);
}
return true;
}
buildBlogPost(item: RSSParser.Item): IBlogPostCreate {
return {
title: item.title as string,
link: item.link as string,
pubDate: item.pubDate as string,
creator: item.creator as string,
// content: item.content as string, // Not saving the contents to save space
contentSnippet: item.contentSnippet as string,
guid: item.guid as string,
categories: item.categories as string[],
date: item.isoDate as unknown as Date,
};
}
}
Precisamos ter um ponto de partida para que essa leitura do feed seja realizada. No nosso BOT, colocamos um cronJob que realiza a checagem por novos posts periodicamente. A initCron é chamada no início da execução da aplicação.
Você também poderia adicionar um comando para o BOT que chama o blogCheckFeedUseCase , por exemplo.
// >> The cronJobs package
import cron from "node-cron";
import { blogCheckFeedUseCase } from "../modules/blog/useCases/checkFeed/index.js";
export function initCron() {
hourlyCheckBlog();
}
function hourlyCheckBlog() {
// Every hour
cron.schedule("0 * * * *", () => {
blogCheckFeedUseCase.exec();
});
}
Aqui o resultado final de como ficou no nosso canal do Discord.
Optei por simplesmente mandar o link (formatado em .md) pois o link-preview que o discord monta funciona muito bem.
O nosso BOT (chamado carinhosamente de DevID) possui algumas facilidades para nossa equipe de devs, como report de PR no DevOps, ainda está dando seus primeiros passos e está aprendendo novas funcionalidades.
Em breve iremos abrir o repositório do nosso BOT como public, aqui está o link:
https://github.com/BNPTI/DevID
Aguardo seu PR lá no GitHub!
Seja o primeiro a comentar