diff --git a/src/client.ts b/src/client.ts index d9ca997..e972c75 100644 --- a/src/client.ts +++ b/src/client.ts @@ -6,7 +6,6 @@ type MPServerCache = Record -type YouTubeCache = Record import Discord from 'discord.js'; import {readFileSync, readdirSync} from 'node:fs'; import {Tokens, Config, FSPlayer} from './typings/interfaces'; @@ -39,7 +38,6 @@ export default class TClient extends Discord.Client { registry: Array; config: Config; tokens: Tokens; - YTCache: YouTubeCache = {}; embed: typeof Discord.EmbedBuilder; collection: typeof Discord.Collection; attachmentBuilder: typeof Discord.AttachmentBuilder; @@ -73,10 +71,6 @@ export default class TClient extends Discord.Client { this.registry = []; this.config = importconfig as Config; this.tokens = tokens as Tokens; - this.YTCache = { - 'UCQ8k8yTDLITldfWYKDs3xFg': undefined, // Daggerwin - 'UCguI73--UraJpso4NizXNzA': undefined // Machinery Restorer - } as YouTubeCache; this.embed = Discord.EmbedBuilder; this.collection = Discord.Collection; this.attachmentBuilder = Discord.AttachmentBuilder; diff --git a/src/funcs/CacheServer.ts b/src/funcs/CacheServer.ts index 9c2471a..31f096d 100644 --- a/src/funcs/CacheServer.ts +++ b/src/funcs/CacheServer.ts @@ -19,10 +19,16 @@ export default class CacheServer { protected static eventManager() { RedisClient .on('connect', ()=>Logger.forwardToConsole('log', Prefix, 'Connection to Redis has been established')) - .on('error', (err:ErrorReply)=>Logger.forwardToConsole('error', Prefix, `Encountered an error in Redis: ${err.message}`)) - } - protected static async connect() { - await RedisClient.connect(); + .on('error', (err:ErrorReply)=>{ + Logger.forwardToConsole('error', Prefix, `Encountered an error in Redis: ${err.message}`) + setTimeout(async()=>{ + if (!RedisClient.isReady) { + Logger.forwardToConsole('log', Prefix, 'Client is zombified, starting a fresh connection...'); + RedisClient.quit(); + await RedisClient.connect(); + } + }, 1500) + }) } static async get(key:any) { const cachedResult = await RedisClient.get(key); @@ -30,19 +36,16 @@ export default class CacheServer { else return null } static async set(key:any, value:any) { - Logger.forwardToConsole('log', Prefix, `Building cache for ${key}`); return await RedisClient.set(key, JSON.stringify(value)); } static async expiry(key:any, time:number) { - Logger.forwardToConsole('log', Prefix, `Setting expiration for ${key} to ${time} seconds`); - return await RedisClient.expire(key, time); + return await RedisClient.expire(key, time); // NOTE: time is in seconds, not milliseconds -- you know what you did wrong } static async delete(key:any) { - Logger.forwardToConsole('log', Prefix, `Deleting cache for ${key}`); return await RedisClient.del(key); } static init() { - this.connect(); + RedisClient.connect(); this.eventManager(); } } diff --git a/src/funcs/YTModule.ts b/src/funcs/YTModule.ts index 668cac0..9450b3b 100644 --- a/src/funcs/YTModule.ts +++ b/src/funcs/YTModule.ts @@ -1,20 +1,38 @@ import {TextChannel} from 'discord.js'; import TClient from '../client.js'; import Logger from '../helpers/Logger.js'; +import CacheServer from './CacheServer.js'; import MessageTool from '../helpers/MessageTool.js'; -export default async(client: TClient, YTChannelID: string, YTChannelName: string, DiscordChannelID: string, DiscordRoleID: string)=>{ +export default async(client:TClient, YTChannelID:string, YTChannelName:string, DiscordChannelID:string, DiscordRoleID:string)=>{ let Data: any; + let cacheExpiry: number = 14400; // Invalidate cache after sitting in Redis for 4 hours try { - await fetch(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelID}`, {signal: AbortSignal.timeout(8000), headers: {'User-Agent': 'Daggerbot - Notification/undici'}}).then(async xml=>Data = client.xjs.xml2js(await xml.text(), {compact: true})) - } catch(err){ - Logger.forwardToConsole('log', 'YTModule', `Failed to fetch "${YTChannelName}" from YouTube`) + await fetch(`https://www.youtube.com/feeds/videos.xml?channel_id=${YTChannelID}`, { + signal: AbortSignal.timeout(10000), + headers: {'User-Agent':'Daggerbot - Notification/undici'}, + }).then(async xml=>(Data = client.xjs.xml2js(await xml.text(), {compact: true}))); + } catch (err) { + Logger.forwardToConsole('log', 'YTModule', `Failed to fetch "${YTChannelName}" from YouTube`); + } + if (!Data) return; + const cacheKey = `YTCache:${YTChannelID}`; + const cachedVideoId = await CacheServer.get(cacheKey); + if (!cachedVideoId) { + const videoId = Data.feed.entry[0]['yt:videoId']._text; + await CacheServer.set(cacheKey, videoId).then(async()=>await CacheServer.expiry(cacheKey, cacheExpiry)); + return; } - if (!Data) return; - if (!client.YTCache[YTChannelID]) return client.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text; - if (Data.feed.entry[1]['yt:videoId']._text === client.YTCache[YTChannelID]){ - client.YTCache[YTChannelID] = Data.feed.entry[0]['yt:videoId']._text; - (client.channels.resolve(DiscordChannelID) as TextChannel).send({content: `${MessageTool.formatMention(DiscordRoleID, 'role')}\n**${YTChannelName}** just uploaded a video!\n${Data.feed.entry[0].link._attributes.href}`, allowedMentions: {parse: ['roles']}}) + if (Data.feed.entry[1]['yt:videoId']._text === cachedVideoId) { + const videoId = Data.feed.entry[0]['yt:videoId']._text; + await CacheServer.delete(cacheKey).then(async()=>{ + await CacheServer.set(cacheKey, videoId).then(async()=>await CacheServer.expiry(cacheKey, cacheExpiry)) + }); + + (client.channels.resolve(DiscordChannelID) as TextChannel).send({ + content: `${MessageTool.formatMention(DiscordRoleID, 'role')}\n**${YTChannelName}** just uploaded a video!\n${Data.feed.entry[0].link._attributes.href}`, + allowedMentions: {parse:['roles']}, + }); } } diff --git a/src/index.ts b/src/index.ts index 06ffd5d..b35bf99 100644 --- a/src/index.ts +++ b/src/index.ts @@ -38,11 +38,11 @@ if (client.config.botSwitches.music){ if (client.config.botSwitches.mpstats) setInterval(async()=>{ const serverlake = (await client.MPServer.findInCache(client.config.mainServer.id)); for await (const [locName, locArea] of Object.entries(client.config.MPStatsLocation)) await MPModule(client, locArea.channel, locArea.message, serverlake[locName], locName) -}, 35000); -setInterval(async()=>{// Ping notification is currently WIP, it might be active in production but I want to see how it goes with role mentions first so I can make any further changes. +}, 35000); // 35 seconds +setInterval(async()=>{ YTModule(client, 'UCQ8k8yTDLITldfWYKDs3xFg', 'Daggerwin', '528967918772551702', '1155760735612305408'); // 528967918772551702 = #videos-and-streams; 1155760735612305408 = YT Upload Ping; YTModule(client, 'UCguI73--UraJpso4NizXNzA', 'Machinery Restorer', '767444045520961567', '1155760735612305408') // 767444045520961567 = #machinery-restorer; ^^ -}, 300000) +}, 300000); // 5 minutes // Event loop for punishments and daily msgs setInterval(async()=>{