Skip to content

Commit 80fc11c

Browse files
authored
feat: add TurnOff feed (#117)
Closes: #104
1 parent e6a4658 commit 80fc11c

2 files changed

Lines changed: 93 additions & 0 deletions

File tree

src/crons/TurnOff.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { Cron, findTextChannelByName } from '../framework/index.ts';
2+
import got from 'got';
3+
import { parse } from 'node-html-parser';
4+
import { KeyValue } from '../database/index.ts';
5+
import { EmbedBuilder, SnowflakeUtil } from 'discord.js';
6+
7+
export default new Cron({
8+
enabled: true,
9+
name: '{turnoff.us}',
10+
description:
11+
'Vérifie toutes les 30 minutes si un nouveau strip de {turnoff.us} est sorti',
12+
schedule: '5,35 * * * *',
13+
async handle(context) {
14+
const strip = await getLastTurnOffStrip();
15+
16+
// vérifie le strip trouvé avec la dernière entrée
17+
const lastStrip = await KeyValue.get<string>('Last-Cron-TurnOff');
18+
const stripStoreIdentity = strip?.id ?? null;
19+
if (lastStrip === stripStoreIdentity) return; // skip si identique
20+
21+
await KeyValue.set('Last-Cron-TurnOff', stripStoreIdentity); // met à jour sinon
22+
23+
if (!strip) return; // skip si pas de strip
24+
25+
context.logger.info(`Found a new {turnoff.us} strip`, strip);
26+
27+
const channel = findTextChannelByName(context.client.channels, 'gif');
28+
29+
await channel.send({
30+
embeds: [
31+
new EmbedBuilder()
32+
.setURL(strip.link)
33+
.setTitle(strip.title)
34+
.setImage(strip.imageUrl)
35+
.setTimestamp(strip.date),
36+
],
37+
enforceNonce: true,
38+
nonce: SnowflakeUtil.generate().toString(),
39+
});
40+
},
41+
});
42+
43+
interface ITurnOffStrip {
44+
id: string;
45+
link: string;
46+
title: string;
47+
date: Date;
48+
imageUrl: string;
49+
}
50+
51+
export async function getLastTurnOffStrip(): Promise<ITurnOffStrip | null> {
52+
const { body } = await got('https://turnoff.us/feed.xml');
53+
const rss = parse(body, {
54+
blockTextElements: {
55+
// link tag in XML RSS is a block with text content
56+
link: true,
57+
},
58+
});
59+
60+
const item = rss.querySelector('item');
61+
if (!item) return null;
62+
63+
const description = item.querySelector('description');
64+
const img = description?.querySelector('img');
65+
66+
return {
67+
id: item.querySelector('guid')?.textContent?.trim() ?? '',
68+
link: item.querySelector('link')?.textContent?.trim() ?? '',
69+
title: item.querySelector('title')?.textContent?.trim() ?? '',
70+
date: new Date(item.querySelector('pubDate')?.textContent ?? new Date()),
71+
imageUrl: img?.getAttribute('src') ?? '',
72+
};
73+
}

tests/cron/TurnOff.spec.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { test, expect } from 'vitest';
2+
3+
import { getLastTurnOffStrip } from '../../src/crons/TurnOff.ts';
4+
5+
test('getLastTurnOffStrip', async () => {
6+
const strip = await getLastTurnOffStrip();
7+
if (!strip) return;
8+
9+
expect(strip.id).toBeTruthy();
10+
expect(typeof strip.id).toBe('string');
11+
12+
expect(strip.link).toBeTruthy();
13+
expect(typeof strip.link).toBe('string');
14+
15+
expect(strip.title).toBeTruthy();
16+
expect(typeof strip.title).toBe('string');
17+
18+
expect(strip.imageUrl).toBeTruthy();
19+
expect(typeof strip.imageUrl).toBe('string');
20+
});

0 commit comments

Comments
 (0)