From c5162c02d13a8726f5fbd8e58052d789e1e3bffa Mon Sep 17 00:00:00 2001 From: AnxietyisReal <96593068+AnxietyisReal@users.noreply.github.com> Date: Fri, 6 Oct 2023 16:54:27 +1100 Subject: [PATCH] Codebase improvements --- .pnp.cjs | 11 +++++++++ package.json | 1 + src/client.ts | 41 ++++++++++++++++++++------------- src/commands/bannedWords.ts | 3 ++- src/commands/case.ts | 9 ++++---- src/commands/dev.ts | 41 +++++++++++++++++++++++---------- src/commands/faq.ts | 14 +++++------ src/commands/mp-maintenance.ts | 4 ++-- src/commands/mp.ts | 4 ++-- src/commands/music.ts | 6 ++--- src/commands/poll.ts | 4 ++-- src/commands/purge.ts | 5 ++-- src/commands/statistics.ts | 4 ++-- src/commands/suggest.ts | 8 +++---- src/commands/tag.ts | 2 +- src/commands/unpunish.ts | 6 ++--- src/events/interactionCreate.ts | 14 +++++++---- src/events/messageCreate.ts | 14 +++++------ src/events/messageUpdate.ts | 3 ++- src/events/ready.ts | 6 +++-- src/funcs/Punish.ts | 4 ++-- src/helpers/FAQStore.ts | 8 ++++--- src/helpers/MessageTool.ts | 10 ++++++++ yarn.lock | 12 ++++++++-- 24 files changed, 151 insertions(+), 83 deletions(-) diff --git a/.pnp.cjs b/.pnp.cjs index 02065f3..b485b36 100644 --- a/.pnp.cjs +++ b/.pnp.cjs @@ -36,6 +36,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/node", "npm:20.8.2"],\ ["@types/node-cron", "npm:3.0.9"],\ ["canvas", "npm:2.11.2"],\ + ["chalk", "npm:5.3.0"],\ ["discord-player", "virtual:20c353e2d6536e37339997f03975c6a660f4d296e664d291bd43620c6162cca8eb5ef90b0998dc9db75ff6862e5da587d0530bae26805f5fadc8f17aaa4ff794#npm:6.6.4"],\ ["discord.js", "npm:14.13.0"],\ ["libsodium-wrappers", "npm:0.7.13"],\ @@ -930,6 +931,15 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { "linkType": "HARD"\ }]\ ]],\ + ["chalk", [\ + ["npm:5.3.0", {\ + "packageLocation": "./.yarn/cache/chalk-npm-5.3.0-d181999efb-623922e077.zip/node_modules/chalk/",\ + "packageDependencies": [\ + ["chalk", "npm:5.3.0"]\ + ],\ + "linkType": "HARD"\ + }]\ + ]],\ ["chownr", [\ ["npm:2.0.0", {\ "packageLocation": "./.yarn/cache/chownr-npm-2.0.0-638f1c9c61-c57cf9dd07.zip/node_modules/chownr/",\ @@ -1051,6 +1061,7 @@ function $$SETUP_STATE(hydrateRuntimeState, basePath) { ["@types/node", "npm:20.8.2"],\ ["@types/node-cron", "npm:3.0.9"],\ ["canvas", "npm:2.11.2"],\ + ["chalk", "npm:5.3.0"],\ ["discord-player", "virtual:20c353e2d6536e37339997f03975c6a660f4d296e664d291bd43620c6162cca8eb5ef90b0998dc9db75ff6862e5da587d0530bae26805f5fadc8f17aaa4ff794#npm:6.6.4"],\ ["discord.js", "npm:14.13.0"],\ ["libsodium-wrappers", "npm:0.7.13"],\ diff --git a/package.json b/package.json index 00784ae..91e00e1 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@octokit/auth-token": "4.0.0", "@octokit/rest": "20.0.2", "canvas": "2.11.2", + "chalk": "5.3.0", "discord-player": "6.6.4", "discord.js": "14.13.0", "libsodium-wrappers": "0.7.13", diff --git a/src/client.ts b/src/client.ts index 7f6008a..ffc4f1c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -88,26 +88,35 @@ export default class TClient extends Discord.Client { } async init(){ console.time('Startup'); - CacheServer.init(); - DatabaseServer.init(); - this.login((await TSClient.Token()).main); - for (const file of readdirSync('dist/events')){ - const eventFile = await import(`./events/${file}`); - this.on(file.replace('.js',''), async(...args)=>eventFile.default.run(this,...args)) - } - for (const file of readdirSync('dist/commands')){ - const command = await import(`./commands/${file}`); - this.commands.set(command.default.data.name,{command, uses: 0}); - this.registry.push(command.default.data.toJSON()) - } - for (const naming of Object.keys(this.config.MPStatsLocation)){ + await Promise.all([ + CacheServer.init(), + DatabaseServer.init(), + this.login((await TSClient.Token()).main) + ]); + + const eventFiles = await Promise.all( + readdirSync('dist/events').map(file=>import(`./events/${file}`)) + ); + eventFiles.forEach((eventFile, index)=>{ + const eventName = readdirSync('dist/events')[index].replace('.js', ''); + this.on(eventName, async(...args)=>eventFile.default.run(this, ...args)); + }); + + const commandFiles = await Promise.all( + readdirSync('dist/commands').map(file=>import(`./commands/${file}`)) + ); + commandFiles.forEach(commandFile=>{ + const {default: command} = commandFile; + this.commands.set(command.data.name, {command, uses: 0}); + this.registry.push(command.data.toJSON()); + }); + + Object.keys(this.config.MPStatsLocation).forEach(naming=>{ this.MPServerCache[naming] = { players: [], status: null, name: null } - } + }); } - isStaff = (guildMember:Discord.GuildMember)=>this.config.mainServer.staffRoles.map((x: string)=>this.config.mainServer.roles[x]).some((x: string)=>guildMember.roles.cache.has(x)); - youNeedRole = (interaction:Discord.CommandInteraction, role:string)=>interaction.reply(`This command is restricted to <@&${this.config.mainServer.roles[role]}>`); } diff --git a/src/commands/bannedWords.ts b/src/commands/bannedWords.ts index fff2b88..d2cc719 100644 --- a/src/commands/bannedWords.ts +++ b/src/commands/bannedWords.ts @@ -1,9 +1,10 @@ import Discord from 'discord.js'; import TClient from '../client.js'; import {writeFileSync} from 'node:fs'; +import MessageTool from '../helpers/MessageTool.js'; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ - if (!client.isStaff(interaction.member) && !client.config.whitelist.includes(interaction.member.id)) return client.youNeedRole(interaction, 'admin') + if (!MessageTool.isStaff(interaction.member) && !client.config.whitelist.includes(interaction.member.id)) return MessageTool.youNeedRole(interaction, 'admin'); const word = interaction.options.getString('word'); const wordExists = await client.bannedWords._content.findById(word); ({ diff --git a/src/commands/case.ts b/src/commands/case.ts index 182ceaf..bc7b57b 100644 --- a/src/commands/case.ts +++ b/src/commands/case.ts @@ -1,9 +1,10 @@ import Discord from 'discord.js'; import TClient from '../client.js'; import FormatTime from '../helpers/FormatTime.js'; +import MessageTool from '../helpers/MessageTool.js'; export default { run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ - if (!client.isStaff(interaction.member)) return client.youNeedRole(interaction, 'dcmod'); + if (!MessageTool.isStaff(interaction.member)) return MessageTool.youNeedRole(interaction, 'dcmod'); const caseId = interaction.options.getInteger('id'); ({ update: async()=>{ @@ -18,8 +19,8 @@ export default { const cancelledBy = punishment.expired ? await client.punishments._content.findOne({cancels:punishment.id}) : null; const cancels = punishment.cancels ? await client.punishments._content.findOne({_id:punishment.cancels}) : null; const embed = new client.embed().setColor(client.config.embedColor).setTimestamp(punishment.time).setTitle(`${punishment.type[0].toUpperCase()+punishment.type.slice(1)} | Case #${punishment.id}`).addFields( - {name: '🔹 User', value: `<@${punishment.member}> \`${punishment.member}\``, inline: true}, - {name: '🔹 Moderator', value: `<@${punishment.moderator}> \`${punishment.moderator}\``, inline: true}, + {name: '🔹 User', value: `${MessageTool.formatMention(punishment.member, 'user')} \`${punishment.member}\``, inline: true}, + {name: '🔹 Moderator', value: `${MessageTool.formatMention(punishment.moderator, 'user')} \`${punishment.moderator}\``, inline: true}, {name: '\u200b', value: '\u200b', inline: true}, {name: '🔹 Reason', value: `\`${punishment.reason || 'Reason unspecified'}\``, inline: true}) if (punishment.duration) embed.addFields({name: '🔹 Duration', value: `${FormatTime(punishment.duration, 100)}`}) @@ -35,7 +36,7 @@ export default { const userPunishment = userPunishmentData.sort((a,b)=>a.time-b.time).map((punishment)=>{ return { name: `${punishment.type[0].toUpperCase()+punishment.type.slice(1)} | Case #${punishment.id}`, - value: `Reason: \`${punishment.reason}\`\n${punishment.duration ? `Duration: ${FormatTime(punishment.duration, 3)}\n` : ''}Moderator: <@${punishment.moderator}>${punishment.expired ? `\nOverwritten by Case #${punishments.find(x=>x.cancels===punishment._id)?._id}` : ''}${punishment.cancels ? `\nOverwrites Case #${punishment.cancels}` : ''}` + value: `Reason: \`${punishment.reason}\`\n${punishment.duration ? `Duration: ${FormatTime(punishment.duration, 3)}\n` : ''}Moderator: ${MessageTool.formatMention(punishment.moderator, 'user')}${punishment.expired ? `\nOverwritten by Case #${punishments.find(x=>x.cancels===punishment._id)?._id}` : ''}${punishment.cancels ? `\nOverwrites Case #${punishment.cancels}` : ''}` } }); if (!punishments || !userPunishment) return interaction.reply(`**${user.username}** has a clean record.`) diff --git a/src/commands/dev.ts b/src/commands/dev.ts index 2af6299..fd74918 100644 --- a/src/commands/dev.ts +++ b/src/commands/dev.ts @@ -11,33 +11,48 @@ import fs from 'node:fs'; import util from 'node:util'; export default { run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>) { - if (!client.config.whitelist.includes(interaction.user.id)) return client.youNeedRole(interaction, 'bottech'); + if (!client.config.whitelist.includes(interaction.user.id)) return MessageTool.youNeedRole(interaction, 'bottech'); ({ eval: async()=>{ if (!client.config.eval) return interaction.reply({content: 'Eval is currently disabled.', ephemeral: true}); const code = interaction.options.getString('code') as string; + let consoleOutput:string = ''; + + const deleteEmbedBtn = new Discord.ButtonBuilder().setCustomId('deleteEmbed').setLabel('Delete').setStyle(Discord.ButtonStyle.Danger).setEmoji('🗑️'); + const deleteEmbedRow = new Discord.ActionRowBuilder().addComponents(deleteEmbedBtn); + const deleteEmbedCollector = interaction.channel.createMessageComponentCollector({componentType: Discord.ComponentType.Button}); + deleteEmbedCollector.on('collect', async i=>{ + if (i.customId === 'deleteEmbed') deleteEmbedCollector.stop(); + }); try { + const consoleLog = console.log; + console.log = (...args: any[])=>{ + consoleLog(...args); + consoleOutput += util.formatWithOptions({depth: 2, colors: true}, ...args) + '\n'; + } + const output = await eval(interaction.options.getBoolean('async') ? `(async()=>{${code}})()` : code); + let outVal = output !== undefined ? output : 'No output'; + if (outVal && outVal.includes && outVal.includes(client.token)) outVal = outVal.replace(client.token, '*'.repeat(8)); + const embedFields:Discord.APIEmbedField[] = [ + {name: 'Input', value: `\`\`\`js\n${code.slice(0,1020)}\n\`\`\``}, + {name: 'Output', value: `**\`\`\`${UsernameHelper.stripName(outVal === 'string' ? String(outVal) : 'ansi\n'+util.formatWithOptions({depth: 2, colors: true}, '%O', outVal)).slice(0,1012)}\n\`\`\`**`} + ]; + if (consoleOutput) embedFields.push({name: 'Console', value: `**\`\`\`ansi\n${UsernameHelper.stripName(consoleOutput).slice(0,1008)}\n\`\`\`**`}); if (typeof output === 'object') { - const embed = new client.embed().setColor(client.config.embedColor).addFields( - {name: 'Input', value: `\`\`\`js\n${code.slice(0,1020)}\n\`\`\``}, - {name: 'Output', value: `\`\`\`${UsernameHelper.stripName('ansi\n'+util.formatWithOptions({depth: 2, colors: true}, '%O', output)).slice(0,1016)}\n\`\`\``} - ); - interaction.reply({embeds: [embed]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed]})); + const embed = new client.embed().setColor(client.config.embedColor).addFields(embedFields); + interaction.reply({embeds: [embed], components: [deleteEmbedRow]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed], components: [deleteEmbedRow]})); } else { - const embed = new client.embed().setColor(client.config.embedColor).addFields( - {name: 'Input', value: `\`\`\`js\n${code.slice(0,1020)}\n\`\`\``}, - {name: 'Output', value: `\`\`\`${UsernameHelper.stripName('\n' + String(output)).slice(0,1016)}\n\`\`\``} - ); - interaction.reply({embeds: [embed]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed]})); + const embed = new client.embed().setColor(client.config.embedColor).addFields(embedFields); + interaction.reply({embeds: [embed], components: [deleteEmbedRow]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed], components: [deleteEmbedRow]})); } } catch (err) { const embed = new client.embed().setColor('#560000').addFields( {name: 'Input', value: `\`\`\`js\n${code.slice(0, 1020)}\n\`\`\``}, {name: 'Output', value: `\`\`\`\n${err}\`\`\``} ); - interaction.reply({embeds: [embed]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed]})).then(()=>{ + interaction.reply({embeds: [embed], components: [deleteEmbedRow]}).catch(()=>(interaction.channel as Discord.TextChannel).send({embeds: [embed], components: [deleteEmbedRow]})).then(()=>{ const filter = (x:Discord.Message)=>x.content.includes('console') && x.author.id === interaction.user.id const messagecollector = (interaction.channel as Discord.TextChannel).createMessageCollector({filter, max: 1, time: 60000}); messagecollector.on('collect', collected=>{ @@ -45,6 +60,8 @@ export default { collected.reply(`\`\`\`\n${UsernameHelper.stripName(err.stack)}\n\`\`\``); }); }); + } finally { + console.log = console.log; } }, update: async()=>{ diff --git a/src/commands/faq.ts b/src/commands/faq.ts index 0d97a2f..574d8fd 100644 --- a/src/commands/faq.ts +++ b/src/commands/faq.ts @@ -21,15 +21,15 @@ export default { ); const youCanGetRole = (role:string, roleEmoji:string)=>`You can get the ${MessageTool.formatMention(client.config.mainServer.roles[role], 'role')} role from <#802283932430106624> by clicking :${roleEmoji}: button on a webhook's message.`; ({ - srp: ()=>FAQStore.reply(null, interaction, null, '[Ballyspring]() is the map that is used in Survival Roleplay S4.\n\n> ℹ️ __Note__\n> The map won\'t look closely like the one in SRP as it is privately edited version of the public map.', null, false), + srp: ()=>FAQStore.reply(interaction, null, '[Ballyspring]() is the map that is used in Survival Roleplay S4.\n\n> ℹ️ __Note__\n> The map won\'t look closely like the one in SRP as it is privately edited version of the public map.', null, false), vtcR: ()=>interaction.reply(youCanGetRole('vtcmember', 'truck')+'\n*VTC skin can also be found in <#801975222609641472> as well.*'), mpR: ()=>interaction.reply(youCanGetRole('mpplayer', 'tractor')), - ytscam: ()=>FAQStore.reply(client, interaction, 'Scammers in YouTube comments section', 'If you ever see a comment mentioning a giveaway or anything else, **it\'s a scam!**\nYou should report it to YouTube and move on or ignore it.\nP.S: They\'re on every channel and not just Daggerwin.', CDN('YTScam'), true), - steamscam: ()=>FAQStore.reply(client, interaction, 'Steam account report scam', 'If you received a DM about this, please report it to Discord Moderators or open a [ticket](https://discord.com/channels/468835415093411861/942173932339986472/1054128182468546631)', CDN('SteamScam'), true), - fsVerifyGame: ()=>FAQStore.reply(client, interaction, 'Verifying your game files', `You can verify your game files if you experience any issues with your game.\n${verifyFaq}`, CDN('Steam-Epic-VerifyGamesLocation'), true), - fsShader: ()=>FAQStore.reply(client, interaction, 'Clearing your shader cache folder', 'If your game keeps crashing shortly after opening your game, then the shaders might be an issue.\nTo resolve this, you can go to `Documents/My Games/FarmingSimulator2022` and delete the folder called `shader_cache`', CDN('shader_cache-Location'), true), - fsLogfile: ()=>FAQStore.reply(client, interaction, 'Uploading your log file', 'You can find `log.txt` in `Documents/My Games/FarmingSimulator2022` and upload it into <#596989522395398144> along with your issue, so people can assist you further and help you resolve.', CDN('log_txt-Location'), true), - fsDevConsole: ()=>FAQStore.reply(client, interaction, 'Enabling the development console', 'Head over to `game.xml` in `Documents/My Games/FarmingSimulator2022` and find the section that mentions `false` inside development section, change it to `true` then you are good to go!\nFYI: The keybind to open console is \``\u200b\` (backtick).', CDN('enableDevConsole'), true) + ytscam: ()=>FAQStore.reply(interaction, 'Scammers in YouTube comments section', 'If you ever see a comment mentioning a giveaway or anything else, **it\'s a scam!**\nYou should report it to YouTube and move on or ignore it.\nP.S: They\'re on every channel and not just Daggerwin.', CDN('YTScam'), true), + steamscam: ()=>FAQStore.reply(interaction, 'Steam account report scam', 'If you received a DM about this, please report it to Discord Moderators or open a [ticket](https://discord.com/channels/468835415093411861/942173932339986472/1054128182468546631)', CDN('SteamScam'), true), + fsVerifyGame: ()=>FAQStore.reply(interaction, 'Verifying your game files', `You can verify your game files if you experience any issues with your game.\n${verifyFaq}`, CDN('Steam-Epic-VerifyGamesLocation'), true), + fsShader: ()=>FAQStore.reply(interaction, 'Clearing your shader cache folder', 'If your game keeps crashing shortly after opening your game, then the shaders might be an issue.\nTo resolve this, you can go to `Documents/My Games/FarmingSimulator2022` and delete the folder called `shader_cache`', CDN('shader_cache-Location'), true), + fsLogfile: ()=>FAQStore.reply(interaction, 'Uploading your log file', 'You can find `log.txt` in `Documents/My Games/FarmingSimulator2022` and upload it into <#596989522395398144> along with your issue, so people can assist you further and help you resolve.', CDN('log_txt-Location'), true), + fsDevConsole: ()=>FAQStore.reply(interaction, 'Enabling the development console', 'Head over to `game.xml` in `Documents/My Games/FarmingSimulator2022` and find the section that mentions `false` inside development section, change it to `true` then you are good to go!\nFYI: The keybind to open console is \``\u200b\` (backtick).', CDN('enableDevConsole'), true) } as any)[interaction.options.getString('question', true)](); }, data: new Discord.SlashCommandBuilder() diff --git a/src/commands/mp-maintenance.ts b/src/commands/mp-maintenance.ts index 86b2983..c745e9b 100644 --- a/src/commands/mp-maintenance.ts +++ b/src/commands/mp-maintenance.ts @@ -1,10 +1,10 @@ import Discord from 'discord.js'; import TClient from '../client.js'; - +import MessageTool from '../helpers/MessageTool.js'; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ if (client.config.mainServer.id === interaction.guildId) { - if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmanager) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech) && !interaction.member.roles.cache.has(client.config.mainServer.roles.admin)) return client.youNeedRole(interaction, 'mpmanager'); + if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmanager) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech) && !interaction.member.roles.cache.has(client.config.mainServer.roles.admin)) return MessageTool.youNeedRole(interaction, 'mpmanager'); } const maintenanceMessage = interaction.options.getString('message'); const activePlayersChannel = '739084625862852715'; diff --git a/src/commands/mp.ts b/src/commands/mp.ts index 3ef9fbe..0ec14f7 100644 --- a/src/commands/mp.ts +++ b/src/commands/mp.ts @@ -18,7 +18,7 @@ export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ if (client.uptime < 35000) return interaction.reply('I have just restarted, please wait for MPLoop to finish initializing.'); const serverSelector = interaction.options.getString('server'); - if (['468835769092669461', '1149238561934151690'].includes(interaction.channelId) && !client.isStaff(interaction.member) && ['status', 'players'].includes(interaction.options.getSubcommand())) return interaction.reply('Please use <#739084625862852715> for `/mp status/players` commands to prevent clutter in this channel.').then(()=>setTimeout(()=>interaction.deleteReply(), 6000)); + if (['468835769092669461', '1149238561934151690'].includes(interaction.channelId) && !MessageTool.isStaff(interaction.member) && ['status', 'players'].includes(interaction.options.getSubcommand())) return interaction.reply('Please use <#739084625862852715> for `/mp status/players` commands to prevent clutter in this channel.').then(()=>setTimeout(()=>interaction.deleteReply(), 6000)); const database = await client.MPServer.findInCache(interaction.guildId); const endpoint = await fetch(database[serverSelector].ip+'/feed/dedicated-server-stats.json?code='+database[serverSelector].code, {signal: AbortSignal.timeout(7500),headers:{'User-Agent':'Daggerbot - MPdata/undici'}}).then(r=>r.json() as Promise); @@ -190,7 +190,7 @@ export default { }, url: async()=>{ if (client.config.mainServer.id == interaction.guildId) { - if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmanager) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech) && !interaction.member.roles.cache.has(client.config.mainServer.roles.admin)) return client.youNeedRole(interaction, 'mpmanager'); + if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmanager) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech) && !interaction.member.roles.cache.has(client.config.mainServer.roles.admin)) return MessageTool.youNeedRole(interaction, 'mpmanager'); } const address = interaction.options.getString('address'); if (!address){ diff --git a/src/commands/music.ts b/src/commands/music.ts index bade487..924e6da 100644 --- a/src/commands/music.ts +++ b/src/commands/music.ts @@ -3,11 +3,11 @@ import TClient from '../client.js'; import TSClient from '../helpers/TSClient.js'; import {Player,useTimeline,useQueue} from 'discord-player'; import {SpotifyExtractor} from '@discord-player/extractor'; - +import MessageTool from '../helpers/MessageTool.js'; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ if (!client.config.botSwitches.music && !client.config.whitelist.includes(interaction.user.id)) return interaction.reply({content:'Music module is currently disabled.',ephemeral:true}); - if (!client.isStaff(interaction.member) && !client.config.whitelist.includes(interaction.member.id)) return interaction.reply('Music module is close to being completed, some parts may be incomplete or broken, so it has been restricted to staff for time-being.'); + if (!MessageTool.isStaff(interaction.member) && !client.config.whitelist.includes(interaction.member.id)) return interaction.reply('Music module is close to being completed, some parts may be incomplete or broken, so it has been restricted to staff for time-being.'); const player = Player.singleton(client); await player.extractors.register(SpotifyExtractor,{clientId: (await TSClient.Token()).spotify.client, clientSecret: (await TSClient.Token()).spotify.secret}); if (!interaction.member.voice.channel) return interaction.reply('Please join a voice channel first to use the command.'); @@ -149,4 +149,4 @@ export default { .setMinValue(0) .setMaxValue(9999) .setRequired(true))) -} \ No newline at end of file +} diff --git a/src/commands/poll.ts b/src/commands/poll.ts index 68b433a..25af69a 100644 --- a/src/commands/poll.ts +++ b/src/commands/poll.ts @@ -1,11 +1,11 @@ import Discord from 'discord.js'; import TClient from '../client.js'; import {writeFileSync, existsSync, mkdirSync} from 'node:fs'; - +import MessageTool from '../helpers/MessageTool.js'; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ if (client.config.mainServer.id === interaction.guildId) { - if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmod) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech)) return client.youNeedRole(interaction, 'mpmod'); + if (!interaction.member.roles.cache.has(client.config.mainServer.roles.mpmod) && !interaction.member.roles.cache.has(client.config.mainServer.roles.bottech)) return MessageTool.youNeedRole(interaction, 'mpmod'); } const channelId = '1084864116776251463'; // #mp-announcements ({ diff --git a/src/commands/purge.ts b/src/commands/purge.ts index 3a25c7d..319b293 100644 --- a/src/commands/purge.ts +++ b/src/commands/purge.ts @@ -1,9 +1,10 @@ import Discord from 'discord.js'; import TClient from '../client.js'; +import MessageTool from '../helpers/MessageTool.js'; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ if (client.config.mainServer.id === interaction.guildId) { - if (!client.isStaff(interaction.member)) return client.youNeedRole(interaction, 'dcmod'); + if (!MessageTool.isStaff(interaction.member)) return MessageTool.youNeedRole(interaction, 'dcmod'); } const amount = interaction.options.getInteger('amount') as number; if (amount > 100) return interaction.reply({content: 'Discord API limits purging up to 100 messages.', ephemeral: true}) @@ -33,4 +34,4 @@ export default { .addUserOption(x=>x .setName('user') .setDescription('Which user to have their messages obliterated?')) -} \ No newline at end of file +} diff --git a/src/commands/statistics.ts b/src/commands/statistics.ts index 552f912..0403b1e 100644 --- a/src/commands/statistics.ts +++ b/src/commands/statistics.ts @@ -30,11 +30,11 @@ export default { const columns = ['Command name', 'Count']; const includedCommands = client.commands.filter(x=>x.uses).sort((a,b)=>b.uses - a.uses); if (includedCommands.size === 0) return interaction.reply(`No commands have been used yet.\nUptime: **${FormatTime(client.uptime, 3, {longNames: true, commas: true})}**`); - const nameLength = Math.max(...includedCommands.map(x=>x.command.default.data.name.length), columns[0].length) + 2; + const nameLength = Math.max(...includedCommands.map(x=>x.command.data.name.length), columns[0].length) + 2; const amountLength = Math.max(...includedCommands.map(x=>x.uses.toString().length), columns[1].length) + 1; const rows = [`${columns[0] + ' '.repeat(nameLength - columns[0].length)}|${' '.repeat(amountLength - columns[1].length) + columns[1]}\n`, '-'.repeat(nameLength) + '-'.repeat(amountLength) + '\n']; includedCommands.forEach(command=>{ - const name = command.command.default.data.name; + const name = command.command.data.name; const count = command.uses.toString(); rows.push(`${name + ' '.repeat(nameLength - name.length)}${' '.repeat(amountLength - count.length) + count}\n`); }); diff --git a/src/commands/suggest.ts b/src/commands/suggest.ts index f8395ec..8196e9a 100644 --- a/src/commands/suggest.ts +++ b/src/commands/suggest.ts @@ -10,7 +10,7 @@ export default { const theirIdea = (await client.suggestion._content.findById(suggestionIDReply))?.idea; const timeFormatting = client.moment().format('DD/MM/YY h:mm A'); const stateChanged = 'Suggestion state has been successfully updated and DM is sent.'; - const dmFail = `Failed to send a DM to <@${userid}>, they possibly have it turned off or blocked me.\nSuggestion ID: **${suggestionIDReply}**`; + const dmFail = `Failed to send a DM to ${MessageTool.formatMention(userid, 'user')}, they possibly have it turned off or blocked me.\nSuggestion ID: **${suggestionIDReply}**`; ({ your: async()=>{ const webhook = await (await (client.channels.fetch(client.config.mainServer.channels.bot_suggestions) as Promise)).fetchWebhooks().then(x => x.find(y => y.name === client.user.username)); @@ -36,7 +36,7 @@ export default { }, approve: async()=>{ if (client.config.mainServer.id === interaction.guildId) { - if (!interaction.member.roles.cache.has(client.config.mainServer.roles.bottech)) return client.youNeedRole(interaction, 'bottech'); + if (!interaction.member.roles.cache.has(client.config.mainServer.roles.bottech)) return MessageTool.youNeedRole(interaction, 'bottech'); } if ((await client.suggestion._content.findById(suggestionIDReply)).state === 'Rejected') return interaction.reply({content: 'This suggestion\'s state is locked and cannot be modified.', ephemeral: true}); (await client.users.fetch(userid)).send({embeds: [new client.embed() @@ -51,7 +51,7 @@ export default { }, reject: async()=>{ if (client.config.mainServer.id === interaction.guildId) { - if (!interaction.member.roles.cache.has(client.config.mainServer.roles.bottech)) return client.youNeedRole(interaction, 'bottech'); + if (!interaction.member.roles.cache.has(client.config.mainServer.roles.bottech)) return MessageTool.youNeedRole(interaction, 'bottech'); } if ((await client.suggestion._content.findById(suggestionIDReply)).state === 'Approved') return interaction.reply({content: 'This suggestion\'s state is locked and cannot be modified.', ephemeral: true}); (await client.users.fetch(userid)).send({embeds: [new client.embed() @@ -104,4 +104,4 @@ export default { .setDescription('(Optional) Include a message with your rejection') .setRequired(true) .setMaxLength(256))) -} \ No newline at end of file +} diff --git a/src/commands/tag.ts b/src/commands/tag.ts index 40f5b80..994b4dc 100644 --- a/src/commands/tag.ts +++ b/src/commands/tag.ts @@ -8,7 +8,7 @@ export default { // If you question all those '?.', let me tell you: Discord.JS is fricking stupid and I am too stressed to find a solution for it. }, async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ - if (interaction.options.getSubcommandGroup() === 'management' && !client.isStaff(interaction.member) && !client.config.whitelist.includes(interaction.member.id)) return client.youNeedRole(interaction, 'dcmod'); + if (interaction.options.getSubcommandGroup() === 'management' && !MessageTool.isStaff(interaction.member) && !client.config.whitelist.includes(interaction.member.id)) return MessageTool.youNeedRole(interaction, 'dcmod'); const tagData = async()=>await client.tags._content.findOne({_id: interaction.options.getString('name')}); const tagMeta = { isEmbedTrue: async()=>(await tagData()).embedBool, diff --git a/src/commands/unpunish.ts b/src/commands/unpunish.ts index 772c52b..4b8f8dc 100644 --- a/src/commands/unpunish.ts +++ b/src/commands/unpunish.ts @@ -1,10 +1,10 @@ import Discord from 'discord.js'; import TClient from '../client.js'; import Logger from '../helpers/Logger.js'; - +import MessageTool from '../helpers/MessageTool.js'; export default { async run(client: TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>){ - if (!client.isStaff(interaction.member as Discord.GuildMember)) return client.youNeedRole(interaction, 'dcmod'); + if (!MessageTool.isStaff(interaction.member as Discord.GuildMember)) return MessageTool.youNeedRole(interaction, 'dcmod'); const punishment = (await client.punishments._content.find({})).find(x=>x._id === interaction.options.getInteger('case_id', true)); if (!punishment) return interaction.reply({content: 'Invalid Case ID', ephemeral: true}); if (punishment.expired) return interaction.reply('This case has been overwritten by another case.'); @@ -23,4 +23,4 @@ export default { .addStringOption(x=>x .setName('reason') .setDescription('Reason for removing the punishment')) -} \ No newline at end of file +} diff --git a/src/events/interactionCreate.ts b/src/events/interactionCreate.ts index 9beb1a9..95ec5d2 100644 --- a/src/events/interactionCreate.ts +++ b/src/events/interactionCreate.ts @@ -11,17 +11,17 @@ export default { if (!client.config.botSwitches.commands && !client.config.whitelist.includes(interaction.user.id)) return interaction.reply({content: `I am currently operating in development mode.\nPlease notify <@${client.config.whitelist[0]}> if this is a mistake.`, ephemeral: true}); if (commandFile){ try{ - commandFile.command.default.run(client, interaction); - commandFile.command.default.autocomplete ? commandFile.command.default.autocomplete(interaction) : undefined; + commandFile.command.run(client, interaction); + commandFile.command.autocomplete ? commandFile.command.autocomplete(interaction) : undefined; commandFile.uses ? commandFile.uses++ : commandFile.uses = 1; } catch (error){ - console.log(`An error occurred while running command "${interaction.commandName} ${interaction.options.getSubcommand(false) ?? ''}"`, error, error.stack); - return interaction.reply('An error occurred while executing that command.'); + console.log(`An error occurred while running command "${interaction.commandName} ${interaction.options.getSubcommandGroup(false) ?? ''} ${interaction.options.getSubcommand(false) ?? ''}"`, error, error.stack); + return interaction.reply('An error occurred while running that command.'); } } } else if (interaction.isAutocomplete()){ try { - await client.commands.get(interaction.commandName).command.default.autocomplete(client, interaction); + await client.commands.get(interaction.commandName).command.autocomplete(client, interaction); } catch (error){ return console.log('An error occurred while running autocomplete:\n', error) } @@ -45,6 +45,10 @@ export default { interaction.member.roles.add(RoleID, 'Button Role'); interaction.reply({content: `You have been added to <@&${RoleID}>`, ephemeral: true}) } + } else if (interaction.customId.includes('deleteEmbed')) { + if (!client.config.whitelist.includes(interaction.user.id)) return interaction.reply({content: '*Only whitelisted people can delete this embed.*', ephemeral: true}); + interaction.message.edit({content: '*Deleted.*', embeds: [], components: []}); + Logger.forwardToConsole('log', 'InteractionLog', `Embed has been deleted at ${interaction.message.url}`); } else Logger.forwardToConsole('log', 'InteractionLog', `Button has been pressed at ${interaction.message.url}`); } } diff --git a/src/events/messageCreate.ts b/src/events/messageCreate.ts index 1c7f49f..7fc072a 100644 --- a/src/events/messageCreate.ts +++ b/src/events/messageCreate.ts @@ -4,11 +4,11 @@ import Response from '../funcs/ResponseModule.js'; import CmdTrigger from '../funcs/CmdModule.js'; import Logger from '../helpers/Logger.js'; import Automoderator from '../funcs/Automod.js'; - +import MessageTool from '../helpers/MessageTool.js'; export default { async run(client:TClient, message:Discord.Message){ if (message.author.bot) return; - if (!message.inGuild()) return (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({content: `<:fish_unamused:1083675172407623711> <@${client.config.whitelist[0]}>\n**${message.author.username}** tried to send me a DM, their message is:\`\`\`${message.content}\`\`\``, allowedMentions: {parse: ['users']}}); + if (!message.inGuild()) return (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({content: `<:fish_unamused:1083675172407623711> ${MessageTool.formatMention(client.config.whitelist[0], 'user')}\n**${message.author.username}** tried to send me a DM, their message is:\`\`\`${message.content}\`\`\``, allowedMentions: {parse: ['users']}}); let automodded: boolean; if (client.config.botSwitches.automod && !message.member.roles.cache.has(client.config.mainServer.roles.admin) && message.guildId == client.config.mainServer.id){ @@ -18,7 +18,7 @@ export default { message.delete().catch(()=>Logger.forwardToConsole('log', 'AUTOMOD-BANNEDWORDS', automodFailReason)); message.channel.send('That word isn\'t allowed here.').then(x=>setTimeout(()=>x.delete(), 10000)); await Automoderator.repeatedMessages(client, message, 30000, 3, 'bw', '30m', 'Constant swearing'); - } else if (message.content.toLowerCase().includes('discord.gg/') && !client.isStaff(message.member as Discord.GuildMember)){ + } else if (message.content.toLowerCase().includes('discord.gg/') && !MessageTool.isStaff(message.member as Discord.GuildMember)){ const url = message.content.split(' ').find(x=>x.includes('discord.gg/')); const validInvite = await client.fetchInvite(url).catch(()=>undefined); if (validInvite && validInvite.guild?.id !== client.config.mainServer.id){ @@ -60,14 +60,14 @@ export default { CmdTrigger.TriggerTest(message, '!!_test-trigger'); if (message.type === 8 && message.channelId === GeneralChatID) message.channel.send({content: outgoingArrays.guildBoost[Math.floor(Math.random() * outgoingArrays.guildBoost.length)], allowedMentions: {parse: ['users']}}) - if (message.mentions.members.has('309373272594579456') && !client.isStaff(message.member)) message.reply('Please don\'t tag Daggerwin, read rule 14 in <#468846117405196289>'); - if (message.mentions.members.has('215497515934416896') && !client.isStaff(message.member) && message.type != 19) message.reply('Please don\'t tag Monster unless it\'s important!'); + if (message.mentions.members.has('309373272594579456') && !MessageTool.isStaff(message.member)) message.reply('Please don\'t tag Daggerwin, read rule 14 in <#468846117405196289>'); + if (message.mentions.members.has('215497515934416896') && !MessageTool.isStaff(message.member) && message.type != 19) message.reply('Please don\'t tag Monster unless it\'s important!'); if (incomingArrays.password.some(e=>message.content.toLowerCase().includes(e))) message.reply('Password and other details can be found in <#543494084363288637>'); if (incomingArrays.cantRead.some(e=>message.content.toLowerCase().includes(e))) message.reply('https://tenor.com/view/aristocats-george-pen-cap-meticulous-gif-5330931'); if (message.content.toLowerCase().includes('is daggerbot working')) message.reply('https://tenor.com/view/i-still-feel-alive-living-existing-active-singing-gif-14630579'); if (incomingArrays.deadChat.some(e=>message.content.toLowerCase().includes(e))) message.reply('https://cdn.discordapp.com/attachments/925589318276382720/1011333656167579849/F57G5ZS.png'); - if (Automoderator.scanMsg(message).includes('nawdic') && incomingArrays.theyBrokeIt.some(e=>Automoderator.scanMsg(message).includes(e)) && client.isStaff(message.member) && message.channelId !== '516344221452599306') message.reply({embeds: [new client.embed().setTitle('*Nawdic has done an oopsie*').setImage('https://c.tenor.com/JSj9ie_MD9kAAAAC/kopfsch%C3%BCtteln-an-kopf-fassen-oh-no.gif').setColor(client.config.embedColor)]}); - if (Automoderator.scanMsg(message).includes('monster') && incomingArrays.theyBrokeIt.some(e=>Automoderator.scanMsg(message).includes(e)) && client.isStaff(message.member) && message.channelId !== '516344221452599306') message.reply({embeds: [new client.embed().setTitle('*Monster has broken something*').setImage('https://media.tenor.com/ZIzIjb_wvEoAAAAC/face-palm.gif').setColor(client.config.embedColor)]}); + if (Automoderator.scanMsg(message).includes('nawdic') && incomingArrays.theyBrokeIt.some(e=>Automoderator.scanMsg(message).includes(e)) && MessageTool.isStaff(message.member) && message.channelId !== '516344221452599306') message.reply({embeds: [new client.embed().setTitle('*Nawdic has done an oopsie*').setImage('https://c.tenor.com/JSj9ie_MD9kAAAAC/kopfsch%C3%BCtteln-an-kopf-fassen-oh-no.gif').setColor(client.config.embedColor)]}); + if (Automoderator.scanMsg(message).includes('monster') && incomingArrays.theyBrokeIt.some(e=>Automoderator.scanMsg(message).includes(e)) && MessageTool.isStaff(message.member) && message.channelId !== '516344221452599306') message.reply({embeds: [new client.embed().setTitle('*Monster has broken something*').setImage('https://media.tenor.com/ZIzIjb_wvEoAAAAC/face-palm.gif').setColor(client.config.embedColor)]}); } } } diff --git a/src/events/messageUpdate.ts b/src/events/messageUpdate.ts index cefe0ff..597a32c 100644 --- a/src/events/messageUpdate.ts +++ b/src/events/messageUpdate.ts @@ -1,11 +1,12 @@ import Discord from 'discord.js'; import TClient from '../client.js'; +import MessageTool from '../helpers/MessageTool.js'; import {escapeCodeBlock} from 'discord.js'; export default { async run(client:TClient, oldMsg:Discord.Message, newMsg:Discord.Message){ if (!client.config.botSwitches.logs) return; if (oldMsg.guild?.id != client.config.mainServer.id || oldMsg.author === null || oldMsg?.author.bot || oldMsg.partial || newMsg.partial || !newMsg.member || ['548032776830582794', '541677709487505408', '949380187668242483'].includes(newMsg.channelId)) return; - if (await client.bannedWords._content.findOne({_id:newMsg.content.toLowerCase().replaceAll(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?\n]/g, ' ').split(' ')}) && (!client.isStaff(newMsg.member))) newMsg.delete(); + if (await client.bannedWords._content.findOne({_id:newMsg.content.toLowerCase().replaceAll(/[!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?\n]/g, ' ').split(' ')}) && (!MessageTool.isStaff(newMsg.member))) newMsg.delete(); if (newMsg.content === oldMsg.content) return; (client.channels.resolve(client.config.mainServer.channels.logs) as Discord.TextChannel).send({embeds: [new client.embed().setColor(client.config.embedColor).setTimestamp().setAuthor({name: `Author: ${oldMsg.author.username} (${oldMsg.author.id})`, iconURL: `${oldMsg.author.displayAvatarURL()}`}).setTitle('Message edited').setDescription(`<@${oldMsg.author.id}>\nOld content:\n\`\`\`\n${oldMsg.content.length < 1 ? '(Attachment)' : escapeCodeBlock(oldMsg.content.slice(0,2048))}\n\`\`\`\nNew content:\n\`\`\`\n${escapeCodeBlock(newMsg.content.slice(0,2048))}\`\`\`\nChannel: <#${oldMsg.channelId}>`)], components: [new Discord.ActionRowBuilder().addComponents(new Discord.ButtonBuilder().setStyle(5).setURL(`${oldMsg.url}`).setLabel('Jump to message'))]}); } diff --git a/src/events/ready.ts b/src/events/ready.ts index 351e016..6d81568 100644 --- a/src/events/ready.ts +++ b/src/events/ready.ts @@ -1,8 +1,10 @@ import Discord from 'discord.js'; import TClient from '../client.js'; - +import chalk from 'chalk'; export default { async run(client:TClient){ + const botSwitches = Object.entries(client.config.botSwitches).map(([k, v])=>`${chalk.yellow(k)}${chalk.black(':')} ${v}`).join('\n').replace(/true/g, chalk.green('true')).replace(/false/g, chalk.red('false')); + await client.guilds.fetch(client.config.mainServer.id).then(async guild=>{ await guild.members.fetch(); setInterval(()=>{ @@ -19,7 +21,7 @@ export default { } console.log(`${client.user.username} has logged into Discord API`); console.log(client.config.botSwitches, client.config.whitelistedServers); - (client.channels.resolve(client.config.mainServer.channels.bot_status) as Discord.TextChannel).send({content: `${client.user.username} is active`, embeds:[new client.embed().setColor(client.config.embedColor).setDescription(`\`\`\`json\n${Object.entries(client.config.botSwitches).map(x=>`${x[0]}: ${x[1]}`).join('\n')}\`\`\``)]}); + (client.channels.resolve(client.config.mainServer.channels.bot_status) as Discord.TextChannel).send({content: `**${client.user.username}** is active`, embeds:[new client.embed().setColor(client.config.embedColor).setDescription(`**\`\`\`ansi\n${botSwitches}\n\`\`\`**`)]}); console.timeEnd('Startup') } } diff --git a/src/funcs/Punish.ts b/src/funcs/Punish.ts index 0e3e6d0..599d348 100644 --- a/src/funcs/Punish.ts +++ b/src/funcs/Punish.ts @@ -1,9 +1,9 @@ import Discord from 'discord.js'; import TClient from '../client.js'; import Logger from '../helpers/Logger.js'; - +import MessageTool from '../helpers/MessageTool.js'; export default async(client:TClient, interaction: Discord.ChatInputCommandInteraction<'cached'>, type: string)=>{ - if (!client.isStaff(interaction.member as Discord.GuildMember)) return client.youNeedRole(interaction, 'dcmod'); + if (!MessageTool.isStaff(interaction.member as Discord.GuildMember)) return MessageTool.youNeedRole(interaction, 'dcmod'); const time = interaction.options.getString('time') ?? undefined; const reason = interaction.options.getString('reason') ?? 'Reason unspecified'; diff --git a/src/helpers/FAQStore.ts b/src/helpers/FAQStore.ts index 3178023..6b86ec4 100644 --- a/src/helpers/FAQStore.ts +++ b/src/helpers/FAQStore.ts @@ -1,10 +1,12 @@ import Discord from 'discord.js'; -import TClient from '../client.js'; import MessageTool from './MessageTool.js'; +import {Config} from 'src/typings/interfaces'; +import {readFileSync} from 'node:fs'; +const config:Config = JSON.parse(readFileSync('src/config.json', 'utf8')); export default class FAQStore { protected static readonly errorMsg:string = 'Failed to send the message, please report to **Toast** if it continues.'; - static async reply(client:TClient|null, interaction:Discord.ChatInputCommandInteraction, title:string|null, message:string, image:string|null, useEmbed:boolean=false) { - if (useEmbed) return interaction.reply({embeds: [MessageTool.embedStruct(client.config.embedColor, title, message, image)]}).catch(err=>interaction.reply(this.errorMsg+'\n'+err)) + static async reply(interaction:Discord.ChatInputCommandInteraction, title:string|null, message:string, image:string|null, useEmbed:boolean=false) { + if (useEmbed) return interaction.reply({embeds: [MessageTool.embedStruct(config.embedColor, title, message, image)]}).catch(err=>interaction.reply(this.errorMsg+'\n'+err)) else return interaction.reply(message).catch(err=>interaction.reply(this.errorMsg+'\n'+err)) } } diff --git a/src/helpers/MessageTool.ts b/src/helpers/MessageTool.ts index b9517ca..6dca941 100644 --- a/src/helpers/MessageTool.ts +++ b/src/helpers/MessageTool.ts @@ -1,4 +1,8 @@ import Discord from 'discord.js'; +import {readFileSync} from 'node:fs'; +import {Config} from 'src/typings/interfaces'; +const config:Config = JSON.parse(readFileSync('src/config.json', 'utf8')); +type RoleKeys = keyof typeof config.mainServer.roles; export default class MessageTool { static embedMusic(color:Discord.ColorResolvable, title:string, thumbnail?:string, footer?:string){ @@ -19,5 +23,11 @@ export default class MessageTool { static formatMention(mention:string, type:'user'|'channel'|'role'){ return `<@${type === 'role' ? '&' : type === 'channel' ? '#' : ''}${mention}>` } + static isStaff(guildMember:Discord.GuildMember){ + return config.mainServer.staffRoles.map((x:string)=>config.mainServer.roles[x]).some((x:string)=>guildMember.roles.cache.has(x)); + } + static youNeedRole(interaction:Discord.CommandInteraction, role:RoleKeys){ + return interaction.reply(`This command is restricted to ${this.formatMention(config.mainServer.roles[role], 'role')}`); + } } // I want to come up with better name instead of calling this file "MessageTool", but I am super bad at naming things. diff --git a/yarn.lock b/yarn.lock index eca2530..391cb83 100644 --- a/yarn.lock +++ b/yarn.lock @@ -697,6 +697,13 @@ __metadata: languageName: node linkType: hard +"chalk@npm:5.3.0": + version: 5.3.0 + resolution: "chalk@npm:5.3.0" + checksum: 623922e077b7d1e9dedaea6f8b9e9352921f8ae3afe739132e0e00c275971bdd331268183b2628cf4ab1727c45ea1f28d7e24ac23ce1db1eb653c414ca8a5a80 + languageName: node + linkType: hard + "chownr@npm:^2.0.0": version: 2.0.0 resolution: "chownr@npm:2.0.0" @@ -800,13 +807,14 @@ __metadata: "@types/node": 20.8.2 "@types/node-cron": 3.0.9 canvas: 2.11.2 + chalk: 5.3.0 discord-player: 6.6.4 discord.js: 14.13.0 libsodium-wrappers: 0.7.13 moment: 2.29.4 mongoose: 7.5.4 ms: 2.1.3 - node-cron: ^3.0.2 + node-cron: 3.0.2 prism-media: 1.3.5 redis: 4.6.10 systeminformation: 5.21.10 @@ -1744,7 +1752,7 @@ __metadata: languageName: node linkType: hard -"node-cron@npm:^3.0.2": +"node-cron@npm:3.0.2": version: 3.0.2 resolution: "node-cron@npm:3.0.2" dependencies: