From be58d911056ee8a35ff4ffbf6b796ea61ccfd1aa Mon Sep 17 00:00:00 2001 From: Purexo <5005154+Purexo@users.noreply.github.com> Date: Fri, 26 Dec 2025 17:31:27 +0100 Subject: [PATCH] feat: add TurnOff feed Closes: https://github.com/ES-Community/bot/issues/104 --- src/crons/TurnOff.ts | 73 ++++++++++++++++++++++++++++++++++++++ tests/cron/TurnOff.spec.ts | 20 +++++++++++ 2 files changed, 93 insertions(+) create mode 100644 src/crons/TurnOff.ts create mode 100644 tests/cron/TurnOff.spec.ts diff --git a/src/crons/TurnOff.ts b/src/crons/TurnOff.ts new file mode 100644 index 0000000..df0e289 --- /dev/null +++ b/src/crons/TurnOff.ts @@ -0,0 +1,73 @@ +import { Cron, findTextChannelByName } from '../framework/index.ts'; +import got from 'got'; +import { parse } from 'node-html-parser'; +import { KeyValue } from '../database/index.ts'; +import { EmbedBuilder, SnowflakeUtil } from 'discord.js'; + +export default new Cron({ + enabled: true, + name: '{turnoff.us}', + description: + 'Vérifie toutes les 30 minutes si un nouveau strip de {turnoff.us} est sorti', + schedule: '5,35 * * * *', + async handle(context) { + const strip = await getLastTurnOffStrip(); + + // vérifie le strip trouvé avec la dernière entrée + const lastStrip = await KeyValue.get('Last-Cron-TurnOff'); + const stripStoreIdentity = strip?.id ?? null; + if (lastStrip === stripStoreIdentity) return; // skip si identique + + await KeyValue.set('Last-Cron-TurnOff', stripStoreIdentity); // met à jour sinon + + if (!strip) return; // skip si pas de strip + + context.logger.info(`Found a new {turnoff.us} strip`, strip); + + const channel = findTextChannelByName(context.client.channels, 'gif'); + + await channel.send({ + embeds: [ + new EmbedBuilder() + .setURL(strip.link) + .setTitle(strip.title) + .setImage(strip.imageUrl) + .setTimestamp(strip.date), + ], + enforceNonce: true, + nonce: SnowflakeUtil.generate().toString(), + }); + }, +}); + +interface ITurnOffStrip { + id: string; + link: string; + title: string; + date: Date; + imageUrl: string; +} + +export async function getLastTurnOffStrip(): Promise { + const { body } = await got('https://turnoff.us/feed.xml'); + const rss = parse(body, { + blockTextElements: { + // link tag in XML RSS is a block with text content + link: true, + }, + }); + + const item = rss.querySelector('item'); + if (!item) return null; + + const description = item.querySelector('description'); + const img = description?.querySelector('img'); + + return { + id: item.querySelector('guid')?.textContent?.trim() ?? '', + link: item.querySelector('link')?.textContent?.trim() ?? '', + title: item.querySelector('title')?.textContent?.trim() ?? '', + date: new Date(item.querySelector('pubDate')?.textContent ?? new Date()), + imageUrl: img?.getAttribute('src') ?? '', + }; +} diff --git a/tests/cron/TurnOff.spec.ts b/tests/cron/TurnOff.spec.ts new file mode 100644 index 0000000..ce72abd --- /dev/null +++ b/tests/cron/TurnOff.spec.ts @@ -0,0 +1,20 @@ +import { test, expect } from 'vitest'; + +import { getLastTurnOffStrip } from '../../src/crons/TurnOff.ts'; + +test('getLastTurnOffStrip', async () => { + const strip = await getLastTurnOffStrip(); + if (!strip) return; + + expect(strip.id).toBeTruthy(); + expect(typeof strip.id).toBe('string'); + + expect(strip.link).toBeTruthy(); + expect(typeof strip.link).toBe('string'); + + expect(strip.title).toBeTruthy(); + expect(typeof strip.title).toBe('string'); + + expect(strip.imageUrl).toBeTruthy(); + expect(typeof strip.imageUrl).toBe('string'); +});