diff --git a/.env.example b/.env.example index a9041ad2..d8547b67 100644 --- a/.env.example +++ b/.env.example @@ -3,6 +3,7 @@ TEST_GUILD_ID= STATUS=DEVELOPMENT BOTSTATUS=Would You? +PRODUCTION=true # Tokens diff --git a/src/events/ready.ts b/src/events/ready.ts index 78f8692f..be4e9d0f 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -45,10 +45,11 @@ const event: Event = { ), ); if (client.user?.id) { + console.log(client.user?.id); await rest.put( Routes.applicationGuildCommands( client.user.id, - process.env.TEST_GUILD_ID, + process.env.TEST_GUILD_ID as string, ), { body: globalCommands, diff --git a/src/util/dailyMessage.ts b/src/util/dailyMessage.ts index b6cbce9f..ba494a89 100644 --- a/src/util/dailyMessage.ts +++ b/src/util/dailyMessage.ts @@ -1,10 +1,12 @@ -import { EmbedBuilder, bold } from "discord.js"; +import { Channel, EmbedBuilder, bold } from "discord.js"; import { white, gray, green } from "chalk-advanced"; import * as mom from "moment-timezone"; import { CronJob } from "cron"; import { captureException } from "@sentry/node"; import WouldYou from "./wouldYou"; import { getWouldYouRather, getWwyd } from "./Functions/jsonImport"; +import { IGuildModel } from "./Models/guildModel"; +import { Error } from "mongoose"; export default class DailyMessage { private client: WouldYou; @@ -18,7 +20,7 @@ export default class DailyMessage { */ start() { new CronJob( - "0 */30 * * * *", // Every 30 minutes, every hour, every day + "0 */30 * * * *", // Every 30 seconds, minutes, every hour, every day () => { this.runSchedule(); }, @@ -33,146 +35,210 @@ export default class DailyMessage { * @return {Promise} */ async runSchedule() { - let guilds = await this.client.database.getAll(); - //guilds = guilds.filter(g => this.client.guilds.cache.has(g.guildID) && g.dailyMsg); - guilds = guilds.filter( - (g) => mom.tz(g.dailyTimezone).format("HH:mm") === g.dailyInterval, + let guilds = ( + await this.client.database.getAllActiveDailyMessageGuilds() + ).filter( + (guild) => + guild.dailyInterval === mom.tz(guild.dailyTimezone).format("HH:mm"), ); - + if (guilds.length <= 0) { + //Check if there are any daily messages active for guilds. + return console.log( + `${new Date().toISOString()}`, + white("Daily Message"), + gray(">"), + green("No active daily messages"), + ); + } console.log( - `${white("Daily Message")} ${gray(">")} ${green( - "Running daily message check for " + guilds.length + " guilds", - )}`, + white("Daily Message"), + gray(">"), + green(`Running daily message check for ${guilds.length} guilds`), ); - let i = 0; - for (const db of guilds) { - if (!db?.dailyChannel) continue; - if (!db.dailyMsg) continue; - i++; - setTimeout(async () => { - const channel = await this.client.channels - .fetch(db.dailyChannel) - .catch(async (err) => { - captureException(err); - await this.client.database.updateGuild(db?.guildID, { - db, - dailyMsg: false, - }); - }); - - if (!channel?.id) { - await this.client.database.updateGuild(db?.guildID, { - db, - dailyMsg: false, - }); - return; - } // Always directly return before do to many actions - - const General = await getWouldYouRather(db.language); - const WhatYouDo = await getWwyd(db.language); - - let randomDaily: any; - let dailyId; - - if (db.customTypes === "regular") { - let array = []; - array.push(...General, ...WhatYouDo); - randomDaily = array[Math.floor(Math.random() * array.length)]; - } else if (db.customTypes === "mixed") { - let array = []; - if (db.customMessages.filter((c) => c.type !== "nsfw").length != 0) { - array.push( - db.customMessages.filter((c) => c.type !== "nsfw")[ - Math.floor( - Math.random() * - db.customMessages.filter((c) => c.type !== "nsfw").length, - ) - ].msg, - ); - } else { - randomDaily = [...General, ...WhatYouDo]; - } - array.push(...General, ...WhatYouDo); - randomDaily = array[Math.floor(Math.random() * array.length)]; - } else if (db.customTypes === "custom") { - if (db.customMessages.filter((c) => c.type !== "nsfw").length === 0) { - const debugChannel = (await this.client.channels.fetch( - "1192118227497652276", - )) as any; - - if (!debugChannel) return console.log("No debug channel found"); - - await debugChannel?.send({ - content: "Sending webhook message line 103 dailymessage.ts", - }); - return this.client.webhookHandler - .sendWebhook( - channel, - db.dailyChannel, - { - content: - "There's currently no custom Would You messages to be displayed for daily messages! Either make new ones or turn off daily messages.", - }, - db.dailyThread, - ) - .catch(async (err) => { - captureException(err); - await this.client.database.updateGuild(db?.guildID, { - db, - dailyMsg: false, - }); - }); - } - - randomDaily = db.customMessages.filter((c) => c.type !== "nsfw")[ - Math.floor( - Math.random() * - db.customMessages.filter((c) => c.type !== "nsfw").length, - ) - ].msg; - } - - dailyId = Math.floor(Math.random() * randomDaily.length); - - const embed = new EmbedBuilder() - .setColor("#0598F6") - .setFooter({ - text: `Daily Message | Type: ${db.customTypes.replace(/^\w/, (c) => - c.toUpperCase(), - )} | ID: ${dailyId}`, - }) - .setDescription(bold(randomDaily) as string); - const debugChannel = (await this.client.channels.fetch( - "1192118227497652276", - )) as any; - - if (!debugChannel) return console.log("No debug channel found"); - - await debugChannel?.send({ - content: "Sending webhook message line 145 dailymessage.ts", - }); - await this.client.webhookHandler - .sendWebhook( - channel, - db.dailyChannel, - { - embeds: [embed], - content: db.dailyRole ? `<@&${db.dailyRole}>` : null, - }, - db.dailyThread, - ) - .catch(async (err) => { - captureException(err); - await this.client.database.updateGuild(db?.guildID, { - db, - dailyMsg: false, - }); - }); - - return await this.client.database.updateGuild(db?.guildID, { - lastUsageTimestamp: Date.now(), - }); - }, i * 2500); // We do a little timeout here to work against discord ratelimit with 50reqs/second + // Loop over every guild to get their message and send it to them trough a webhook. + guilds.forEach(async (guild) => { + try { + await this.sendDaily(guild); + } catch (error) { + this.handleError(new Error(error as string), guild); + } + }); + return; // REMOVE ME + + // let i = 0; + // for (const db of guilds) { + // if (!db?.dailyChannel) continue; + // if (!db.dailyMsg) continue; + // i++; + // setTimeout(async () => { + // const channel = await this.client.channels + // .fetch(db.dailyChannel) + // .catch(async (err) => { + // captureException(err); + // await this.client.database.updateGuild(db?.guildID, { + // db, + // dailyMsg: false, + // }); + // }); + + // if (!channel?.id) { + // await this.client.database.updateGuild(db?.guildID, { + // db, + // dailyMsg: false, + // }); + // return; + // } // Always directly return before do to many actions + + // let randomDaily: any; + // let dailyId; + + // dailyId = Math.floor(Math.random() * randomDaily.length); + + // const embed = new EmbedBuilder() + // .setColor("#0598F6") + // .setFooter({ + // text: `Daily Message | Type: ${db.customTypes.replace(/^\w/, (c) => + // c.toUpperCase(), + // )} | ID: ${dailyId}`, + // }) + // .setDescription(bold(randomDaily) as string); + // const debugChannel = (await this.client.channels.fetch( + // "1192118227497652276", + // )) as any; + + // if (!debugChannel) return console.log("No debug channel found"); + + // await debugChannel?.send({ + // content: "Sending webhook message line 145 dailymessage.ts", + // }); + // await this.client.webhookHandler + // .sendWebhook( + // channel, + // db.dailyChannel, + // { + // embeds: [embed], + // content: db.dailyRole ? `<@&${db.dailyRole}>` : null, + // }, + // db.dailyThread, + // ) + // .catch(async (err) => { + // captureException(err); + // await this.client.database.updateGuild(db?.guildID, { + // db, + // dailyMsg: false, + // }); + // }); + + // return await this.client.database.updateGuild(db?.guildID, { + // lastUsageTimestamp: Date.now(), + // }); + // }, i * 2500); // We do a little timeout here to work against discord ratelimit with 50reqs/second + // } + } + private async sendDaily(guild: IGuildModel): Promise { + let randomDaily = await this.getDailyMessage(guild); + let channel = await this.getDailyMessageChannel(guild); + if (!channel) { + return this.handleError( + new Error("No channel has been fetched to post a daily message to!"), + guild, + ); + } + if (!randomDaily) { + return this.handleError( + new Error("No random question has been fetched!"), + guild, + ); + } + let embed = this.buildEmbed( + randomDaily[0], + randomDaily[1], + guild.customTypes, + ); + if (!embed) { + return this.handleError( + new Error( + `Failed to build daily message embed for guild ${guild.guildID}`, + ), + guild, + ); + } + return this.sendWebhook(channel, embed, guild); + } + private async getDailyMessageChannel( + guild: IGuildModel, + ): Promise { + return await this.client.channels.fetch(guild.dailyChannel); + } + private async getDailyMessage( + guild: IGuildModel, + ): Promise<[string, number] | null> { + const General = await getWouldYouRather(guild.language); // Fetch all general questions with the specified guild language + const WhatYouDo = await getWwyd(guild.language); // Fetch all WouldYou questions with the specified guild language + let allMessageArray = []; //Create a funcion scoped array to store all questions. + // Populate the allMessageArray with all the regular questions. + if (guild.customTypes === "regular") { + allMessageArray.push(...General, ...WhatYouDo); } + // Populate the allMessageArray with all custom messages and the regular questions. + if (guild.customTypes === "mixed") { + allMessageArray.push(...General, ...WhatYouDo); + if (guild.customMessages.length <= 0) { + let id = Math.floor(Math.random() * allMessageArray.length); + return [allMessageArray[id], id]; + } + guild.customMessages.forEach((message) => + allMessageArray.push(message.msg), + ); + } + // Populate the allMessageArray with all custom messages. + if (guild.customTypes === "custom") { + guild.customMessages.forEach((message) => + allMessageArray.push(message.msg), + ); + } + // Handle the allMessageArray and send a return a random question or 0 + if (allMessageArray.length <= 0) return null; + let id = Math.floor(Math.random() * allMessageArray.length); + return [allMessageArray[id], id]; + } + private async sendWebhook( + channel: Channel, + embed: EmbedBuilder, + guild: IGuildModel, + ): Promise { + try { + await this.client.webhookHandler.sendWebhook( + channel, + guild.dailyChannel, + { + embeds: [embed], + content: guild.dailyRole ? `<@&${guild.dailyRole}>` : null, + }, + guild.dailyThread, + ); + } catch (error) { + this.handleError(new Error(error as string), guild); + } finally { + return; + } + } + private async handleError(error: Error, guild: IGuildModel): Promise { + console.error(error); + captureException(error); + await this.client.database.updateGuild(guild.guildID, { + guild, + dailyMsg: false, + }); + } + private buildEmbed(question: string, id: number, type: string): EmbedBuilder { + return new EmbedBuilder() + .setColor("#0598F6") + .setFooter({ + text: `Daily Message | Type: ${type.replace(/^\w/, (c) => + c.toUpperCase(), + )} | ID: ${id}`, + }) + .setDescription(bold(question) as string); } } diff --git a/src/util/databaseHandler.ts b/src/util/databaseHandler.ts index a6c97f7d..cdbf8fda 100644 --- a/src/util/databaseHandler.ts +++ b/src/util/databaseHandler.ts @@ -252,4 +252,12 @@ export default class DatabaseHandler { async getAll() { return this.guildModel.find(); } + /** + * @name getAllActiveDailyMessageGuilds + * @description Fetch all the active dailyMessage guilds. + * @returns {Promise} + */ + async getAllActiveDailyMessageGuilds(): Promise { + return this.guildModel.find({ dailyMsg: true }); + } } diff --git a/src/util/webhookHandler.ts b/src/util/webhookHandler.ts index 14e95eb9..64495d8e 100644 --- a/src/util/webhookHandler.ts +++ b/src/util/webhookHandler.ts @@ -139,101 +139,95 @@ export default class WebhookHandler { } else return null; }; - // webhookFallBack = async ( - // channel: any = null, - // channelId: string, - // message: any, - // err: any = false, - // ): Promise => { - // if (!channel) - // channel = await this.c.channels.fetch(channelId).catch((er) => { - // captureException(er); - // }); - - // if (!channel) return; - // if ( - // err && - // (err?.code === 10015 || - // (typeof err.message === "string" && - // err.message.includes("Unknown Webhook"))) && - // channel - // ?.permissionsFor(this.c?.user?.id) - // .has([PermissionFlagsBits.ManageWebhooks]) - // ) { - // const webhooks = await channel.fetchWebhooks(); - - // if (webhooks && webhooks.size > 0) { - // let i = 0; - // for (const web of webhooks) { - // i++; - // setInterval(() => { - // if (web?.owner?.id === this.c?.user?.id) { - // web - // .delete("Deleting old webhook, to create a new one") - // .catch((err: any) => { - // captureException(err); - // }); - // } - // }, 1000 * i); - // } - // } - - // const webhook = await this.createWebhook( - // channel, - // channelId, - // "Would You", - // this.c.user?.displayAvatarURL() as string, - // "Webhook token unavailable, creating new webhook", - // ); - - // if (!webhook?.id || !webhook.token) - // return this.webhookFallBack(channel, channelId, message, false); - - // const webhookClient = new WebhookClient({ - // id: webhook.id, - // token: cryptr.decrypt(webhook.token), - // }); - - // if (!webhookClient) - // return this.webhookFallBack(channel, channelId, message, false); - // const debugChannel = await this.c.channels.fetch("1192118227497652276") as any; - - // if(!debugChannel) return console.log("No debug channel found") - - // await debugChannel?.send({content: "Sending webhook message line 203 webhookhandler.ts"}) - - // await webhookClient.send(message).catch(async (err) => { - // captureException(err); - // return this.webhookFallBack(channel, channelId, message, false); - // }); - // } else { - // if ( - // channel - // ?.permissionsFor(this.c?.user?.id) - // .has([PermissionFlagsBits.EmbedLinks]) - // ) { - // const guildSettings = await this.c.database.getGuild(channel.guild.id); - - // message.embeds = message?.embeds ?? []; - // message.content = null; - // message.embeds = [ - // new EmbedBuilder() - // .setColor("#FE0001") - // .setDescription( - // "🛑 " + - // this.c.translation.get( - // guildSettings?.language ?? "en_EN", - // "webhookManager.noWebhook", - // ), - // ), - // ]; - - // return await channel.send(message).catch((err: Error) => { - // captureException(err); - // }); - // } - // } - // }; + webhookFallBack = async ( + channel: any = null, + channelId: string, + message: any, + err: any = false, + ): Promise => { + if (!channel) + channel = await this.c.channels.fetch(channelId).catch((er) => { + captureException(er); + }); + + if (!channel) return; + if ( + err && + (err?.code === 10015 || + (typeof err.message === "string" && + err.message.includes("Unknown Webhook"))) && + channel + ?.permissionsFor(this.c?.user?.id) + .has([PermissionFlagsBits.ManageWebhooks]) + ) { + const webhooks = await channel.fetchWebhooks(); + + if (webhooks && webhooks.size > 0) { + let i = 0; + for (const web of webhooks) { + i++; + setInterval(() => { + if (web?.owner?.id === this.c?.user?.id) { + web + .delete("Deleting old webhook, to create a new one") + .catch((err: any) => { + captureException(err); + }); + } + }, 1000 * i); + } + } + + const webhook = await this.createWebhook( + channel, + channelId, + "Would You", + this.c.user?.displayAvatarURL() as string, + "Webhook token unavailable, creating new webhook", + ); + + if (!webhook?.id || !webhook.token) + return this.webhookFallBack(channel, channelId, message, false); + + const webhookClient = new WebhookClient({ + id: webhook.id, + token: cryptr.decrypt(webhook.token), + }); + + if (!webhookClient) + return this.webhookFallBack(channel, channelId, message, false); + await webhookClient.send(message).catch(async (err) => { + captureException(err); + return this.webhookFallBack(channel, channelId, message, false); + }); + } else { + if ( + channel + ?.permissionsFor(this.c?.user?.id) + .has([PermissionFlagsBits.EmbedLinks]) + ) { + const guildSettings = await this.c.database.getGuild(channel.guild.id); + + message.embeds = message?.embeds ?? []; + message.content = null; + message.embeds = [ + new EmbedBuilder() + .setColor("#FE0001") + .setDescription( + "🛑 " + + this.c.translation.get( + guildSettings?.language ?? "en_EN", + "webhookManager.noWebhook", + ), + ), + ]; + + return await channel.send(message).catch((err: Error) => { + captureException(err); + }); + } + } + }; /** * Send a message to a channel with a webhook @@ -248,6 +242,7 @@ export default class WebhookHandler { message: any, thread?: boolean, ) => { + if (!channelId && channel?.id) channelId = channel.id; if (!channelId) return; @@ -257,7 +252,6 @@ export default class WebhookHandler { if (webhookData?.id) webhookData.id = webhookData.id; if (webhookData?.token) webhookData.token = webhookData.token; - if (!webhookData?.id || !webhookData?.token) { let webhook = await this.createWebhook( channel, @@ -269,65 +263,49 @@ export default class WebhookHandler { if (webhook?.id) webhook.id = webhook.id; if (webhook?.token) webhook.token = webhook.token; - - if (!webhook?.id || !webhook?.token) return; + if (!webhook?.id || !webhook?.token) + return this.webhookFallBack(channel, channelId, message, false); const webhookClient = new WebhookClient({ id: webhook.id, token: cryptr.decrypt(webhook.token), }); - if (!webhookClient) return; - const debugChannel = (await this.c.channels.fetch( - "1192118227497652276", - )) as any; - - if (!debugChannel) return console.log("No debug channel found"); - - await debugChannel?.send({ - content: "Sending webhook message line 287 webhookhandler.ts", + if (!webhookClient) + return this.webhookFallBack(channel, channelId, message, false); + const fallbackThread = await webhookClient.send(message).catch((err) => { + captureException(err); + return this.webhookFallBack(channel, channelId, message, false); }); - //const fallbackThread = await webhookClient.send(message).catch((err) => { - // captureException(err); - // return; - //}); - // if (!thread) return; - // this.c.rest.setToken(process.env.DISCORD_TOKEN as string); - // this.c.rest.post( - // ("/channels/" + - // channelId + - // "/messages/" + - // (fallbackThread as any).id + - // "/threads") as any, - // { - // body: { - // name: `${[ - // date.getFullYear(), - // date.getMonth() + 1, - // date.getDate(), - // ].join("/")} - Daily Message`, - // auto_archive_duration: "1440", - // }, - // }, - //); + if (!thread) return; + this.c.rest.setToken(process.env.DISCORD_TOKEN as string); + this.c.rest.post( + ("/channels/" + + channelId + + "/messages/" + + (fallbackThread as any).id + + "/threads") as any, + { + body: { + name: `${[ + date.getFullYear(), + date.getMonth() + 1, + date.getDate(), + ].join("/")} - Daily Message`, + auto_archive_duration: "1440", + }, + }, + ); } else { const webhook = new WebhookClient({ id: webhookData?.id, token: webhookData?.token, }); + if (!webhook) + return this.webhookFallBack(channel, channelId, message, false); - if (!webhook) return; - const debugChannel = (await this.c.channels.fetch( - "1192118227497652276", - )) as any; - - if (!debugChannel) return console.log("No debug channel found"); - - await debugChannel?.send({ - content: "Sending webhook message line 323 webhookhandler.ts", - }); const webhookThread = await webhook.send(message).catch((err) => { captureException(err); - return; + return this.webhookFallBack(channel, channelId, message, false); }); if (!thread) return;