From e51a56c79fdec85b59eef6ff56ee5032248ac912 Mon Sep 17 00:00:00 2001 From: Hikari Date: Mon, 9 Mar 2026 13:34:18 -0700 Subject: [PATCH] chore: remove news feed feature (#17) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Removes the hourly RSS news posting scheduler and `postNews` module - Removes the `rss.ts` interface and `lastRssItems` tracking from the `Amari` interface and bot initialisation - Removes the `news` channel ID from `ids.ts` - Removes the `rss-parser` dependency ✨ This PR was created with help from Hikari~ 🌸 Co-authored-by: Naomi Carrigan Reviewed-on: https://git.nhcarrigan.com/nhcarrigan/amari/pulls/17 Co-authored-by: Hikari Co-committed-by: Hikari --- package.json | 3 +- pnpm-lock.yaml | 36 ---------- src/config/ids.ts | 1 - src/index.ts | 13 +--- src/interfaces/amari.ts | 10 +-- src/interfaces/rss.ts | 65 ------------------ src/modules/postNews.ts | 146 ---------------------------------------- 7 files changed, 6 insertions(+), 268 deletions(-) delete mode 100644 src/interfaces/rss.ts delete mode 100644 src/modules/postNews.ts diff --git a/package.json b/package.json index d370647..f299f39 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "discord.js": "14.22.0", "fastify": "5.7.4", "node-schedule": "2.1.1", - "octokit": "5.0.5", - "rss-parser": "3.13.0" + "octokit": "5.0.5" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 46226ba..53de4f9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -32,9 +32,6 @@ importers: octokit: specifier: 5.0.5 version: 5.0.5 - rss-parser: - specifier: 3.13.0 - version: 3.13.0 devDependencies: '@nhcarrigan/eslint-config': specifier: 5.2.0 @@ -1099,9 +1096,6 @@ packages: electron-to-chromium@1.5.207: resolution: {integrity: sha512-mryFrrL/GXDTmAtIVMVf+eIXM09BBPlO5IQ7lUyKmK8d+A4VpRGG+M3ofoVef6qyF8s60rJei8ymlJxjUA8Faw==} - entities@2.2.0: - resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} - error-ex@1.3.2: resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} @@ -1984,9 +1978,6 @@ packages: engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true - rss-parser@3.13.0: - resolution: {integrity: sha512-7jWUBV5yGN3rqMMj7CZufl/291QAhvrrGpDNE4k/02ZchL0npisiYYqULF71jCEKoIiHvK/Q2e6IkDwPziT7+w==} - run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} @@ -2009,9 +2000,6 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} - sax@1.4.1: - resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} - secure-json-parse@4.0.0: resolution: {integrity: sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==} @@ -2401,14 +2389,6 @@ packages: utf-8-validate: optional: true - xml2js@0.5.0: - resolution: {integrity: sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==} - engines: {node: '>=4.0.0'} - - xmlbuilder@11.0.1: - resolution: {integrity: sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==} - engines: {node: '>=4.0'} - yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} @@ -3507,8 +3487,6 @@ snapshots: electron-to-chromium@1.5.207: {} - entities@2.2.0: {} - error-ex@1.3.2: dependencies: is-arrayish: 0.2.1 @@ -4609,11 +4587,6 @@ snapshots: '@rollup/rollup-win32-x64-msvc': 4.46.3 fsevents: 2.3.3 - rss-parser@3.13.0: - dependencies: - entities: 2.2.0 - xml2js: 0.5.0 - run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 @@ -4643,8 +4616,6 @@ snapshots: safe-stable-stringify@2.5.0: {} - sax@1.4.1: {} - secure-json-parse@4.0.0: {} semver@5.7.2: {} @@ -5071,11 +5042,4 @@ snapshots: ws@8.18.3: {} - xml2js@0.5.0: - dependencies: - sax: 1.4.1 - xmlbuilder: 11.0.1 - - xmlbuilder@11.0.1: {} - yocto-queue@0.1.0: {} diff --git a/src/config/ids.ts b/src/config/ids.ts index 174ae5f..ebf934d 100644 --- a/src/config/ids.ts +++ b/src/config/ids.ts @@ -20,7 +20,6 @@ export const ids = { mentorshipGoalForum: "1400629118110011526", mentorshipProjectForum: "1400616702265266186", naomiDiscussionForum: "1408154690121633917", - news: "1407804798677418198", partnershipRequests: "1451009066355654829", policyIdeation: "1417294974046965842", pressInquiries: "1451011543482368163", diff --git a/src/index.ts b/src/index.ts index ab0d1a7..5ee3529 100644 --- a/src/index.ts +++ b/src/index.ts @@ -21,7 +21,6 @@ import { checkRetroAchievements } from "./modules/checkAchievements.js"; import { getForumTagId } from "./modules/getForumTagId.js"; import { logMenteeJoin } from "./modules/logMenteeJoin.js"; import { logMenteeLeave } from "./modules/logMenteeLeave.js"; -import { postFreeCodeCampNews, postHackerNews } from "./modules/postNews.js"; import { postProgressReminders } from "./modules/postProgressReminders.js"; import { processMentorshipRole } from "./modules/processMentorshipRole.js"; import { processUserGuildTag } from "./modules/processUserGuildTag.js"; @@ -60,12 +59,8 @@ const amari: Amari = { ], partials: [ Partials.Channel ], }), - github: octokit, - githubApp: githubApp, - lastRssItems: { - freeCodeCamp: null, - hackerNews: null, - }, + github: octokit, + githubApp: githubApp, recentlyActiveChannels: new Set(), }; @@ -78,10 +73,6 @@ amari.discord.once(Events.ClientReady, () => { ); void cacheData(amari); analytics.startCron(); - scheduleJob("post news", "0 * * * *", async() => { - await postFreeCodeCampNews(amari); - await postHackerNews(amari); - }); scheduleJob("check guild tags", "0 0 * * *", async() => { await logger.log("debug", "Auditing guild tags."); await cacheData(amari); diff --git a/src/interfaces/amari.ts b/src/interfaces/amari.ts index b901846..e642d45 100644 --- a/src/interfaces/amari.ts +++ b/src/interfaces/amari.ts @@ -8,12 +8,8 @@ import type { Client } from "discord.js"; import type { App } from "octokit"; export interface Amari { - discord: Client; - github: App["octokit"]; - githubApp: App; - lastRssItems: { - freeCodeCamp: string | null; - hackerNews: string | null; - }; + discord: Client; + github: App["octokit"]; + githubApp: App; recentlyActiveChannels: Set; } diff --git a/src/interfaces/rss.ts b/src/interfaces/rss.ts deleted file mode 100644 index cd2dbba..0000000 --- a/src/interfaces/rss.ts +++ /dev/null @@ -1,65 +0,0 @@ -/** - * @copyright NHCarrigan - * @license Naomi's Public License - * @author Naomi Carrigan - */ - -/* eslint-disable @typescript-eslint/naming-convention -- Gonna have weird properties in this file. */ - -interface FreeCodeCampRSS { - items: Array<{ - "creator": string; - "title": string; - "link": string; - "pubDate": string; - "content:encoded": string; - "dc:creator": string; - "content": string; - "contentSnippet": string; - "guid": string; - "categories": Array; - "isoDate": Date; - }>; - feedUrl: string; - image: { - link: string; - url: string; - title: string; - }; - paginationLinks: { - self: string; - }; - title: string; - description: string; - generator: string; - link: string; - lastBuildDate: string; - ttl: string; -} - -interface HackerNewsRSS { - items: Array<{ - "creator": string; - "title": string; - "link": string; - "pubDate": string; - "dc:creator": string; - "comments": string; - "content": string; - "contentSnippet": string; - "guid": string; - "isoDate": string; - }>; - feedUrl: string; - paginationLinks: { - self: string; - }; - title: string; - description: string; - generator: string; - link: string; - lastBuildDate: string; - docs: string; -} - -export type { FreeCodeCampRSS, HackerNewsRSS }; diff --git a/src/modules/postNews.ts b/src/modules/postNews.ts deleted file mode 100644 index f42b5cd..0000000 --- a/src/modules/postNews.ts +++ /dev/null @@ -1,146 +0,0 @@ -/** - * @copyright NHCarrigan - * @license Naomi's Public License - * @author Naomi Carrigan - */ - -/* eslint-disable complexity -- These need a lot of logic. */ - -import { ChannelType } from "discord.js"; -// eslint-disable-next-line @typescript-eslint/naming-convention -- Importing a class. -import Parser from "rss-parser"; -import { ids } from "../config/ids.js"; -import { logger } from "../utils/logger.js"; -import type { Amari } from "../interfaces/amari.js"; -import type { FreeCodeCampRSS, HackerNewsRSS } from "../interfaces/rss.js"; - -/** - * We are completely aware that the contents of this regular expression - * are a violation of our Code of Conduct. Unfortunately, this is necessary - * to allow us to filter out the RSS feeds for inappropriate content. - * We apologise for any distress or harm this line may cause. - */ -const naughtyRegex -// eslint-disable-next-line stylistic/max-len -- Required for filtering. - = /\b(?:harass(?:ment|ing|ed)?|bully(?:ing)?|discriminat(?:e|ion|ory)|deadnam(?:e|ing)|misgender(?:ing|ed)?|doxx?(?:ing)?|threat(?:en(?:s|ing|ed)?)?|intimidat(?:e|ion|ing)|spam|scam|fraud|phish(?:ing)?|malware|exploit|attack(?:s|ing)?|hate\s*speech|slur|racist|sexist|homophobic|transphobic|ableist|xenophobic|bigot(?:ry|ed)?|troll(?:ing)?|abuse|derogat(?:ory|ing)|offensive|vulgar|obscene|nsfw|porn(?:o|ography)?|sexual(?:ly)?\s*(?:harass|explicit|content)|gore|violent|illegal|pirat(?:e|ed|ing)|crack(?:ed|ing)?|warez|torrent(?:s|ing)?|copyright\s*violat|stolen|leak(?:ed|ing)?\s*(?:data|info|personal)|dox|privacy\s*violat|confidential|unauthorized|solicitation|advertis(?:e|ing|ement)|promot(?:e|ion|ing)|affiliate|referral|spam(?:ming)?|sell(?:ing)?|buy(?:ing)?|commercial|marketing|drug\s*deal(?:er|ing)?|narcotics?|cocaine|heroin|meth(?:amphetamine)?|fentanyl|opiates?|opioids?|carfentanil|mdma|ecstasy|lsd|psilocybin|mushrooms?\s*trip|ketamine|pcp|ghb|rohypnol|roofies?|xanax|percocet|oxyco(?:done|ntin)|vicodin|adderall|ritalin|controlled\s*substance|illicit\s*drug|street\s*drug|drug\s*traffick(?:ing)?|prescription\s*fraud|pill\s*mill|cannabis\s*(?!legal|dispensary)|marijuana\s*(?!legal|dispensary)|weed\s*(?!control|killer)|pot\s*dealer|dope|murder(?:ing|ed)?|kill(?:ing|ed)?\s*(?:someone|person|people)|assassinat(?:e|ion)|homicide|manslaughter|assault(?:ing|ed)?|battery|kidnap(?:ping)?|abduct(?:ion|ed)?|human\s*traffick(?:ing)?|sex\s*traffick(?:ing)?|child\s*abuse|rape|sexual\s*assault|molest(?:ation|ing|ed)?|pedophil(?:e|ia)|child\s*porn|cp\s*(?=\s|$)|csam|robbery|burgl(?:ar|ary)|theft|steal(?:ing)?|shoplifting|embezzl(?:e|ement|ing)|launder(?:ing)?\s*money|extortion|blackmail|bribery|arson|terrorism|terrorist|bomb(?:ing)?|explo(?:sive|ding)|weapon\s*deal|arms\s*traffick|firearm\s*(?=illegal|unregistered)|gun\s*(?=illegal|unregistered)|counterfe(?:it|ing)|forg(?:e|ery|ing)|identity\s*theft|tax\s*evasion|insider\s*trading)\b/i; - -/** - * Used to filter out naughty words from RSS feeds. - * @param titleOrContent - The title or content to check. - * @returns True if the title or content is naughty, false if it is clean. - */ -const hasNaughtyWords = (titleOrContent: string): boolean => { - return naughtyRegex.test(titleOrContent); -}; - -/** - * Fetches the RSS feed from freeCodeCamp News and posts the latest updates. - * @param amari - Amari's instance. - */ -const postFreeCodeCampNews = async(amari: Amari): Promise => { - try { - const parser = new Parser(); - const { items } = await parser.parseURL( - "https://www.freecodecamp.org/news/rss", - ); - if (amari.lastRssItems.freeCodeCamp === null) { - amari.lastRssItems.freeCodeCamp = items[0]?.guid ?? null; - return; - } - const lastIndex = items.findIndex((item) => { - return item.guid === amari.lastRssItems.freeCodeCamp; - }); - const latestPosts - = lastIndex > -1 - ? items.slice(0, Math.min(lastIndex, 5)) - : items.slice(0, 5); - const channel - = amari.discord.channels.cache.get(ids.channels.news) - ?? await amari.discord.channels.fetch(ids.channels.news); - if (channel === null) { - throw new Error("Cannot find news channel."); - } - if (!channel.isSendable()) { - throw new Error("News channel is not sendable."); - } - if (amari.lastRssItems.freeCodeCamp !== items[0]?.guid) { - amari.lastRssItems.freeCodeCamp = items[0]?.guid ?? null; - } - await Promise.all( - latestPosts.map(async(post) => { - if ( - hasNaughtyWords(post.title) - || hasNaughtyWords(post.contentSnippet) - || hasNaughtyWords(post.content) - ) { - return; - } - const sent = await channel.send(post.link); - if (channel.type === ChannelType.GuildAnnouncement) { - await sent.crosspost(); - } - }), - ); - } catch (error) { - if (error instanceof Error) { - await logger.error("post freecodecamp news module", error); - } - } -}; - -/** - * Fetches the RSS feed from HackerNews and posts the latest updates. - * @param amari - Amari's instance. - */ -const postHackerNews = async(amari: Amari): Promise => { - try { - const parser = new Parser(); - const { items } = await parser.parseURL( - "https://hnrss.org/newest?link=comments", - ); - if (amari.lastRssItems.hackerNews === null) { - amari.lastRssItems.hackerNews = items[0]?.guid ?? null; - return; - } - const lastIndex = items.findIndex((item) => { - return item.guid === amari.lastRssItems.hackerNews; - }); - const latestPosts - = lastIndex > -1 - ? items.slice(0, Math.min(lastIndex, 5)) - : items.slice(0, 5); - const channel - = amari.discord.channels.cache.get(ids.channels.news) - ?? await amari.discord.channels.fetch(ids.channels.news); - if (channel === null) { - throw new Error("Cannot find news channel."); - } - if (!channel.isSendable()) { - throw new Error("News channel is not sendable."); - } - if (amari.lastRssItems.hackerNews !== latestPosts[0]?.guid) { - amari.lastRssItems.hackerNews = latestPosts[0]?.guid ?? null; - } - await Promise.all( - latestPosts.map(async(post) => { - if ( - hasNaughtyWords(post.title) - || hasNaughtyWords(post.contentSnippet) - || hasNaughtyWords(post.content) - ) { - return; - } - const sent = await channel.send(post.link); - if (channel.type === ChannelType.GuildAnnouncement) { - await sent.crosspost(); - } - }), - ); - } catch (error) { - if (error instanceof Error) { - await logger.error("post hackernews module", error); - } - } -}; - -export { postFreeCodeCampNews, postHackerNews };