From 132e3cc62b742c2a66ec3c1d34447efa54686fdf Mon Sep 17 00:00:00 2001 From: Anthony Berg Date: Sun, 19 Jan 2025 16:56:07 +0100 Subject: [PATCH] refactor(arabot): create separate functions for fetching and checking types of users, channels, etc --- src/commands/coordinators/access.ts | 15 +- src/commands/coordinators/anonymous.ts | 3 +- src/commands/coordinators/clear.ts | 5 +- src/commands/coordinators/private.ts | 101 +++++--- src/commands/economy/balance.ts | 6 +- src/commands/economy/daily.ts | 6 +- src/commands/economy/pay.ts | 30 +-- src/commands/fun/1984.ts | 5 +- src/commands/fun/cringe.ts | 5 +- src/commands/fun/happy.ts | 5 +- src/commands/fun/hug.ts | 5 +- src/commands/fun/kill.ts | 5 +- src/commands/fun/poke.ts | 5 +- src/commands/fun/sad.ts | 5 +- src/commands/fun/shrug.ts | 5 +- src/commands/mod/ban/ban.ts | 48 ++-- src/commands/mod/ban/tempBan.ts | 52 ++-- src/commands/mod/ban/unban.ts | 40 ++- src/commands/mod/diversity.ts | 97 ++++--- src/commands/mod/moveall.ts | 32 +-- src/commands/mod/rename.ts | 6 +- src/commands/mod/restriction/restrict.ts | 49 ++-- src/commands/mod/restriction/restrictLogs.ts | 65 ++--- src/commands/mod/restriction/restrictTools.ts | 54 ++-- src/commands/mod/restriction/unrestrict.ts | 111 ++++---- src/commands/mod/slowmode.ts | 9 +- src/commands/mod/softMute.ts | 14 +- src/commands/mod/sus.ts | 145 +++++------ src/commands/mod/vcMute.ts | 23 +- src/commands/mod/warning/deleteWarning.ts | 62 ++--- src/commands/mod/warning/warn.ts | 50 ++-- src/commands/mod/warning/warnings.ts | 21 +- src/commands/outreach/outreach.ts | 59 ++--- src/commands/roles/bookClub.ts | 19 +- src/commands/roles/debateHost.ts | 19 +- src/commands/roles/gameNightHost.ts | 19 +- src/commands/roles/guest.ts | 19 +- src/commands/roles/staff/mentor.ts | 19 +- src/commands/roles/staff/mod.ts | 19 +- src/commands/roles/staff/restrictedAccess.ts | 19 +- src/commands/roles/staff/stagehost.ts | 19 +- src/commands/roles/staff/trialMod.ts | 19 +- src/commands/roles/staff/trialVerifier.ts | 19 +- src/commands/roles/staff/verifier.ts | 19 +- src/commands/roles/verification/activist.ts | 21 +- src/commands/roles/verification/aravegan.ts | 21 +- src/commands/roles/verification/convinced.ts | 17 +- src/commands/roles/verification/plus.ts | 19 +- src/commands/roles/verification/trusted.ts | 17 +- src/commands/roles/verification/vegan.ts | 21 +- src/commands/roles/verification/vegcurious.ts | 21 +- src/commands/utils/count.ts | 8 +- src/commands/verification/verify.ts | 24 +- .../verification/verifyTimeoutRemove.ts | 13 +- src/commands/xp/rank.ts | 13 +- src/interaction-handlers/nonVeganAccess.ts | 55 ++-- src/interaction-handlers/welcome.ts | 45 ++-- src/listeners/ban/ban.ts | 32 +-- src/listeners/ban/unban.ts | 32 +-- src/listeners/counting.ts | 19 +- src/listeners/fixRoles.ts | 11 +- src/listeners/modMail.ts | 16 +- src/listeners/nonVeganAccess.ts | 21 +- src/listeners/ready.ts | 6 +- src/listeners/rolesJoinServer.ts | 115 ++++---- src/listeners/suggestions.ts | 20 +- src/listeners/vcMute.ts | 3 +- src/listeners/verification/joinVC.ts | 39 +-- src/listeners/verification/leaveVC.ts | 79 +++--- src/listeners/verification/start.ts | 91 ++++--- src/listeners/verification/trusted.ts | 6 +- src/listeners/verification/welcome.ts | 21 +- src/scheduled-tasks/messages/diversityMon.ts | 22 +- src/scheduled-tasks/messages/diversityWed.ts | 22 +- .../messages/restrictedMessage.ts | 22 +- src/scheduled-tasks/messages/standup.ts | 16 +- .../messages/verifyReminder.ts | 18 +- src/scheduled-tasks/tempBan.ts | 51 ++-- src/scheduled-tasks/verifyTimeout.ts | 30 +-- src/scheduled-tasks/verifyUnblock.ts | 28 +- src/utils/checker.ts | 11 +- src/utils/fetcher.ts | 245 ++++++++++++++++++ src/utils/typeChecking.ts | 37 +++ src/utils/verification.ts | 41 ++- 84 files changed, 1389 insertions(+), 1312 deletions(-) create mode 100644 src/utils/fetcher.ts create mode 100644 src/utils/typeChecking.ts diff --git a/src/commands/coordinators/access.ts b/src/commands/coordinators/access.ts index dae91d4..0398815 100644 --- a/src/commands/coordinators/access.ts +++ b/src/commands/coordinators/access.ts @@ -18,8 +18,10 @@ */ import { Command, RegisterBehavior } from '@sapphire/framework'; -import { ChannelType, MessageFlagsBitField } from 'discord.js'; +import { MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; +import { isRole, isUser } from '#utils/typeChecking'; +import { isTextChannel } from '@sapphire/discord.js-utilities'; export class AccessCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -91,7 +93,7 @@ export class AccessCommand extends Command { const role = interaction.options.getRole('role'); // Checks if all the variables are of the right type - if (user === null && role === null) { + if (!isUser(user) && !isRole(role)) { await interaction.reply({ content: 'Error fetching slash command data!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -100,7 +102,7 @@ export class AccessCommand extends Command { } // If user and role is provided, the return an error - if (user !== null && role !== null) { + if (isUser(user) && isRole(role)) { await interaction.reply({ content: 'You have entered a user and a role at the same time! Please only enter one at a time.', @@ -110,10 +112,7 @@ export class AccessCommand extends Command { } // Checks that the channel is a GuildText or GuildVoice, otherwise, return error - if ( - channel.type !== ChannelType.GuildText && - channel.type !== ChannelType.GuildVoice - ) { + if (!isTextChannel(channel) && !channel.isVoiceBased()) { await interaction.reply({ content: 'Please only select a text or voice channel!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -149,7 +148,7 @@ export class AccessCommand extends Command { } // Set permissions of voice channel - if (channel.type === ChannelType.GuildVoice) { + if (channel.isVoiceBased()) { switch (permission) { case 'add': await channel.permissionOverwrites.create(permId, { diff --git a/src/commands/coordinators/anonymous.ts b/src/commands/coordinators/anonymous.ts index 9436264..cb240e5 100644 --- a/src/commands/coordinators/anonymous.ts +++ b/src/commands/coordinators/anonymous.ts @@ -20,6 +20,7 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Message, MessageFlagsBitField } from 'discord.js'; import { ChannelType, TextChannel } from 'discord.js'; +import { isTextChannel } from '@sapphire/discord.js-utilities'; export class AnonymousCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -74,7 +75,7 @@ export class AnonymousCommand extends Command { } if (channel === null) { - if (interaction.channel === null) { + if (!isTextChannel(interaction.channel)) { await interaction.reply({ content: 'Error getting the channel!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/coordinators/clear.ts b/src/commands/coordinators/clear.ts index 795f951..f1e0867 100644 --- a/src/commands/coordinators/clear.ts +++ b/src/commands/coordinators/clear.ts @@ -19,6 +19,7 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Message, MessageFlagsBitField } from 'discord.js'; +import { isGuildBasedChannel } from '@sapphire/discord.js-utilities'; export class ClearCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -57,7 +58,7 @@ export class ClearCommand extends Command { const messages = interaction.options.getInteger('messages', true); const { channel } = interaction; - if (channel === null || channel.isDMBased()) { + if (!isGuildBasedChannel(channel)) { await interaction.reply({ content: 'Could not fetch channel!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -92,7 +93,7 @@ export class ClearCommand extends Command { const { channel } = message; - if (!channel.isTextBased() || channel.isDMBased()) { + if (!isGuildBasedChannel(channel)) { await message.react('❌'); await message.reply('Unsupported channel type!'); return; diff --git a/src/commands/coordinators/private.ts b/src/commands/coordinators/private.ts index 0130e6e..b448849 100644 --- a/src/commands/coordinators/private.ts +++ b/src/commands/coordinators/private.ts @@ -19,14 +19,8 @@ import { RegisterBehavior } from '@sapphire/framework'; import { Subcommand } from '@sapphire/plugin-subcommands'; +import { TextChannel, Snowflake, MessageFlagsBitField } from 'discord.js'; import { - Guild, - TextChannel, - Snowflake, - MessageFlagsBitField, -} from 'discord.js'; -import { - CategoryChannel, ChannelType, EmbedBuilder, GuildMember, @@ -34,6 +28,19 @@ import { time, } from 'discord.js'; import IDs from '#utils/ids'; +import { + isCategoryChannel, + isGuildBasedChannel, + isGuildMember, + isTextChannel, + isVoiceChannel, +} from '@sapphire/discord.js-utilities'; +import { + getCategoryChannel, + getGuildMember, + getVoiceChannel, +} from '#utils/fetcher'; +import { isUser } from '#utils/typeChecking'; export class PrivateCommand extends Subcommand { public constructor( @@ -110,11 +117,11 @@ export class PrivateCommand extends Subcommand { return; } - const member = guild.members.cache.get(user.id); - const mod = guild.members.cache.get(modUser.id); + const member = await getGuildMember(user.id, guild); + const mod = await getGuildMember(modUser.id, guild); // Checks if guildMember is null - if (member === undefined || mod === undefined) { + if (!isGuildMember(member) || !isGuildMember(mod)) { await interaction.editReply({ content: 'Error fetching users!', }); @@ -123,7 +130,7 @@ export class PrivateCommand extends Subcommand { const [name, coordinator] = this.getCoordinator(mod); - if (this.checkPrivate(member.id, coordinator, guild)) { + if (await this.checkPrivate(member.id, coordinator)) { await interaction.editReply({ content: 'A private channel already exists!', }); @@ -247,17 +254,17 @@ export class PrivateCommand extends Subcommand { }); // Checks if all the variables are of the right type - if (guild === null || channel === null) { + if (guild === null || !isGuildBasedChannel(channel)) { await interaction.editReply({ content: 'Error fetching user!', }); return; } - const mod = guild.members.cache.get(modUser.id); + const mod = await getGuildMember(modUser.id, guild); // Checks if guildMember is null - if (mod === undefined) { + if (!isGuildMember(mod)) { await interaction.editReply({ content: 'Error fetching users!', }); @@ -268,8 +275,8 @@ export class PrivateCommand extends Subcommand { const coordinator = coordinatorInfo[1]; let topic: string[]; - if (user === null) { - if (channel.type !== ChannelType.GuildText) { + if (!isUser(user)) { + if (!isTextChannel(channel)) { await interaction.editReply({ content: 'Please make sure you ran this command in the original private text channel!', @@ -296,10 +303,10 @@ export class PrivateCommand extends Subcommand { await channel.delete(); const vcId = topic[topic.indexOf(coordinator) + 1]; - const voiceChannel = guild.channels.cache.get(vcId); + const voiceChannel = await getVoiceChannel(vcId); if ( - voiceChannel !== undefined && + isVoiceChannel(voiceChannel) && voiceChannel.parentId === IDs.categories.private ) { await voiceChannel.delete(); @@ -307,9 +314,7 @@ export class PrivateCommand extends Subcommand { return; } - const category = guild.channels.cache.get(IDs.categories.private) as - | CategoryChannel - | undefined; + const category = await getCategoryChannel(IDs.categories.private); if (category === undefined) { await interaction.editReply({ @@ -318,26 +323,32 @@ export class PrivateCommand extends Subcommand { return; } - const textChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildText, + const textChannels = category.children.cache.filter((channel) => + isTextChannel(channel), ); - textChannels.forEach((c) => { - const textChannel = c as TextChannel; + + for (const c of textChannels) { + const channel = c[1]; + + if (!isTextChannel(channel)) { + continue; + } + // Checks if the channel topic has the user's snowflake - if (textChannel.topic?.includes(user?.id)) { - topic = textChannel.topic.split(' '); + if (channel.topic !== null && channel.topic.includes(user.id)) { + topic = channel.topic.split(' '); const vcId = topic[topic.indexOf(coordinator) + 1]; - const voiceChannel = guild.channels.cache.get(vcId); + const voiceChannel = await getVoiceChannel(vcId); if ( - voiceChannel !== undefined && + isVoiceChannel(voiceChannel) && voiceChannel.parentId === IDs.categories.private ) { - voiceChannel.delete(); + await voiceChannel.delete(); } - textChannel.delete(); + await channel.delete(); } - }); + } await interaction.editReply({ content: `Successfully deleted the channel for ${user}`, @@ -378,29 +389,35 @@ export class PrivateCommand extends Subcommand { return [name, id]; } - private checkPrivate(user: Snowflake, coordinator: string, guild: Guild) { - const category = guild.channels.cache.get(IDs.categories.private) as - | CategoryChannel - | undefined; + private async checkPrivate(user: Snowflake, coordinator: string) { + const category = await getCategoryChannel(IDs.categories.private); - if (category === undefined) { + if (!isCategoryChannel(category)) { return true; } const textChannels = category.children.cache.filter( (c) => c.type === ChannelType.GuildText, ); + let exists = false; - textChannels.forEach((c) => { - const textChannel = c as TextChannel; + + for (const c of textChannels) { + const channel = c[1]; + + if (!isTextChannel(channel)) { + continue; + } + // Checks if the channel topic has the user's snowflake if ( - textChannel.topic?.includes(user) && - textChannel.topic?.includes(coordinator) + channel.topic !== null && + channel.topic.includes(user) && + channel.topic.includes(coordinator) ) { exists = true; } - }); + } return exists; } } diff --git a/src/commands/economy/balance.ts b/src/commands/economy/balance.ts index e12571c..d00d70b 100644 --- a/src/commands/economy/balance.ts +++ b/src/commands/economy/balance.ts @@ -22,6 +22,8 @@ import { User, Guild, Message, MessageFlagsBitField } from 'discord.js'; import { updateUser } from '#utils/database/dbExistingUser'; import { getBalance } from '#utils/database/fun/economy'; import { EmbedBuilder } from 'discord.js'; +import { getGuildMember } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class BalanceCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -93,9 +95,9 @@ export class BalanceCommand extends Command { success: false, }; - const member = guild.members.cache.get(user.id); + const member = await getGuildMember(user.id, guild); - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Could not find your guild member!'; return info; } diff --git a/src/commands/economy/daily.ts b/src/commands/economy/daily.ts index c14b3ad..0604dcc 100644 --- a/src/commands/economy/daily.ts +++ b/src/commands/economy/daily.ts @@ -30,6 +30,8 @@ import { updateUser } from '#utils/database/dbExistingUser'; import { daily, getLastDaily } from '#utils/database/fun/economy'; import { EmbedBuilder } from 'discord.js'; import IDs from '#utils/ids'; +import { getGuildMember } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class DailyCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -114,9 +116,9 @@ export class DailyCommand extends Command { return info; } - const member = guild.members.cache.get(user.id); + const member = await getGuildMember(user.id, guild); - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Could not find your guild member!'; return info; } diff --git a/src/commands/economy/pay.ts b/src/commands/economy/pay.ts index b4841c8..84b860e 100644 --- a/src/commands/economy/pay.ts +++ b/src/commands/economy/pay.ts @@ -23,6 +23,8 @@ import { updateUser } from '#utils/database/dbExistingUser'; import { getBalance, transfer } from '#utils/database/fun/economy'; import { EmbedBuilder } from 'discord.js'; import IDs from '#utils/ids'; +import { getGuildMember, getTextBasedChannel } from '#utils/fetcher'; +import { isGuildMember, isTextChannel } from '@sapphire/discord.js-utilities'; export class BalanceCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -158,15 +160,15 @@ export class BalanceCommand extends Command { return info; } - const member = guild.members.cache.get(user.id); - const recipientMember = guild.members.cache.get(recipient.id); + const member = await getGuildMember(user.id, guild); + const recipientMember = await getGuildMember(recipient.id, guild); - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Could not find your guild member!'; return info; } - if (recipientMember === undefined) { + if (!isGuildMember(recipientMember)) { info.message = 'Could not find the user!'; return info; } @@ -200,22 +202,12 @@ export class BalanceCommand extends Command { info.embeds.push(embed); // Log the payment in the server - let logChannel = guild.channels.cache.get(IDs.channels.logs.economy); + const logChannel = await getTextBasedChannel(IDs.channels.logs.economy); - if (logChannel === undefined) { - const fetchLogChannel = await guild.channels - .fetch(IDs.channels.logs.economy) - .catch(() => undefined); - - if (fetchLogChannel === null || fetchLogChannel === undefined) { - this.container.logger.error('Pay: Could not fetch log channel'); - return info; - } else { - logChannel = fetchLogChannel; - } - } - - if (!logChannel.isSendable()) { + if (!isTextChannel(logChannel)) { + this.container.logger.error('Pay: Could not fetch log channel'); + return info; + } else if (!logChannel.isSendable()) { this.container.logger.error( 'Pay: the bot does not have permission to send in the log channel', ); diff --git a/src/commands/fun/1984.ts b/src/commands/fun/1984.ts index 139b48d..39e3e3c 100644 --- a/src/commands/fun/1984.ts +++ b/src/commands/fun/1984.ts @@ -18,9 +18,10 @@ */ import { Command, RegisterBehavior } from '@sapphire/framework'; -import { EmbedBuilder, GuildMember, MessageFlagsBitField } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import { N1984 } from '#utils/gifs'; import { addFunLog, countTotal } from '#utils/database/fun/fun'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class N1984Command extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -48,7 +49,7 @@ export class N1984Command extends Command { const { member } = interaction; // Type checks - if (!(member instanceof GuildMember)) { + if (!isGuildMember(member)) { await interaction.reply({ content: 'Failed to fetch your user on the bot!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/fun/cringe.ts b/src/commands/fun/cringe.ts index 5be1e7b..fb5ff28 100644 --- a/src/commands/fun/cringe.ts +++ b/src/commands/fun/cringe.ts @@ -18,9 +18,10 @@ */ import { Command, RegisterBehavior } from '@sapphire/framework'; -import { EmbedBuilder, GuildMember, MessageFlagsBitField } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import { Cringe } from '#utils/gifs'; import { addFunLog, countTotal } from '#utils/database/fun/fun'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class CringeCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -47,7 +48,7 @@ export class CringeCommand extends Command { const { member } = interaction; // Type check - if (!(member instanceof GuildMember)) { + if (!isGuildMember(member)) { await interaction.reply({ content: 'Failed to fetch your user on the bot!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/fun/happy.ts b/src/commands/fun/happy.ts index eafa307..9de593a 100644 --- a/src/commands/fun/happy.ts +++ b/src/commands/fun/happy.ts @@ -18,8 +18,9 @@ */ import { Command, RegisterBehavior } from '@sapphire/framework'; -import { EmbedBuilder, GuildMember, MessageFlagsBitField } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import { Happy } from '#utils/gifs'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class HappyCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -46,7 +47,7 @@ export class HappyCommand extends Command { const { member } = interaction; // Type checks - if (!(member instanceof GuildMember)) { + if (!isGuildMember(member)) { await interaction.reply({ content: 'Failed to fetch your user on the bot!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/fun/hug.ts b/src/commands/fun/hug.ts index e11348a..404902c 100644 --- a/src/commands/fun/hug.ts +++ b/src/commands/fun/hug.ts @@ -18,9 +18,10 @@ */ import { Command, RegisterBehavior } from '@sapphire/framework'; -import { EmbedBuilder, GuildMember, MessageFlagsBitField } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import { Hugs } from '#utils/gifs'; import { addFunLog, countTotal } from '#utils/database/fun/fun'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class HugCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -58,7 +59,7 @@ export class HugCommand extends Command { // Type Checks - if (!(hugger instanceof GuildMember)) { + if (!isGuildMember(hugger)) { await interaction.reply({ content: 'Failed to fetch your user on the bot!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/fun/kill.ts b/src/commands/fun/kill.ts index 3f45d15..8103895 100644 --- a/src/commands/fun/kill.ts +++ b/src/commands/fun/kill.ts @@ -18,9 +18,10 @@ */ import { Command, RegisterBehavior } from '@sapphire/framework'; -import { EmbedBuilder, GuildMember, MessageFlagsBitField } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import { Kill } from '#utils/gifs'; import { addFunLog, countTotal } from '#utils/database/fun/fun'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class KillCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -57,7 +58,7 @@ export class KillCommand extends Command { const sender = interaction.member; // Type checks - if (!(sender instanceof GuildMember)) { + if (!isGuildMember(sender)) { await interaction.reply({ content: 'Failed to fetch your user on the bot!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/fun/poke.ts b/src/commands/fun/poke.ts index da14032..65abfbf 100644 --- a/src/commands/fun/poke.ts +++ b/src/commands/fun/poke.ts @@ -18,9 +18,10 @@ */ import { Command, RegisterBehavior } from '@sapphire/framework'; -import { EmbedBuilder, GuildMember, MessageFlagsBitField } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import { Poke } from '#utils/gifs'; import { addFunLog, countTotal } from '#utils/database/fun/fun'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class PokeCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -57,7 +58,7 @@ export class PokeCommand extends Command { const sender = interaction.member; // Type checks - if (!(sender instanceof GuildMember)) { + if (!isGuildMember(sender)) { await interaction.reply({ content: 'Failed to fetch your user on the bot!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/fun/sad.ts b/src/commands/fun/sad.ts index 8c5aa96..e0551f8 100644 --- a/src/commands/fun/sad.ts +++ b/src/commands/fun/sad.ts @@ -18,8 +18,9 @@ */ import { Command, RegisterBehavior } from '@sapphire/framework'; -import { EmbedBuilder, GuildMember, MessageFlagsBitField } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import { Sad } from '#utils/gifs'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class SadCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -46,7 +47,7 @@ export class SadCommand extends Command { const { member } = interaction; // Type checks - if (!(member instanceof GuildMember)) { + if (!isGuildMember(member)) { await interaction.reply({ content: 'Failed to fetch your user on the bot!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/fun/shrug.ts b/src/commands/fun/shrug.ts index ab039b8..a5b90c0 100644 --- a/src/commands/fun/shrug.ts +++ b/src/commands/fun/shrug.ts @@ -18,8 +18,9 @@ */ import { Command, RegisterBehavior } from '@sapphire/framework'; -import { EmbedBuilder, GuildMember, MessageFlagsBitField } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import { Shrug } from '#utils/gifs'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class ShrugCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -46,7 +47,7 @@ export class ShrugCommand extends Command { const { member } = interaction; // Type checks - if (!(member instanceof GuildMember)) { + if (!isGuildMember(member)) { await interaction.reply({ content: 'Failed to fetch your user on the bot!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/mod/ban/ban.ts b/src/commands/mod/ban/ban.ts index e22ecd9..8bc7029 100644 --- a/src/commands/mod/ban/ban.ts +++ b/src/commands/mod/ban/ban.ts @@ -33,6 +33,12 @@ import { checkTempBan, removeTempBan, } from '#utils/database/moderation/tempBan'; +import { getGuildMember, getTextBasedChannel, getUser } from '#utils/fetcher'; +import { isUser } from '#utils/typeChecking'; +import { + isGuildMember, + isTextBasedChannel, +} from '@sapphire/discord.js-utilities'; export class BanCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -150,17 +156,19 @@ export class BanCommand extends Command { success: false, }; - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); - if (user === undefined) { - user = (await guild.client.users.fetch(userId)) as User; + if (!isUser(user)) { + info.message = + 'The user does not exist! (The user provided is probably wrong, or their account has been deleted.)'; + return info; } // Gets mod's GuildMember - const mod = guild.members.cache.get(modId); + const mod = await getGuildMember(modId, guild); // Checks if guildMember is null - if (mod === undefined) { + if (!isGuildMember(mod)) { info.message = 'Error fetching mod!'; return info; } @@ -174,13 +182,9 @@ export class BanCommand extends Command { await updateUser(mod); // Gets guildMember - let member = guild.members.cache.get(userId); + const member = await getGuildMember(userId, guild); - if (member === undefined) { - member = await guild.members.fetch(userId).catch(() => undefined); - } - - if (member !== undefined) { + if (isGuildMember(member)) { // Checks if the user is not restricted if (member.roles.cache.has(IDs.roles.vegan.vegan)) { info.message = 'You need to restrict the user first!'; @@ -214,24 +218,14 @@ export class BanCommand extends Command { info.success = true; // Log the ban - let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted); + const logChannel = await getTextBasedChannel(IDs.channels.logs.restricted); - if (logChannel === undefined) { - const fetchLogChannel = await guild.channels.fetch( - IDs.channels.logs.restricted, - ); + if (!isTextBasedChannel(logChannel)) { + this.container.logger.error('Ban: Could not fetch log channel'); + info.message = `${user} has been banned. This hasn't been logged in a text channel as log channel could not be found`; - if (fetchLogChannel === null || fetchLogChannel === undefined) { - this.container.logger.error('Ban: Could not fetch log channel'); - info.message = `${user} has been banned. This hasn't been logged in a text channel as log channel could not be found`; - - return info; - } else { - logChannel = fetchLogChannel; - } - } - - if (!logChannel.isSendable()) { + return info; + } else if (!logChannel.isSendable()) { this.container.logger.error( 'Ban: The bot does not have permission to send in the logs channel!', ); diff --git a/src/commands/mod/ban/tempBan.ts b/src/commands/mod/ban/tempBan.ts index c6cfc4f..f72f08c 100644 --- a/src/commands/mod/ban/tempBan.ts +++ b/src/commands/mod/ban/tempBan.ts @@ -24,6 +24,12 @@ import { EmbedBuilder, Message } from 'discord.js'; import IDs from '#utils/ids'; import { addTempBan, checkTempBan } from '#utils/database/moderation/tempBan'; import { addEmptyUser, updateUser } from '#utils/database/dbExistingUser'; +import { getGuildMember, getTextBasedChannel, getUser } from '#utils/fetcher'; +import { isUser } from '#utils/typeChecking'; +import { + isGuildMember, + isTextBasedChannel, +} from '@sapphire/discord.js-utilities'; export class TempBanCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -203,17 +209,19 @@ export class TempBanCommand extends Command { const banLength = new DurationFormatter().format(time.offset); - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); - if (user === undefined) { - user = (await guild.client.users.fetch(userId)) as User; + if (!isUser(user)) { + info.message = + 'The user does not exist! (The user provided is probably wrong, or their account has been deleted.)'; + return info; } // Gets mod's GuildMember - const mod = guild.members.cache.get(modId); + const mod = await getGuildMember(modId, guild); // Checks if guildMember is null - if (mod === undefined) { + if (!isGuildMember(mod)) { info.message = 'Error fetching mod!'; return info; } @@ -227,13 +235,9 @@ export class TempBanCommand extends Command { await updateUser(mod); // Gets guildMember - let member = guild.members.cache.get(userId); + const member = await getGuildMember(userId, guild); - if (member === undefined) { - member = await guild.members.fetch(userId).catch(() => undefined); - } - - if (member !== undefined) { + if (isGuildMember(member)) { // Checks if the user is not restricted if (member.roles.cache.has(IDs.roles.vegan.vegan)) { info.message = 'You need to restrict the user first!'; @@ -275,27 +279,17 @@ export class TempBanCommand extends Command { info.success = true; // Log the ban - let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted); + const logChannel = await getTextBasedChannel(IDs.channels.logs.restricted); - if (logChannel === undefined) { - const fetchLogChannel = await guild.channels.fetch( - IDs.channels.logs.restricted, - ); + if (!isTextBasedChannel(logChannel)) { + this.container.logger.error('Temp Ban: Could not fetch log channel'); - if (fetchLogChannel === null || fetchLogChannel === undefined) { - this.container.logger.error('Temp Ban: Could not fetch log channel'); + info.message = + `${user} has been temporarily banned for ${banLength}. ` + + "This hasn't been logged in a text channel as log channel could not be found"; - info.message = - `${user} has been temporarily banned for ${banLength}. ` + - "This hasn't been logged in a text channel as log channel could not be found"; - - return info; - } else { - logChannel = fetchLogChannel; - } - } - - if (!logChannel.isSendable()) { + return info; + } else if (!logChannel.isSendable()) { this.container.logger.error( 'Temp Ban: The bot does not have permission to send in the logs channel!', ); diff --git a/src/commands/mod/ban/unban.ts b/src/commands/mod/ban/unban.ts index 4bc5d7f..bee8cd8 100644 --- a/src/commands/mod/ban/unban.ts +++ b/src/commands/mod/ban/unban.ts @@ -34,6 +34,12 @@ import { removeTempBan, } from '#utils/database/moderation/tempBan'; import { addEmptyUser, addExistingUser } from '#utils/database/dbExistingUser'; +import { getGuildMember, getTextBasedChannel, getUser } from '#utils/fetcher'; +import { + isGuildMember, + isTextBasedChannel, +} from '@sapphire/discord.js-utilities'; +import { isNullish } from '@sapphire/utilities'; export class UnbanCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -123,10 +129,10 @@ export class UnbanCommand extends Command { }; // Gets mod's GuildMember - const mod = guild.members.cache.get(modId); + const mod = await getGuildMember(modId, guild); // Checks if guildMember is null - if (mod === undefined) { + if (!isGuildMember(mod)) { info.message = 'Error fetching mod!'; return info; } @@ -134,14 +140,11 @@ export class UnbanCommand extends Command { // Check if mod is in database await addExistingUser(mod); - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); if (user === undefined) { - user = await guild.client.users.fetch(userId); - if (user === undefined) { - info.message = 'Could not fetch the user!'; - return info; - } + info.message = 'Could not fetch the user!'; + return info; } let dbBan = await checkBan(userId); @@ -161,7 +164,7 @@ export class UnbanCommand extends Command { } let { reason } = ban; - if (reason === null || reason === undefined) { + if (isNullish(reason)) { reason = ''; } @@ -191,23 +194,14 @@ export class UnbanCommand extends Command { info.success = true; // Log unban - let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted); + let logChannel = await getTextBasedChannel(IDs.channels.logs.restricted); - if (logChannel === undefined) { - const fetchLogChannel = await guild.channels.fetch( - IDs.channels.logs.restricted, - ); + if (!isTextBasedChannel(logChannel)) { + this.container.logger.error('Unban Error: Could not fetch log channel'); + info.message = `${user} has been unbanned. This hasn't been logged in a text channel as log channel could not be found`; - if (fetchLogChannel === null || fetchLogChannel === undefined) { - this.container.logger.error('Unban Error: Could not fetch log channel'); - info.message = `${user} has been unbanned. This hasn't been logged in a text channel as log channel could not be found`; - - return info; - } else { - logChannel = fetchLogChannel; - } + return info; } - if (!logChannel.isSendable()) { this.container.logger.error( 'Unban: The bot does not have permission to send in the logs channel!', diff --git a/src/commands/mod/diversity.ts b/src/commands/mod/diversity.ts index 3b7414d..ef5b8ce 100644 --- a/src/commands/mod/diversity.ts +++ b/src/commands/mod/diversity.ts @@ -22,14 +22,20 @@ import { Args, container, RegisterBehavior } from '@sapphire/framework'; import { Subcommand } from '@sapphire/plugin-subcommands'; import { - ChannelType, GuildMember, Message, MessageFlagsBitField, PermissionsBitField, } from 'discord.js'; -import type { TextChannel, Snowflake } from 'discord.js'; +import type { Snowflake } from 'discord.js'; import IDs from '#utils/ids'; +import { getGuildMember, getRole, getTextBasedChannel } from '#utils/fetcher'; +import { + isGuildMember, + isTextChannel, + isThreadChannel, +} from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class DiversityCommand extends Subcommand { public constructor( @@ -90,30 +96,11 @@ export class DiversityCommand extends Subcommand { // Command run public async toggleOpen(interaction: Subcommand.ChatInputCommandInteraction) { - // Check if guild is not null - if (interaction.guild === null) { - await interaction.reply({ - content: 'Guild not found!', - flags: MessageFlagsBitField.Flags.Ephemeral, - withResponse: true, - }); - return; - } - // Get the channel - const channel = interaction.guild.channels.cache.get(interaction.channelId); - // Check if channel is not undefined - if (channel === undefined) { - await interaction.reply({ - content: 'Channel not found!', - flags: MessageFlagsBitField.Flags.Ephemeral, - withResponse: true, - }); - return; - } + const channel = await getTextBasedChannel(interaction.channelId); // Check if channel is text - if (channel.type !== ChannelType.GuildText) { + if (!isTextChannel(channel)) { await interaction.reply({ content: 'Channel is not a text channel!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -122,9 +109,6 @@ export class DiversityCommand extends Subcommand { return; } - // Converts GuildBasedChannel to TextChannel - const channelText = channel as TextChannel; - // Check if the command was run in the diversity section if (channel.parentId !== IDs.categories.diversity) { await interaction.reply({ @@ -141,7 +125,7 @@ export class DiversityCommand extends Subcommand { .has([PermissionsBitField.Flags.SendMessages]); // Toggle send message in channel - await channelText.permissionOverwrites.edit(IDs.roles.vegan.vegan, { + await channel.permissionOverwrites.edit(IDs.roles.vegan.vegan, { SendMessages: !open, }); @@ -156,28 +140,46 @@ export class DiversityCommand extends Subcommand { ) { // TODO add database updates // Get the arguments - const user = interaction.options.getUser('user'); + const user = interaction.options.getUser('user', true); const mod = interaction.member; const { guild } = interaction; // Checks if all the variables are of the right type - if (user === null || guild === null || mod === null) { + if (guild === null) { await interaction.reply({ - content: 'Error fetching user!', + content: 'Error fetching the guild!', flags: MessageFlagsBitField.Flags.Ephemeral, withResponse: true, }); return; } - // Gets guildMember whilst removing the ability of each other variables being null - const guildMember = guild.members.cache.get(user.id); - const diversity = guild.roles.cache.get(IDs.roles.staff.diversity); - - // Checks if guildMember is null - if (guildMember === undefined || diversity === undefined) { + if (!isGuildMember(mod)) { await interaction.reply({ - content: 'Error fetching user!', + content: 'Error fetching your user!', + flags: MessageFlagsBitField.Flags.Ephemeral, + withResponse: true, + }); + return; + } + + const member = await getGuildMember(user.id, guild); + const diversity = await getRole(IDs.roles.staff.diversity, guild); + + // Checks if the member was found + if (!isGuildMember(member)) { + await interaction.reply({ + content: 'Error fetching the user!', + flags: MessageFlagsBitField.Flags.Ephemeral, + withResponse: true, + }); + return; + } + + // Checks if the role was found + if (!isRole(diversity)) { + await interaction.reply({ + content: 'Error fetching the diversity role!', flags: MessageFlagsBitField.Flags.Ephemeral, withResponse: true, }); @@ -185,10 +187,10 @@ export class DiversityCommand extends Subcommand { } // Checks if the user has Diversity and to give them or remove them based on if they have it - if (guildMember.roles.cache.has(IDs.roles.staff.diversity)) { + if (member.roles.cache.has(IDs.roles.staff.diversity)) { // Remove the Diversity role from the user - await guildMember.roles.remove(diversity); - await this.threadManager(guildMember.id, false); + await member.roles.remove(diversity); + await this.threadManager(member.id, false); await interaction.reply({ content: `Removed the ${diversity.name} role from ${user}`, withResponse: true, @@ -196,8 +198,8 @@ export class DiversityCommand extends Subcommand { return; } // Add Diversity Team role to the user - await guildMember.roles.add(diversity); - await this.threadManager(guildMember.id, true); + await member.roles.add(diversity); + await this.threadManager(member.id, true); await interaction.reply({ content: `Gave ${user} the ${diversity.name} role!`, withResponse: true, @@ -220,7 +222,7 @@ export class DiversityCommand extends Subcommand { const mod = message.member; - if (mod === null) { + if (!isGuildMember(mod)) { await message.react('❌'); await message.reply( 'Diversity coordinator not found! Try again or contact a developer!', @@ -236,9 +238,9 @@ export class DiversityCommand extends Subcommand { return; } - const diversity = guild.roles.cache.get(IDs.roles.staff.diversity); + const diversity = await getRole(IDs.roles.staff.diversity, guild); - if (diversity === undefined) { + if (!isRole(diversity)) { await message.react('❌'); await message.reply('Role not found! Try again or contact a developer!'); return; @@ -271,11 +273,8 @@ export class DiversityCommand extends Subcommand { const thread = await container.client.channels.fetch( IDs.channels.diversity.diversity, ); - if (thread === null) { - return; - } - if (!thread.isThread()) { + if (!isThreadChannel(thread)) { return; } diff --git a/src/commands/mod/moveall.ts b/src/commands/mod/moveall.ts index 95992b9..1ce13cc 100644 --- a/src/commands/mod/moveall.ts +++ b/src/commands/mod/moveall.ts @@ -23,6 +23,11 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Message, MessageFlagsBitField } from 'discord.js'; import { ChannelType } from 'discord.js'; +import { + isGuildMember, + isVoiceBasedChannel, +} from '@sapphire/discord.js-utilities'; +import { getVoiceBasedChannel } from '#utils/fetcher'; export class MoveAllCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -83,40 +88,31 @@ export class MoveAllCommand extends Command { return; } - if (member === null) { + if (!isGuildMember(member)) { await interaction.editReply({ content: 'Error fetching your user', }); return; } - const mod = guild.members.cache.get(member.user.id); - - if (mod === undefined) { - await interaction.editReply({ - content: 'Error fetching user from guild', - }); - return; - } - - if (mod.voice.channelId === null) { + if (member.voice.channelId === null) { await interaction.editReply({ content: 'You need to be in a voice channel to run this command!', }); return; } - const voice = guild.channels.cache.get(mod.voice.channelId); + const voice = await getVoiceBasedChannel(member.voice.channelId); - if (voice === undefined || !voice.isVoiceBased()) { + if (!isVoiceBasedChannel(voice)) { await interaction.editReply({ content: 'Error fetching your current voice channel!', }); return; } - voice.members.forEach((memberVC) => { - memberVC.voice.setChannel(channel.id); + voice.members.forEach((vcMember) => { + vcMember.voice.setChannel(channel.id); }); await interaction.editReply({ @@ -137,7 +133,7 @@ export class MoveAllCommand extends Command { const mod = message.member; const { guild } = message; - if (mod === null) { + if (!isGuildMember(mod)) { await message.react('❌'); await message.reply('Could not find your user!'); return; @@ -157,9 +153,9 @@ export class MoveAllCommand extends Command { return; } - const voice = guild.channels.cache.get(mod.voice.channelId); + const voice = await getVoiceBasedChannel(mod.voice.channelId); - if (voice === undefined || !voice.isVoiceBased()) { + if (!isVoiceBasedChannel(voice)) { await message.react('❌'); await message.reply('Could not fetch current voice channel!'); return; diff --git a/src/commands/mod/rename.ts b/src/commands/mod/rename.ts index 39832a5..f25d0c3 100644 --- a/src/commands/mod/rename.ts +++ b/src/commands/mod/rename.ts @@ -19,6 +19,8 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { GuildMember, Message, MessageFlagsBitField } from 'discord.js'; +import { getGuildMember } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class RenameUserCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -75,10 +77,10 @@ export class RenameUserCommand extends Command { } // Gets guildMember whilst removing the ability of each other variables being null - const member = guild.members.cache.get(user.id); + const member = await getGuildMember(user.id, guild); // Checks if guildMember is null - if (member === undefined) { + if (!isGuildMember(member)) { await interaction.reply({ content: 'Error fetching user!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index 2cfb8c3..457daf2 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -40,6 +40,11 @@ import { import { restrict, checkActive } from '#utils/database/moderation/restriction'; import { randint } from '#utils/maths'; import { blockedRolesAfterRestricted } from '#utils/blockedRoles'; +import { getGuildMember, getTextBasedChannel, getUser } from '#utils/fetcher'; +import { + isGuildMember, + isTextBasedChannel, +} from '@sapphire/discord.js-utilities'; export async function restrictRun( userId: Snowflake, @@ -53,21 +58,18 @@ export async function restrictRun( success: false, }; - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); if (user === undefined) { - user = await guild.client.users.fetch(userId).catch(() => undefined); - if (user === undefined) { - info.message = 'Error fetching user'; - return info; - } + info.message = 'Error fetching user'; + return info; } // Gets mod's GuildMember - const mod = guild.members.cache.get(modId); + const mod = await getGuildMember(modId, guild); // Checks if guildMember is null - if (mod === undefined) { + if (!isGuildMember(mod)) { info.message = 'Error fetching mod'; return info; } @@ -81,17 +83,13 @@ export async function restrictRun( } // Gets guildMember - let member = guild.members.cache.get(userId); - - if (member === undefined) { - member = await guild.members.fetch(userId).catch(() => undefined); - } + const member = await getGuildMember(userId, guild); const restrictRoles = IDs.roles.restrictions.restricted; let section = tolerance ? randint(3, 4) : randint(1, 2); - if (member !== undefined) { + if (isGuildMember(member)) { // Checks if the user is not restricted if (member.roles.cache.hasAny(...restrictRoles)) { info.message = `${member} is already restricted!`; @@ -219,7 +217,7 @@ export async function restrictRun( } } - if (member !== undefined && member.voice.channelId !== null) { + if (isGuildMember(member) && member.voice.channelId !== null) { await member.voice.disconnect(); } @@ -243,23 +241,14 @@ export async function restrictRun( await user.send({ embeds: [dmEmbed] }).catch(() => {}); // Log the ban - let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted); + const logChannel = await getTextBasedChannel(IDs.channels.logs.restricted); - if (logChannel === undefined) { - const fetchLogChannel = await guild.channels.fetch( - IDs.channels.logs.restricted, - ); - if (fetchLogChannel === null || fetchLogChannel === undefined) { - container.logger.error('Restrict: Could not fetch log channel'); - info.message = `Restricted ${user} but could not find the log channel. This has been logged to the database.`; + if (!isTextBasedChannel(logChannel)) { + container.logger.error('Restrict: Could not fetch log channel'); + info.message = `Restricted ${user} but could not find the log channel. This has been logged to the database.`; - return info; - } else { - logChannel = fetchLogChannel; - } - } - - if (!logChannel.isSendable()) { + return info; + } else if (!logChannel.isSendable()) { container.logger.error( 'Restrict: The bot does not have permission to send in the logs channel!', ); diff --git a/src/commands/mod/restriction/restrictLogs.ts b/src/commands/mod/restriction/restrictLogs.ts index 467387a..f3e5936 100644 --- a/src/commands/mod/restriction/restrictLogs.ts +++ b/src/commands/mod/restriction/restrictLogs.ts @@ -18,11 +18,14 @@ */ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; -import { ChannelType, EmbedBuilder, MessageFlagsBitField } from 'discord.js'; -import type { Message, TextChannel, Guild, Snowflake } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; +import type { Message, Guild, Snowflake } from 'discord.js'; import IDs from '#utils/ids'; import { getRestrictions } from '#utils/database/moderation/restriction'; import { checkStaff } from '#utils/checker'; +import { isUser } from '#utils/typeChecking'; +import { isGuildMember, isTextChannel } from '@sapphire/discord.js-utilities'; +import { getGuildMember, getUser } from '#utils/fetcher'; export class RestrictLogsCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -56,7 +59,7 @@ export class RestrictLogsCommand extends Command { public async chatInputRun(interaction: Command.ChatInputCommandInteraction) { // Get the arguments const user = interaction.options.getUser('user'); - let { channel } = interaction; + const { channel } = interaction; const { guild } = interaction; // Checks if all the variables are of the right type @@ -71,26 +74,28 @@ export class RestrictLogsCommand extends Command { let userId: Snowflake | null = null; - if (user !== undefined && user !== null) { + if (isUser(user)) { userId = user.id; } const staffChannel = checkStaff(channel); if (staffChannel) { - channel = channel as TextChannel; - + // Checking Channel topic for Snowflake if (userId === null) { let topic: string[]; - if (channel.parentId === IDs.categories.modMail) { + if ( + isTextChannel(channel) && + channel.parentId === IDs.categories.modMail && + channel.topic !== null + ) { // Checks if the channel topic has the user's snowflake - if (channel.topic !== null) { - topic = channel.topic.split(' '); - userId = topic[2]; - } + topic = channel.topic.split(' '); + userId = topic[2]; } } + // If no Snowflake was provided/found if (userId === null) { await interaction.reply({ content: 'User could not be found or was not provided!', @@ -130,10 +135,11 @@ export class RestrictLogsCommand extends Command { return; } + // Attempting to get the user's Snowflake from the channel topic. if (userId === null) { const { channel } = message; - if (channel.type !== ChannelType.GuildText) { + if (!isTextChannel(channel)) { await message.react('❌'); await message.reply('User was not provided!'); return; @@ -141,13 +147,13 @@ export class RestrictLogsCommand extends Command { let topic: string[]; - if (channel.parentId === IDs.categories.modMail) { - // Checks if the channel topic has the user's snowflake - if (channel.topic !== null) { - topic = channel.topic.split(' '); - // eslint-disable-next-line prefer-destructuring - userId = topic[2]; - } + // Checks if the channel topic has the user's snowflake + if ( + channel.parentId === IDs.categories.modMail && + channel.topic !== null + ) { + topic = channel.topic.split(' '); + userId = topic[2]; } } @@ -172,14 +178,12 @@ export class RestrictLogsCommand extends Command { success: false, }; - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); - if (user === undefined) { - user = await guild.client.users.fetch(userId).catch(() => undefined); - if (user === undefined) { - info.message = 'Error fetching user'; - return info; - } + if (!isUser(user)) { + info.message = + 'Error fetching user. (You probably provided an incorrect user.)'; + return info; } const restrictions = await getRestrictions(userId); @@ -204,14 +208,15 @@ export class RestrictLogsCommand extends Command { ) { // Get mod names let restMod = restrictions[i].modId; - const restModMember = guild.members.cache.get(restMod); - if (restModMember !== undefined) { + const restModMember = await getGuildMember(restMod, guild); + if (isGuildMember(restModMember)) { restMod = restModMember.displayName; } + let endRestMod = restrictions[i].endModId; if (endRestMod !== null) { - const endRestModMember = guild.members.cache.get(endRestMod); - if (endRestModMember !== undefined) { + const endRestModMember = await getGuildMember(endRestMod, guild); + if (isGuildMember(endRestModMember)) { endRestMod = endRestModMember.displayName; } } diff --git a/src/commands/mod/restriction/restrictTools.ts b/src/commands/mod/restriction/restrictTools.ts index d71292f..59fddf8 100644 --- a/src/commands/mod/restriction/restrictTools.ts +++ b/src/commands/mod/restriction/restrictTools.ts @@ -19,9 +19,15 @@ import { RegisterBehavior } from '@sapphire/framework'; import { Subcommand } from '@sapphire/plugin-subcommands'; -import { MessageFlagsBitField, TextChannel } from 'discord.js'; -import { CategoryChannel, ChannelType } from 'discord.js'; +import { MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; +import { isUser } from '#utils/typeChecking'; +import { + isCategoryChannel, + isTextChannel, + isVoiceChannel, +} from '@sapphire/discord.js-utilities'; +import { getCategoryChannel, getVoiceChannel } from '#utils/fetcher'; export class RestrictToolsCommand extends Subcommand { public constructor( @@ -92,8 +98,8 @@ export class RestrictToolsCommand extends Subcommand { let topic: string[]; - if (user === null) { - if (channel.type !== ChannelType.GuildText) { + if (!isUser(user)) { + if (!isTextChannel(channel)) { await interaction.editReply({ content: 'Please make sure you ran this command in the original restricted text channel!', @@ -132,10 +138,10 @@ export class RestrictToolsCommand extends Subcommand { await channel.delete(); const vcId = topic[3]; - const voiceChannel = guild.channels.cache.get(vcId); + const voiceChannel = await getVoiceChannel(vcId); if ( - voiceChannel !== undefined && + isVoiceChannel(voiceChannel) && voiceChannel.parentId === IDs.categories.restricted ) { await voiceChannel.delete(); @@ -144,55 +150,45 @@ export class RestrictToolsCommand extends Subcommand { return; } - const category = guild.channels.cache.get(IDs.categories.restricted); + const category = await getCategoryChannel(IDs.categories.restricted); - if (!(category instanceof CategoryChannel)) { + if (!isCategoryChannel(category)) { await interaction.editReply({ content: 'Could not find category!', }); return; } - const textChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildText, + const textChannels = category.children.cache.filter((channel) => + isTextChannel(channel), ); - for (const channel of textChannels) { - const textChannel = channel[1]; + for (const c of textChannels) { + const channel = c[1]; // Checks that the channel is a text channel - if (!(textChannel instanceof TextChannel)) { + if (!isTextChannel(channel)) { continue; } // Checks that the channel has a topic - if (textChannel.topic === null) { + if (channel.topic === null) { continue; } // Checks if the channel topic has the user's snowflake - if (textChannel.topic.includes(user.id)) { - topic = textChannel.topic.split(' '); + if (channel.topic.includes(user.id)) { + topic = channel.topic.split(' '); const vcId = topic[topic.indexOf(user.id) + 1]; - let voiceChannel = guild.channels.cache.get(vcId); - - if (voiceChannel === undefined) { - const fetchVoiceChannel = await guild.channels - .fetch(vcId) - .catch(() => undefined); - - if (fetchVoiceChannel !== null && fetchVoiceChannel !== undefined) { - voiceChannel = fetchVoiceChannel; - } - } + const voiceChannel = await getVoiceChannel(vcId); if ( - voiceChannel !== undefined && + isVoiceChannel(voiceChannel) && voiceChannel.parentId === IDs.categories.restricted ) { await voiceChannel.delete(); } - await textChannel.delete(); + await channel.delete(); } } diff --git a/src/commands/mod/restriction/unrestrict.ts b/src/commands/mod/restriction/unrestrict.ts index 6f6599e..0a13f24 100644 --- a/src/commands/mod/restriction/unrestrict.ts +++ b/src/commands/mod/restriction/unrestrict.ts @@ -19,13 +19,7 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import type { Guild, Message, Snowflake, User } from 'discord.js'; -import { - CategoryChannel, - ChannelType, - EmbedBuilder, - MessageFlagsBitField, - TextChannel, -} from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { addExistingUser, fetchRoles } from '#utils/database/dbExistingUser'; import { @@ -33,6 +27,21 @@ import { unRestrict, unRestrictLegacy, } from '#utils/database/moderation/restriction'; +import { + getCategoryChannel, + getGuildMember, + getTextBasedChannel, + getUser, + getVoiceChannel, +} from '#utils/fetcher'; +import { isUser } from '#utils/typeChecking'; +import { + isCategoryChannel, + isGuildMember, + isTextBasedChannel, + isTextChannel, + isVoiceChannel, +} from '@sapphire/discord.js-utilities'; export class UnRestrictCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -138,22 +147,18 @@ export class UnRestrictCommand extends Command { runInVeganRestrict: false, }; - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); - if (user === undefined) { - user = await guild.client.users.fetch(userId).catch(() => undefined); - - if (user === undefined) { - info.message = 'Error fetching user'; - return info; - } + if (!isUser(user)) { + info.message = 'Error fetching user'; + return info; } // Gets mod's GuildMember - const mod = guild.members.cache.get(modId); + const mod = await getGuildMember(modId, guild); // Checks if guildMember is null - if (mod === undefined) { + if (!isGuildMember(mod)) { info.message = 'Error fetching mod'; return info; } @@ -162,16 +167,11 @@ export class UnRestrictCommand extends Command { await addExistingUser(mod); // Gets guildMember - let member = guild.members.cache.get(userId); + const member = await getGuildMember(userId, guild); - if (member === undefined) { - member = await guild.members.fetch(userId).catch(() => undefined); - - if (member === undefined) { - info.message = - "Can't unrestrict the user as they are not on this server"; - return info; - } + if (!isGuildMember(member)) { + info.message = "Can't unrestrict the user as they are not on this server"; + return info; } // Check if user is in database @@ -210,9 +210,9 @@ export class UnRestrictCommand extends Command { // Remove vegan restrict channels if (member.roles.cache.has(IDs.roles.vegan.vegan)) { - const category = guild.channels.cache.get(IDs.categories.restricted); + const category = await getCategoryChannel(IDs.categories.restricted); - if (!(category instanceof CategoryChannel)) { + if (!isCategoryChannel(category)) { info.message = 'Could not find the restricted category! The channels will have to be deleted manually.'; return info; @@ -220,52 +220,42 @@ export class UnRestrictCommand extends Command { let topic: string[]; - const textChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildText, + const textChannels = category.children.cache.filter((channel) => + isTextChannel(channel), ); - for (const channel of textChannels) { - const textChannel = channel[1]; + for (const c of textChannels) { + const channel = c[1]; // Checks that the channel is a text channel - if (!(textChannel instanceof TextChannel)) { + if (!isTextChannel(channel)) { continue; } // Checks that the channel has a topic - if (textChannel.topic === null) { + if (channel.topic === null) { continue; } // Checks if the channel topic has the user's snowflake - if (textChannel.topic.includes(userId)) { - if (textChannel.id === channelRun) { + if (channel.topic.includes(userId)) { + if (channel.id === channelRun) { info.runInVeganRestrict = true; } - topic = textChannel.topic.split(' '); + topic = channel.topic.split(' '); const vcId = topic[topic.indexOf(user.id) + 1]; - let voiceChannel = guild.channels.cache.get(vcId); - - if (voiceChannel === undefined) { - const fetchVoiceChannel = await guild.channels - .fetch(vcId) - .catch(() => undefined); - - if (fetchVoiceChannel !== null && fetchVoiceChannel !== undefined) { - voiceChannel = fetchVoiceChannel; - } - } + const voiceChannel = await getVoiceChannel(vcId); if ( - voiceChannel !== undefined && + isVoiceChannel(voiceChannel) && // Used for sanitising the channel topic, so another voice channel does not get deleted voiceChannel.parentId === IDs.categories.restricted ) { await voiceChannel.delete(); } - await textChannel.delete(); + await channel.delete(); } } } @@ -273,23 +263,14 @@ export class UnRestrictCommand extends Command { info.success = true; // Log the ban - let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted); + const logChannel = await getTextBasedChannel(IDs.channels.logs.restricted); - if (logChannel === undefined) { - const fetchLogChannel = await guild.channels.fetch( - IDs.channels.logs.restricted, - ); - if (fetchLogChannel === null || fetchLogChannel === undefined) { - this.container.logger.error('Unrestrict: Could not fetch log channel'); - info.message = `Unrestricted ${user} but could not find the log channel. This has been logged to the database.`; + if (!isTextBasedChannel(logChannel)) { + this.container.logger.error('Unrestrict: Could not fetch log channel'); + info.message = `Unrestricted ${user} but could not find the log channel. This has been logged to the database.`; - return info; - } else { - logChannel = fetchLogChannel; - } - } - - if (!logChannel.isSendable()) { + return info; + } else if (!logChannel.isSendable()) { this.container.logger.error( 'Unrestrict: The bot does not have permission to send in the logs channel!', ); diff --git a/src/commands/mod/slowmode.ts b/src/commands/mod/slowmode.ts index bcb0d67..fc202d1 100644 --- a/src/commands/mod/slowmode.ts +++ b/src/commands/mod/slowmode.ts @@ -19,9 +19,12 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Message, MessageFlagsBitField, TextBasedChannel } from 'discord.js'; -import { ChannelType } from 'discord.js'; import { Duration, DurationFormatter } from '@sapphire/time-utilities'; import { isNumber } from '#utils/maths'; +import { + isTextBasedChannel, + isTextChannel, +} from '@sapphire/discord.js-utilities'; export class SlowmodeCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -58,7 +61,7 @@ export class SlowmodeCommand extends Command { const duration = interaction.options.getString('duration', true); const { channel } = interaction; - if (channel === null) { + if (!isTextBasedChannel(channel)) { await interaction.reply({ content: 'Could not fetch channel!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -94,7 +97,7 @@ export class SlowmodeCommand extends Command { message: '', success: false, }; - if (channel.type !== ChannelType.GuildText) { + if (!isTextChannel(channel)) { info.message = 'Channel is not a text channel!'; return info; } diff --git a/src/commands/mod/softMute.ts b/src/commands/mod/softMute.ts index 0a6dfc5..a14d886 100644 --- a/src/commands/mod/softMute.ts +++ b/src/commands/mod/softMute.ts @@ -20,6 +20,8 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { GuildMember, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; +import { getGuildMember } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class SoftMuteCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -70,11 +72,11 @@ export class SoftMuteCommand extends Command { return; } - // Gets guildMember whilst removing the ability of each other variables being null - const guildMember = guild.members.cache.get(user.id); + // Gets GuildMember whilst removing the ability of each other variables being null + const member = await getGuildMember(user.id, guild); // Checks if guildMember is null - if (guildMember === undefined) { + if (!isGuildMember(member)) { await interaction.reply({ content: 'Error fetching user!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -83,8 +85,8 @@ export class SoftMuteCommand extends Command { return; } - if (guildMember.roles.cache.has(IDs.roles.restrictions.softMute)) { - await guildMember.roles.remove(IDs.roles.restrictions.softMute); + if (member.roles.cache.has(IDs.roles.restrictions.softMute)) { + await member.roles.remove(IDs.roles.restrictions.softMute); await interaction.reply({ content: `Removed soft muted for ${user}`, withResponse: true, @@ -92,7 +94,7 @@ export class SoftMuteCommand extends Command { return; } - await guildMember.roles.add(IDs.roles.restrictions.softMute); + await member.roles.add(IDs.roles.restrictions.softMute); await interaction.reply({ content: `Soft muted ${user}`, diff --git a/src/commands/mod/sus.ts b/src/commands/mod/sus.ts index 3116df7..0848a51 100644 --- a/src/commands/mod/sus.ts +++ b/src/commands/mod/sus.ts @@ -27,8 +27,6 @@ import { ButtonStyle, User, Guild, - TextChannel, - GuildMember, Snowflake, MessageFlagsBitField, } from 'discord.js'; @@ -43,6 +41,13 @@ import { import { checkStaff } from '#utils/checker'; import IDs from '#utils/ids'; import { createSusLogEmbed } from '#utils/embeds'; +import { getGuildMember, getTextBasedChannel, getUser } from '#utils/fetcher'; +import { isUser } from '#utils/typeChecking'; +import { + isGuildMember, + isTextBasedChannel, + isTextChannel, +} from '@sapphire/discord.js-utilities'; // TODO add a check when they join the server to give the user the sus role again @@ -229,19 +234,20 @@ export class SusCommand extends Subcommand { info.success = true; // Log the sus note - let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as - | TextChannel - | undefined; + const logChannel = await getTextBasedChannel(IDs.channels.logs.sus); - if (logChannel === undefined) { - logChannel = (await guild.channels.fetch(IDs.channels.logs.sus)) as - | TextChannel - | undefined; - if (logChannel === undefined) { - this.container.logger.error('Sus Error: Could not fetch log channel'); - info.message = `Added a sus note for ${user} but could not find the log channel. This has been logged to the database.`; - return info; - } + if (!isTextBasedChannel(logChannel)) { + this.container.logger.error('Sus: Could not fetch log channel.'); + info.message = `Added a sus note for ${user} but could not find the log channel. This has been logged to the database.`; + + return info; + } else if (!logChannel.isSendable()) { + this.container.logger.error( + 'Sus: Does not have permission to message in the log channel.', + ); + info.message = `Added a sus note for ${user} but could not send in the logs channel. This has been logged to the database.`; + + return info; } const message = new EmbedBuilder() @@ -265,15 +271,9 @@ export class SusCommand extends Subcommand { private async addSusRole(user: User, guild: Guild) { // Get GuildMember for user to add a sus note for - let member = guild.members.cache.get(user.id); + const member = await getGuildMember(user.id, guild); - // Checks if Member was not found in cache - if (member === undefined) { - // Fetches Member from API call to Discord - member = await guild.members.fetch(user.id).catch(() => undefined); - } - - if (member === undefined) { + if (!isGuildMember(member)) { return; } @@ -289,7 +289,7 @@ export class SusCommand extends Subcommand { const { guild } = interaction; // Checks if all the variables are of the right type - if (guild == null) { + if (guild === null) { await interaction.reply({ content: 'Error fetching guild!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -330,7 +330,7 @@ export class SusCommand extends Subcommand { const { guild, channel } = interaction; // Checks if all the variables are of the right type - if (guild === null || channel === null) { + if (guild === null || !isTextBasedChannel(channel)) { await interaction.reply({ content: 'Error fetching guild or channel!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -356,11 +356,8 @@ export class SusCommand extends Subcommand { const modId = note.modId; // Get user GuildMembers for user and mod and person who ran command - let user = guild.client.users.cache.get(userId); - if (!(user instanceof User)) { - user = await guild.client.users.fetch(userId).catch(() => undefined); - } - if (user === undefined) { + const user = await getUser(userId); + if (!isUser(user)) { await interaction.reply({ content: 'Error fetching user!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -369,15 +366,11 @@ export class SusCommand extends Subcommand { return; } - let modCreator = guild.client.users.cache.get(modId); - if (!(modCreator instanceof User)) { - modCreator = await guild.client.users.fetch(modId).catch(() => undefined); - } + const modCreator = await getUser(modId); - let modCreatorDisplay = modId; - if (modCreator instanceof User) { - modCreatorDisplay = modCreator.displayName; - } + const modCreatorDisplay = isUser(modCreator) + ? modCreator.displayName + : modId; // Create an embed for the note const noteEmbed = new EmbedBuilder() @@ -443,18 +436,15 @@ export class SusCommand extends Subcommand { // Checks if there are no notes on the user and if there's none, remove the sus role if (notes.length === 0) { - let member = guild.members.cache.get(userId); - if (!(member instanceof GuildMember)) { - member = await guild.members.fetch(userId).catch(() => undefined); - } + const member = guild.members.cache.get(userId); - if (member instanceof GuildMember) { + if (isGuildMember(member)) { await member.roles.remove(IDs.roles.restrictions.sus); } } // Logs the removal of the sus note - await this.deleteNoteLogger(userId, mod, noteId, guild); + await this.deleteNoteLogger(userId, mod, noteId); } }); @@ -467,32 +457,22 @@ export class SusCommand extends Subcommand { } // Logs removal of 1 sus note - private async deleteNoteLogger( - userId: Snowflake, - mod: User, - noteId: number, - guild: Guild, - ) { + private async deleteNoteLogger(userId: Snowflake, mod: User, noteId: number) { // Find user - let user = guild.client.users.cache.get(userId); - if (user === undefined) { - user = await guild.client.users.fetch(userId).catch(() => undefined); - } - if (user === undefined) return; + const user = await getUser(userId); + if (!isUser(user)) return; // Log the sus note - let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as - | TextChannel - | undefined; + const logChannel = await getTextBasedChannel(IDs.channels.logs.sus); - if (logChannel === undefined) { - logChannel = (await guild.channels.fetch(IDs.channels.logs.sus)) as - | TextChannel - | undefined; - if (logChannel === undefined) { - this.container.logger.error('Sus Error: Could not fetch log channel'); - return; - } + if (!isTextBasedChannel(logChannel)) { + this.container.logger.error('Sus: Could not fetch log channel.'); + return; + } else if (!logChannel.isSendable()) { + this.container.logger.error( + 'Sus: The bot does not have permission to send in the log channel', + ); + return; } const embed = new EmbedBuilder() @@ -521,7 +501,7 @@ export class SusCommand extends Subcommand { const { guild, channel } = interaction; // Checks if all the variables are of the right type - if (guild === null || channel === null) { + if (guild === null || !isTextBasedChannel(channel)) { await interaction.reply({ content: 'Error fetching guild or channel!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -530,10 +510,10 @@ export class SusCommand extends Subcommand { return; } - const member = guild.members.cache.get(user.id); + const member = await getGuildMember(user.id, guild); // Checks if managed to find GuildMember for the user - if (member === undefined) { + if (!isGuildMember(member)) { await interaction.reply({ content: 'Error fetching user!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -570,10 +550,11 @@ export class SusCommand extends Subcommand { ) { // Get mod name let mod = notes[i].modId; - const modGuildMember = guild.members.cache.get(mod); - if (modGuildMember !== undefined) { + const modGuildMember = await getGuildMember(mod, guild); + if (isGuildMember(modGuildMember)) { mod = modGuildMember.displayName; } + // Add sus note to embed noteEmbed.addFields({ name: `Sus ID: ${ @@ -633,7 +614,7 @@ export class SusCommand extends Subcommand { }); } - await this.deleteAllNotesLogger(user, mod, guild); + await this.deleteAllNotesLogger(user, mod); }); // Remove the buttons after they have been clicked @@ -648,20 +629,18 @@ export class SusCommand extends Subcommand { } // Logs removal of 1 sus note - private async deleteAllNotesLogger(user: User, mod: User, guild: Guild) { + private async deleteAllNotesLogger(user: User, mod: User) { // Log the sus note - let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as - | TextChannel - | undefined; + const logChannel = await getTextBasedChannel(IDs.channels.logs.sus); - if (logChannel === undefined) { - logChannel = (await guild.channels.fetch(IDs.channels.logs.sus)) as - | TextChannel - | undefined; - if (logChannel === undefined) { - this.container.logger.error('Sus Error: Could not fetch log channel'); - return; - } + if (!isTextChannel(logChannel)) { + this.container.logger.error('Sus: Could not fetch log channel.'); + return; + } else if (!logChannel.isSendable()) { + this.container.logger.error( + 'Sus: Could not not send in the log channel.', + ); + return; } const embed = new EmbedBuilder() diff --git a/src/commands/mod/vcMute.ts b/src/commands/mod/vcMute.ts index 763f367..c8f1fe4 100644 --- a/src/commands/mod/vcMute.ts +++ b/src/commands/mod/vcMute.ts @@ -25,6 +25,8 @@ import { checkActive, } from '#utils/database/moderation/vcMute'; import { addExistingUser } from '#utils/database/dbExistingUser'; +import { getGuildMember } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class VCMuteCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -66,7 +68,7 @@ export class VCMuteCommand extends Command { // Get the arguments const user = interaction.options.getUser('user', true); const reason = interaction.options.getString('reason'); - const modUser = interaction.user; + const mod = interaction.member; const { guild } = interaction; // Checks if all the variables are of the right type @@ -80,11 +82,10 @@ export class VCMuteCommand extends Command { } // Gets guildMember whilst removing the ability of each other variables being null - const member = guild.members.cache.get(user.id); - const mod = guild.members.cache.get(modUser.id); + const member = await getGuildMember(user.id, guild); - // Checks if guildMember is null - if (member === undefined || mod === undefined) { + // Checks if `member` was found + if (!isGuildMember(member)) { await interaction.reply({ content: 'Error fetching user!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -93,6 +94,16 @@ export class VCMuteCommand extends Command { return; } + // Checks if `mod` was found + if (!isGuildMember(mod)) { + await interaction.reply({ + content: 'Error fetching your user!', + flags: MessageFlagsBitField.Flags.Ephemeral, + withResponse: true, + }); + return; + } + // Check if removing VC Mute if (await checkActive(member.id)) { await removeMute(member.id); @@ -137,7 +148,7 @@ export class VCMuteCommand extends Command { const reason = args.finished ? null : await args.rest('string'); const mod = message.member; - if (mod === null) { + if (!isGuildMember(mod)) { await message.react('❌'); await message.reply( 'Moderator not found! Try again or contact a developer!', diff --git a/src/commands/mod/warning/deleteWarning.ts b/src/commands/mod/warning/deleteWarning.ts index c443aad..6e56c0a 100644 --- a/src/commands/mod/warning/deleteWarning.ts +++ b/src/commands/mod/warning/deleteWarning.ts @@ -19,13 +19,16 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; -import type { Message, Guild, User } from 'discord.js'; +import type { Message, User } from 'discord.js'; import IDs from '#utils/ids'; import { deleteWarning, fetchWarning, } from '#utils/database/moderation/warnings'; import { checkStaff } from '#utils/checker'; +import { getTextBasedChannel, getUser } from '#utils/fetcher'; +import { isUser } from '#utils/typeChecking'; +import { isTextChannel } from '@sapphire/discord.js-utilities'; export class DeleteWarningCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -62,17 +65,6 @@ export class DeleteWarningCommand extends Command { // Get the arguments const warningId = interaction.options.getInteger('id', true); const mod = interaction.user; - const { guild } = interaction; - - // Checks if all the variables are of the right type - if (guild === null) { - await interaction.reply({ - content: 'Error fetching guild!', - flags: MessageFlagsBitField.Flags.Ephemeral, - withResponse: true, - }); - return; - } const staffChannel = checkStaff(interaction.channel); @@ -80,7 +72,7 @@ export class DeleteWarningCommand extends Command { flags: staffChannel ? undefined : MessageFlagsBitField.Flags.Ephemeral, }); - const info = await this.deleteWarning(warningId, mod, guild); + const info = await this.deleteWarning(warningId, mod); await interaction.editReply({ content: info.message, @@ -110,7 +102,7 @@ export class DeleteWarningCommand extends Command { return; } - const info = await this.deleteWarning(warningId, mod, guild); + const info = await this.deleteWarning(warningId, mod); await message.reply({ content: info.message, embeds: info.embeds }); if (!info.success) { @@ -118,7 +110,7 @@ export class DeleteWarningCommand extends Command { } } - private async deleteWarning(warningId: number, mod: User, guild: Guild) { + private async deleteWarning(warningId: number, mod: User) { const info = { message: '', embeds: [] as EmbedBuilder[], @@ -136,38 +128,26 @@ export class DeleteWarningCommand extends Command { info.success = true; const userId = warning.userId; - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); - if (user === undefined) { - user = await guild.client.users.fetch(userId); - if (user === undefined) { - info.message = `Deleted warning ID \`${warningId}\`, but the user could not be found!`; - return info; - } + if (!isUser(user)) { + info.message = `Deleted warning ID \`${warningId}\`, but the user could not be found!`; + return info; } // Log the warnings deletion - let logChannel = guild.channels.cache.get(IDs.channels.logs.sus); + const logChannel = await getTextBasedChannel(IDs.channels.logs.sus); - if (logChannel === undefined) { - const fetchLogChannel = await guild.channels - .fetch(IDs.channels.logs.sus) - .catch(() => undefined); - if (fetchLogChannel === null || fetchLogChannel === undefined) { - this.container.logger.error( - 'Delete Warning: Could not fetch log channel', - ); - info.message = - `Deleted warning for ${user} (Warning ID: ${warningId} but ` + - 'could not find the log channel.'; + if (!isTextChannel(logChannel)) { + this.container.logger.error( + 'Delete Warning: Could not fetch log channel', + ); + info.message = + `Deleted warning for ${user} (Warning ID: ${warningId} but ` + + 'could not find the log channel.'; - return info; - } else { - logChannel = fetchLogChannel; - } - } - - if (!logChannel.isSendable()) { + return info; + } else if (!logChannel.isSendable()) { this.container.logger.error( 'Delete Warning: The bot does not have permission to send in the logs channel!', ); diff --git a/src/commands/mod/warning/warn.ts b/src/commands/mod/warning/warn.ts index b91be8f..9b71e9a 100644 --- a/src/commands/mod/warning/warn.ts +++ b/src/commands/mod/warning/warn.ts @@ -29,6 +29,9 @@ import { import { updateUser } from '#utils/database/dbExistingUser'; import { addWarn } from '#utils/database/moderation/warnings'; import IDs from '#utils/ids'; +import { getGuildMember, getTextBasedChannel, getUser } from '#utils/fetcher'; +import { isGuildMember, isTextChannel } from '@sapphire/discord.js-utilities'; +import { isUser } from '#utils/typeChecking'; export class WarnCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -104,7 +107,7 @@ export class WarnCommand extends Command { return; } const reason = args.finished ? null : await args.rest('string'); - const mod = message.member; + const mod = message.author; if (reason === null) { await message.react('❌'); @@ -112,14 +115,6 @@ export class WarnCommand extends Command { return; } - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Moderator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -150,10 +145,10 @@ export class WarnCommand extends Command { }; // Gets mod's GuildMember - const mod = guild.members.cache.get(modId); + const mod = await getGuildMember(modId, guild); // Checks if guildMember is null - if (mod === undefined) { + if (!isGuildMember(mod)) { info.message = 'Error fetching mod!'; return info; } @@ -162,14 +157,11 @@ export class WarnCommand extends Command { await updateUser(mod); // Gets User for person being restricted - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); - if (user === undefined) { - user = await guild.client.users.fetch(userId); - if (user === undefined) { - info.message = 'Error fetching user'; - return info; - } + if (!isUser(user)) { + info.message = 'Error fetching user'; + return info; } await addWarn(userId, modId, reason); @@ -191,24 +183,14 @@ export class WarnCommand extends Command { await user.send({ embeds: [dmEmbed] }).catch(() => {}); // Log the ban - let logChannel = guild.channels.cache.get(IDs.channels.logs.sus); + const logChannel = await getTextBasedChannel(IDs.channels.logs.sus); - if (logChannel === undefined) { - const fetchLogChannel = await guild.channels - .fetch(IDs.channels.logs.sus) - .catch(() => undefined); + if (!isTextChannel(logChannel)) { + this.container.logger.error('Warn: Could not fetch log channel'); + info.message = `Warned ${user} but could not find the log channel. This has been logged to the database.`; - if (fetchLogChannel === null || fetchLogChannel === undefined) { - this.container.logger.error('Warn: Could not fetch log channel'); - info.message = `Warned ${user} but could not find the log channel. This has been logged to the database.`; - - return info; - } else { - logChannel = fetchLogChannel; - } - } - - if (!logChannel.isSendable()) { + return info; + } else if (!logChannel.isSendable()) { this.container.logger.error( 'Warn: The bot does not have permission to send in the logs channel!', ); diff --git a/src/commands/mod/warning/warnings.ts b/src/commands/mod/warning/warnings.ts index ea68dcc..53c9fd2 100644 --- a/src/commands/mod/warning/warnings.ts +++ b/src/commands/mod/warning/warnings.ts @@ -18,12 +18,15 @@ */ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; -import { ChannelType, EmbedBuilder, MessageFlagsBitField } from 'discord.js'; +import { EmbedBuilder, MessageFlagsBitField } from 'discord.js'; import type { Message, Guild, User } from 'discord.js'; import IDs from '#utils/ids'; import { fetchWarnings } from '#utils/database/moderation/warnings'; import { checkStaff } from '#utils/checker'; import { createWarningsEmbed } from '#utils/embeds'; +import { isUser } from '#utils/typeChecking'; +import { isTextChannel } from '@sapphire/discord.js-utilities'; +import { getUser } from '#utils/fetcher'; export class WarningsCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -103,10 +106,10 @@ export class WarningsCommand extends Command { return; } - if (user === undefined) { + if (!isUser(user)) { const { channel } = message; - if (channel.type !== ChannelType.GuildText) { + if (!isTextChannel(channel)) { await message.react('❌'); await message.reply('User was not provided!'); return; @@ -121,18 +124,16 @@ export class WarningsCommand extends Command { // eslint-disable-next-line prefer-destructuring const userId = topic[2]; - user = guild.client.users.cache.get(userId); - - if (user === undefined) { - user = await guild.client.users.fetch(userId); - } + user = await getUser(userId); } } } - if (user === undefined) { + if (!isUser(user)) { await message.react('❌'); - await message.reply('User was not provided!'); + await message.reply( + 'User was not provided! (You most likely provided a user incorrectly.)', + ); return; } diff --git a/src/commands/outreach/outreach.ts b/src/commands/outreach/outreach.ts index 43d2b0f..5c940b3 100644 --- a/src/commands/outreach/outreach.ts +++ b/src/commands/outreach/outreach.ts @@ -41,6 +41,11 @@ import { } from '#utils/database/outreach'; import IDs from '#utils/ids'; import { EmbedBuilder } from 'discord.js'; +import { + isGuildMember, + isTextBasedChannel, +} from '@sapphire/discord.js-utilities'; +import { getGuildMember, getTextBasedChannel } from '#utils/fetcher'; export class OutreachCommand extends Subcommand { public constructor( @@ -189,7 +194,7 @@ export class OutreachCommand extends Subcommand { interaction: Subcommand.ChatInputCommandInteraction, ) { // const start = interaction.options.getBoolean('start'); - const modUser = interaction.user; + const mod = interaction.member; const { guild } = interaction; if (guild === null) { @@ -200,9 +205,7 @@ export class OutreachCommand extends Subcommand { return; } - const mod = guild.members.cache.get(modUser.id); - - if (mod === undefined) { + if (!isGuildMember(mod)) { await interaction.reply({ content: 'Outreach Leader was not found!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -228,7 +231,7 @@ export class OutreachCommand extends Subcommand { await updateUser(mod); - await createEvent(modUser.id); + await createEvent(mod.id); await interaction.reply({ content: 'Created the event!', @@ -237,7 +240,7 @@ export class OutreachCommand extends Subcommand { } public async eventEnd(interaction: Subcommand.ChatInputCommandInteraction) { - const modUser = interaction.user; + const mod = interaction.member; const { guild } = interaction; if (guild === null) { @@ -248,9 +251,7 @@ export class OutreachCommand extends Subcommand { return; } - const mod = guild.members.cache.get(modUser.id); - - if (mod === undefined) { + if (!isGuildMember(mod)) { await interaction.reply({ content: 'Your guild member was not found!', flags: MessageFlagsBitField.Flags.Ephemeral, @@ -306,9 +307,9 @@ export class OutreachCommand extends Subcommand { educated += group.educated; }); - const activist = guild.channels.cache.get(IDs.channels.activism.activism); + const activist = await getTextBasedChannel(IDs.channels.activism.activism); - if (activist === undefined || !activist.isTextBased()) { + if (!isTextBasedChannel(activist)) { await interaction.editReply( 'Event has now ended, but could not post statistics!', ); @@ -386,9 +387,9 @@ export class OutreachCommand extends Subcommand { const statGroups = await getStatGroups(event.id); const groupNo = statGroups.length + 1; - const leaderMember = await guild.members.cache.get(leader.id); + const leaderMember = await getGuildMember(leader.id, guild); - if (leaderMember === undefined) { + if (!isGuildMember(leaderMember)) { await interaction.editReply({ content: `Could not find ${leader}'s guild member.`, }); @@ -465,7 +466,7 @@ export class OutreachCommand extends Subcommand { public async groupAdd(interaction: Subcommand.ChatInputCommandInteraction) { const user = interaction.options.getUser('user', true); const group = interaction.options.getRole('group'); - const leader = interaction.user; + const leader = interaction.member; const { guild } = interaction; if (guild === null) { @@ -476,6 +477,13 @@ export class OutreachCommand extends Subcommand { return; } + if (!isGuildMember(leader)) { + await interaction.editReply({ + content: 'Could not find your GuildMember!', + }); + return; + } + await interaction.deferReply({ flags: MessageFlagsBitField.Flags.Ephemeral, }); @@ -494,18 +502,9 @@ export class OutreachCommand extends Subcommand { return; } - const leaderMember = guild.members.cache.get(leader.id); - - if (leaderMember === undefined) { - await interaction.editReply({ - content: 'Could not find your GuildMember in cache!', - }); - return; - } - if ( leader.id !== stat.stat.leaderId && - !leaderMember.roles.cache.has(IDs.roles.staff.outreachLeader) + !leader.roles.cache.has(IDs.roles.staff.outreachLeader) ) { await interaction.editReply({ content: `You are not the leader for ${group}`, @@ -537,9 +536,9 @@ export class OutreachCommand extends Subcommand { return; } - const member = guild.members.cache.get(user.id); + const member = await getGuildMember(user.id, guild); - if (member === undefined) { + if (!isGuildMember(member)) { await interaction.editReply({ content: 'Could not fetch the member!', }); @@ -579,14 +578,6 @@ export class OutreachCommand extends Subcommand { educated: educated !== null ? educated : 0, }; - if (leader === null) { - await interaction.reply({ - content: 'Could not find your user!', - flags: MessageFlagsBitField.Flags.Ephemeral, - }); - return; - } - await interaction.deferReply({ flags: MessageFlagsBitField.Flags.Ephemeral, }); diff --git a/src/commands/roles/bookClub.ts b/src/commands/roles/bookClub.ts index 398aadc..c7ca86e 100644 --- a/src/commands/roles/bookClub.ts +++ b/src/commands/roles/bookClub.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class BookClubCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -90,14 +93,6 @@ export class BookClubCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Event coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -117,16 +112,16 @@ export class BookClubCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const bookClub = guild.roles.cache.get(IDs.roles.bookClub); + const member = await getGuildMember(user.id, guild); + const bookClub = await getRole(IDs.roles.bookClub, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (bookClub === undefined) { + if (!isRole(bookClub)) { info.message = 'Error fetching book club role from cache!'; return info; } diff --git a/src/commands/roles/debateHost.ts b/src/commands/roles/debateHost.ts index 2f409be..cf4349c 100644 --- a/src/commands/roles/debateHost.ts +++ b/src/commands/roles/debateHost.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class DebateHostCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -91,14 +94,6 @@ export class DebateHostCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Event coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -118,16 +113,16 @@ export class DebateHostCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const debateHost = guild.roles.cache.get(IDs.roles.debateHost); + const member = await getGuildMember(user.id, guild); + const debateHost = await getRole(IDs.roles.debateHost, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (debateHost === undefined) { + if (!isRole(debateHost)) { info.message = 'Error fetching debate host role from cache!'; return info; } diff --git a/src/commands/roles/gameNightHost.ts b/src/commands/roles/gameNightHost.ts index f8a8645..2b74e9a 100644 --- a/src/commands/roles/gameNightHost.ts +++ b/src/commands/roles/gameNightHost.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class GameNightHostCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -90,14 +93,6 @@ export class GameNightHostCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Event coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -117,16 +112,16 @@ export class GameNightHostCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const gameNightHost = guild.roles.cache.get(IDs.roles.gameNightHost); + const member = await getGuildMember(user.id, guild); + const gameNightHost = await getRole(IDs.roles.gameNightHost, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (gameNightHost === undefined) { + if (!isRole(gameNightHost)) { info.message = 'Error fetching game night host role from cache!'; return info; } diff --git a/src/commands/roles/guest.ts b/src/commands/roles/guest.ts index 6cf7f90..223ed88 100644 --- a/src/commands/roles/guest.ts +++ b/src/commands/roles/guest.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class GuestCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -90,14 +93,6 @@ export class GuestCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Event coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -117,16 +112,16 @@ export class GuestCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const guest = guild.roles.cache.get(IDs.roles.guest); + const member = await getGuildMember(user.id, guild); + const guest = await getRole(IDs.roles.guest, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (guest === undefined) { + if (!isRole(guest)) { info.message = 'Error fetching guest role from cache!'; return info; } diff --git a/src/commands/roles/staff/mentor.ts b/src/commands/roles/staff/mentor.ts index 8969374..6810e89 100644 --- a/src/commands/roles/staff/mentor.ts +++ b/src/commands/roles/staff/mentor.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { isRole } from '#utils/typeChecking'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class MentorCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -91,14 +94,6 @@ export class MentorCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Mentor coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -118,16 +113,16 @@ export class MentorCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const mentor = guild.roles.cache.get(IDs.roles.staff.mentor); + const member = await getGuildMember(user.id, guild); + const mentor = await getRole(IDs.roles.staff.mentor, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (mentor === undefined) { + if (!isRole(mentor)) { info.message = 'Error fetching mentor role from cache!'; return info; } diff --git a/src/commands/roles/staff/mod.ts b/src/commands/roles/staff/mod.ts index 4074dd0..352d860 100644 --- a/src/commands/roles/staff/mod.ts +++ b/src/commands/roles/staff/mod.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class ModCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -90,14 +93,6 @@ export class ModCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Mod coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -117,16 +112,16 @@ export class ModCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const moderator = guild.roles.cache.get(IDs.roles.staff.moderator); + const member = await getGuildMember(user.id, guild); + const moderator = await getRole(IDs.roles.staff.moderator, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (moderator === undefined) { + if (!isRole(moderator)) { info.message = 'Error fetching the moderator role from cache!'; return info; } diff --git a/src/commands/roles/staff/restrictedAccess.ts b/src/commands/roles/staff/restrictedAccess.ts index 7991beb..f0d3324 100644 --- a/src/commands/roles/staff/restrictedAccess.ts +++ b/src/commands/roles/staff/restrictedAccess.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class RestrictedAccessCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -91,14 +94,6 @@ export class RestrictedAccessCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Mod coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -118,16 +113,16 @@ export class RestrictedAccessCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const restricted = guild.roles.cache.get(IDs.roles.staff.restricted); + const member = await getGuildMember(user.id, guild); + const restricted = await getRole(IDs.roles.staff.restricted, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (restricted === undefined) { + if (!isRole(restricted)) { info.message = 'Error fetching the restricted access role from cache!'; return info; } diff --git a/src/commands/roles/staff/stagehost.ts b/src/commands/roles/staff/stagehost.ts index d7df6bf..cdb81a4 100644 --- a/src/commands/roles/staff/stagehost.ts +++ b/src/commands/roles/staff/stagehost.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class StageHostCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -90,14 +93,6 @@ export class StageHostCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Event coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -117,16 +112,16 @@ export class StageHostCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const stageHost = guild.roles.cache.get(IDs.roles.stageHost); + const member = await getGuildMember(user.id, guild); + const stageHost = await getRole(IDs.roles.stageHost, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (stageHost === undefined) { + if (!isRole(stageHost)) { info.message = 'Error fetching stage host role from cache!'; return info; } diff --git a/src/commands/roles/staff/trialMod.ts b/src/commands/roles/staff/trialMod.ts index 38c6652..5338d85 100644 --- a/src/commands/roles/staff/trialMod.ts +++ b/src/commands/roles/staff/trialMod.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class TrialModCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -91,14 +94,6 @@ export class TrialModCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Mod coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -118,16 +113,16 @@ export class TrialModCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const moderator = guild.roles.cache.get(IDs.roles.staff.trialModerator); + const member = await getGuildMember(user.id, guild); + const moderator = await getRole(IDs.roles.staff.trialModerator, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (moderator === undefined) { + if (!isRole(moderator)) { info.message = 'Error fetching the trial moderator role from cache!'; return info; } diff --git a/src/commands/roles/staff/trialVerifier.ts b/src/commands/roles/staff/trialVerifier.ts index ee767f3..29feaa7 100644 --- a/src/commands/roles/staff/trialVerifier.ts +++ b/src/commands/roles/staff/trialVerifier.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class TrialVerifierCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -90,14 +93,6 @@ export class TrialVerifierCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Verifier coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -117,16 +112,16 @@ export class TrialVerifierCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const trialVerifier = guild.roles.cache.get(IDs.roles.staff.trialVerifier); + const member = await getGuildMember(user.id, guild); + const trialVerifier = await getRole(IDs.roles.staff.trialVerifier, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (trialVerifier === undefined) { + if (!isRole(trialVerifier)) { info.message = 'Error fetching the trial verifier role from cache!'; return info; } diff --git a/src/commands/roles/staff/verifier.ts b/src/commands/roles/staff/verifier.ts index 3b1d620..289272c 100644 --- a/src/commands/roles/staff/verifier.ts +++ b/src/commands/roles/staff/verifier.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class VerifierCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -90,14 +93,6 @@ export class VerifierCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Verifier coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -117,16 +112,16 @@ export class VerifierCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const verifier = guild.roles.cache.get(IDs.roles.staff.verifier); + const member = await getGuildMember(user.id, guild); + const verifier = await getRole(IDs.roles.staff.verifier, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (verifier === undefined) { + if (!isRole(verifier)) { info.message = 'Error fetching verifier role from cache!'; return info; } diff --git a/src/commands/roles/verification/activist.ts b/src/commands/roles/verification/activist.ts index b69f5b9..169af2e 100644 --- a/src/commands/roles/verification/activist.ts +++ b/src/commands/roles/verification/activist.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class ActivistCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -93,12 +96,6 @@ export class ActivistCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply('Staff not found! Try again or contact a developer!'); - return; - } - const { guild } = message; if (guild === null) { @@ -118,22 +115,22 @@ export class ActivistCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const modMember = guild.members.cache.get(mod.id); - const activist = guild.roles.cache.get(IDs.roles.vegan.activist); + const member = await getGuildMember(user.id, guild); + const modMember = await getGuildMember(mod.id, guild); + const activist = await getRole(IDs.roles.vegan.activist, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (modMember === undefined) { + if (!isGuildMember(modMember)) { info.message = "Error fetching the staff's guild member!"; return info; } - if (activist === undefined) { + if (!isRole(activist)) { info.message = 'Error fetching activist role from cache!'; return info; } diff --git a/src/commands/roles/verification/aravegan.ts b/src/commands/roles/verification/aravegan.ts index 199b750..ff48145 100644 --- a/src/commands/roles/verification/aravegan.ts +++ b/src/commands/roles/verification/aravegan.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class ARAVeganCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -92,12 +95,6 @@ export class ARAVeganCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply('Staff not found! Try again or contact a developer!'); - return; - } - const { guild } = message; if (guild === null) { @@ -117,22 +114,22 @@ export class ARAVeganCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const modMember = guild.members.cache.get(mod.id); - const vegan = guild.roles.cache.get(IDs.roles.vegan.araVegan); + const member = await getGuildMember(user.id, guild); + const modMember = await getGuildMember(mod.id, guild); + const vegan = await getRole(IDs.roles.vegan.araVegan, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (modMember === undefined) { + if (!isGuildMember(modMember)) { info.message = "Error fetching the staff's guild member!"; return info; } - if (vegan === undefined) { + if (!isRole(vegan)) { info.message = 'Error fetching vegan role from cache!'; return info; } diff --git a/src/commands/roles/verification/convinced.ts b/src/commands/roles/verification/convinced.ts index 45a5011..2962340 100644 --- a/src/commands/roles/verification/convinced.ts +++ b/src/commands/roles/verification/convinced.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class ConvincedCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -91,12 +94,6 @@ export class ConvincedCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply('Mod not found! Try again or contact a developer!'); - return; - } - const { guild } = message; if (guild === null) { @@ -116,16 +113,16 @@ export class ConvincedCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const convinced = guild.roles.cache.get(IDs.roles.nonvegan.convinced); + const member = await getGuildMember(user.id, guild); + const convinced = await getRole(IDs.roles.nonvegan.convinced, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (convinced === undefined) { + if (!isRole(convinced)) { info.message = 'Error fetching coordinator role from cache!'; return info; } diff --git a/src/commands/roles/verification/plus.ts b/src/commands/roles/verification/plus.ts index 4f355d1..06219f6 100644 --- a/src/commands/roles/verification/plus.ts +++ b/src/commands/roles/verification/plus.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class PlusCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -88,14 +91,6 @@ export class PlusCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply( - 'Coordinator not found! Try again or contact a developer!', - ); - return; - } - const { guild } = message; if (guild === null) { @@ -115,16 +110,16 @@ export class PlusCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const plus = guild.roles.cache.get(IDs.roles.vegan.plus); + const member = await getGuildMember(user.id, guild); + const plus = await getRole(IDs.roles.vegan.plus, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (plus === undefined) { + if (!isRole(plus)) { info.message = 'Error fetching plus role from cache!'; return info; } diff --git a/src/commands/roles/verification/trusted.ts b/src/commands/roles/verification/trusted.ts index fd27d7b..25ea975 100644 --- a/src/commands/roles/verification/trusted.ts +++ b/src/commands/roles/verification/trusted.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class TrustedCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -91,12 +94,6 @@ export class TrustedCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply('Mod not found! Try again or contact a developer!'); - return; - } - const { guild } = message; if (guild === null) { @@ -116,16 +113,16 @@ export class TrustedCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const trusted = guild.roles.cache.get(IDs.roles.trusted); + const member = await getGuildMember(user.id, guild); + const trusted = await getRole(IDs.roles.trusted, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (trusted === undefined) { + if (!isRole(trusted)) { info.message = 'Error fetching trusted role from cache!'; return info; } diff --git a/src/commands/roles/verification/vegan.ts b/src/commands/roles/verification/vegan.ts index 0888cfb..ae3f848 100644 --- a/src/commands/roles/verification/vegan.ts +++ b/src/commands/roles/verification/vegan.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class VeganCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -93,12 +96,6 @@ export class VeganCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply('Staff not found! Try again or contact a developer!'); - return; - } - const { guild } = message; if (guild === null) { @@ -118,22 +115,22 @@ export class VeganCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const modMember = guild.members.cache.get(mod.id); - const vegan = guild.roles.cache.get(IDs.roles.vegan.vegan); + const member = await getGuildMember(user.id, guild); + const modMember = await getGuildMember(mod.id, guild); + const vegan = await getRole(IDs.roles.vegan.vegan, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (modMember === undefined) { + if (!isGuildMember(modMember)) { info.message = "Error fetching the staff's guild member!"; return info; } - if (vegan === undefined) { + if (!isRole(vegan)) { info.message = 'Error fetching vegan role from cache!'; return info; } diff --git a/src/commands/roles/verification/vegcurious.ts b/src/commands/roles/verification/vegcurious.ts index 43e13df..c3e219b 100644 --- a/src/commands/roles/verification/vegcurious.ts +++ b/src/commands/roles/verification/vegcurious.ts @@ -21,6 +21,9 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { Guild, User, Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { roleAddLog, roleRemoveLog } from '#utils/logging/role'; +import { getGuildMember, getRole } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; +import { isRole } from '#utils/typeChecking'; export class VegCuriousCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -91,12 +94,6 @@ export class VegCuriousCommand extends Command { const mod = message.author; - if (mod === null) { - await message.react('❌'); - await message.reply('Staff not found! Try again or contact a developer!'); - return; - } - const { guild } = message; if (guild === null) { @@ -116,22 +113,22 @@ export class VegCuriousCommand extends Command { message: '', success: false, }; - const member = guild.members.cache.get(user.id); - const modMember = guild.members.cache.get(mod.id); - const vegCurious = guild.roles.cache.get(IDs.roles.nonvegan.vegCurious); + const member = await getGuildMember(user.id, guild); + const modMember = await getGuildMember(mod.id, guild); + const vegCurious = await getRole(IDs.roles.nonvegan.vegCurious, guild); // Checks if user's GuildMember was found in cache - if (member === undefined) { + if (!isGuildMember(member)) { info.message = 'Error fetching guild member for the user!'; return info; } - if (modMember === undefined) { + if (!isGuildMember(modMember)) { info.message = "Error fetching the staff's guild member!"; return info; } - if (vegCurious === undefined) { + if (!isRole(vegCurious)) { info.message = 'Error fetching veg curious role from cache!'; return info; } diff --git a/src/commands/utils/count.ts b/src/commands/utils/count.ts index f2757a2..cf2ec3e 100644 --- a/src/commands/utils/count.ts +++ b/src/commands/utils/count.ts @@ -20,6 +20,8 @@ import { Command, RegisterBehavior } from '@sapphire/framework'; import { Message, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; +import { getRole } from '#utils/fetcher'; +import { isRole } from '#utils/typeChecking'; export class RenameUserCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -55,10 +57,10 @@ export class RenameUserCommand extends Command { await guild.members.fetch(); - const vegan = guild.roles.cache.get(IDs.roles.vegan.vegan); - const notVegan = guild.roles.cache.get(IDs.roles.nonvegan.nonvegan); + const vegan = await getRole(IDs.roles.vegan.vegan, guild); + const notVegan = await getRole(IDs.roles.nonvegan.nonvegan, guild); - if (vegan === undefined || notVegan === undefined) { + if (!isRole(vegan) || !isRole(notVegan)) { await interaction.reply({ content: 'Error fetching roles!', flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/commands/verification/verify.ts b/src/commands/verification/verify.ts index d16798d..31c39df 100644 --- a/src/commands/verification/verify.ts +++ b/src/commands/verification/verify.ts @@ -31,6 +31,8 @@ import { giveVerificationRoles, } from '#utils/verification'; import { manualVerification } from '#utils/database/verification'; +import { getGuildMember } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class VerifyCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -165,15 +167,12 @@ export class VerifyCommand extends Command { convinced: false, }; - let member = guild.members.cache.get(user.id); + const member = await getGuildMember(user.id, guild); // Checks if member is null - if (member === undefined) { - member = await guild.members.fetch(user.id).catch(() => undefined); - if (member === undefined) { - info.message = 'Failed to fetch member'; - return info; - } + if (!isGuildMember(member)) { + info.message = 'Failed to fetch member'; + return info; } if (member.roles.cache.hasAny(...IDs.roles.restrictions.restricted)) { @@ -181,15 +180,12 @@ export class VerifyCommand extends Command { return info; } - let verifier = guild.members.cache.get(verifierId); + const verifier = await getGuildMember(verifierId, guild); // Checks if verifier is null - if (verifier === undefined) { - verifier = await guild.members.fetch(user.id).catch(() => undefined); - if (verifier === undefined) { - info.message = 'Failed to fetch verifier'; - return info; - } + if (!isGuildMember(verifier)) { + info.message = 'Failed to fetch verifier'; + return info; } const roleArgs = rolesString.split(' '); diff --git a/src/commands/verification/verifyTimeoutRemove.ts b/src/commands/verification/verifyTimeoutRemove.ts index 1dab908..0e519b4 100644 --- a/src/commands/verification/verifyTimeoutRemove.ts +++ b/src/commands/verification/verifyTimeoutRemove.ts @@ -21,6 +21,8 @@ import { Command, RegisterBehavior } from '@sapphire/framework'; import IDs from '#utils/ids'; import { checkVerificationFinish } from '#utils/database/verification'; import { MessageFlagsBitField } from 'discord.js'; +import { getGuildMember } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class VerifyTimeoutRemoveCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -71,14 +73,11 @@ export class VerifyTimeoutRemoveCommand extends Command { flags: MessageFlagsBitField.Flags.Ephemeral, }); - let member = guild.members.cache.get(user.id); + const member = await getGuildMember(user.id, guild); - if (member === undefined) { - member = await guild.members.fetch(user.id).catch(undefined); - if (member === undefined) { - await interaction.editReply(`${user} is not on this server!`); - return; - } + if (!isGuildMember(member)) { + await interaction.editReply(`${user} is not on this server!`); + return; } if (!member.roles.cache.has(IDs.roles.verifyBlock)) { diff --git a/src/commands/xp/rank.ts b/src/commands/xp/rank.ts index 7952f5b..676c4ea 100644 --- a/src/commands/xp/rank.ts +++ b/src/commands/xp/rank.ts @@ -21,6 +21,8 @@ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; import { User, Guild, Message, MessageFlagsBitField } from 'discord.js'; import { EmbedBuilder } from 'discord.js'; import { getRank, xpToNextLevel } from '#utils/database/fun/xp'; +import { getGuildMember } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class RankCommand extends Command { public constructor(context: Command.LoaderContext, options: Command.Options) { @@ -108,14 +110,11 @@ export class RankCommand extends Command { success: false, }; - let member = guild.members.cache.get(user.id); + const member = await getGuildMember(user.id, guild); - if (member === undefined) { - member = await guild.members.fetch(user.id).catch(() => undefined); - if (member === undefined) { - info.message = 'The user is not on this server!'; - return info; - } + if (!isGuildMember(member)) { + info.message = 'The user is not on this server!'; + return info; } const rank = await getRank(user.id); diff --git a/src/interaction-handlers/nonVeganAccess.ts b/src/interaction-handlers/nonVeganAccess.ts index b2a3158..49c4ff0 100644 --- a/src/interaction-handlers/nonVeganAccess.ts +++ b/src/interaction-handlers/nonVeganAccess.ts @@ -21,12 +21,9 @@ import { InteractionHandler, InteractionHandlerTypes, } from '@sapphire/framework'; -import { - ButtonInteraction, - GuildMember, - MessageFlagsBitField, -} from 'discord.js'; +import { ButtonInteraction, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class NonVeganAccessButtonHandler extends InteractionHandler { public constructor( @@ -46,13 +43,13 @@ export class NonVeganAccessButtonHandler extends InteractionHandler { } public async run(interaction: ButtonInteraction) { - let { member } = interaction; + const { member } = interaction; const errorMessage = 'There was an error giving you the role, please try again later or contact ModMail/the developer ' + 'to sort out this problem.'; - if (member === null) { + if (!isGuildMember(member)) { await interaction.reply({ content: errorMessage, flags: MessageFlagsBitField.Flags.Ephemeral, @@ -60,28 +57,22 @@ export class NonVeganAccessButtonHandler extends InteractionHandler { return; } - try { - member = member as GuildMember; - - if (!member.roles.cache.has(IDs.roles.vegan.vegan)) { - await interaction.reply({ - content: 'You need to be vegan to use this button!', - flags: MessageFlagsBitField.Flags.Ephemeral, - }); - return; - } - - if (member.roles.cache.has(IDs.roles.vegan.nvAccess)) { - await member.roles.remove(IDs.roles.vegan.nvAccess); - await interaction.reply({ - content: - 'Your access from the non vegan section has been removed. ' + - 'If you want to gain access again, click this button again.', - flags: MessageFlagsBitField.Flags.Ephemeral, - }); - return; - } - + if (!member.roles.cache.has(IDs.roles.vegan.vegan)) { + await interaction.reply({ + content: 'You need to be vegan to use this button!', + flags: MessageFlagsBitField.Flags.Ephemeral, + }); + return; + } else if (member.roles.cache.has(IDs.roles.vegan.nvAccess)) { + await member.roles.remove(IDs.roles.vegan.nvAccess); + await interaction.reply({ + content: + 'Your access from the non vegan section has been removed. ' + + 'If you want to gain access again, click this button again.', + flags: MessageFlagsBitField.Flags.Ephemeral, + }); + return; + } else { await member.roles.add(IDs.roles.vegan.nvAccess); await interaction.reply({ content: @@ -89,12 +80,6 @@ export class NonVeganAccessButtonHandler extends InteractionHandler { 'If you want to remove access again, click this button again.', flags: MessageFlagsBitField.Flags.Ephemeral, }); - } catch (error) { - this.container.logger.error(`Non Vegan Access Interaction: ${error}`); - await interaction.reply({ - content: errorMessage, - flags: MessageFlagsBitField.Flags.Ephemeral, - }); } } } diff --git a/src/interaction-handlers/welcome.ts b/src/interaction-handlers/welcome.ts index 96dc826..b855b07 100644 --- a/src/interaction-handlers/welcome.ts +++ b/src/interaction-handlers/welcome.ts @@ -21,14 +21,12 @@ import { InteractionHandler, InteractionHandlerTypes, } from '@sapphire/framework'; -import { - ButtonInteraction, - GuildMember, - MessageFlagsBitField, -} from 'discord.js'; +import { ButtonInteraction, MessageFlagsBitField } from 'discord.js'; import IDs from '#utils/ids'; import { checkActive } from '#utils/database/moderation/restriction'; import { addUser } from '#utils/database/dbExistingUser'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isGuildMember, isTextChannel } from '@sapphire/discord.js-utilities'; export class WelcomeButtonHandler extends InteractionHandler { public constructor( @@ -49,9 +47,7 @@ export class WelcomeButtonHandler extends InteractionHandler { public async run(interaction: ButtonInteraction) { const { member } = interaction; - let general = this.container.client.channels.cache.get( - IDs.channels.nonVegan.general, - ); + const general = await getTextBasedChannel(IDs.channels.nonVegan.general); // Messages that are used multiple times const roleErrorMessage = @@ -63,32 +59,21 @@ export class WelcomeButtonHandler extends InteractionHandler { 'to be verified and gain access to more channels.'; // Checks if general is not in the cache - if (general === undefined) { - // Sends an API request to get the channel - const generalFetch = await this.container.client.channels - .fetch(IDs.channels.nonVegan.general) - .catch(() => undefined); + if (!isTextChannel(general)) { + this.container.logger.error( + 'WelcomeButtonHandler: Could not find and fetch the general channel!', + ); + await interaction.reply({ + content: + 'Sorry there was a problem trying to give you access to the server. Please try again later.', + flags: MessageFlagsBitField.Flags.Ephemeral, + }); - // If general does not exist - if (generalFetch === null || generalFetch === undefined) { - this.container.logger.error( - 'WelcomeButtonHandler: Could not find and fetch the general channel!', - ); - await interaction.reply({ - content: - 'Sorry there was a problem trying to give you access to the server. Please try again later.', - flags: MessageFlagsBitField.Flags.Ephemeral, - }); - - return; - } - - // Replace fetched version of general with the cached version - general = generalFetch; + return; } // If the member could not be found - if (!(member instanceof GuildMember)) { + if (!isGuildMember(member)) { await interaction.reply({ content: roleErrorMessage, flags: MessageFlagsBitField.Flags.Ephemeral, diff --git a/src/listeners/ban/ban.ts b/src/listeners/ban/ban.ts index d9758ba..d96536e 100644 --- a/src/listeners/ban/ban.ts +++ b/src/listeners/ban/ban.ts @@ -19,10 +19,12 @@ import { Listener } from '@sapphire/framework'; import type { GuildBan } from 'discord.js'; -import { AuditLogEvent, EmbedBuilder, TextChannel } from 'discord.js'; +import { AuditLogEvent, EmbedBuilder } from 'discord.js'; import { addBan, checkBan } from '#utils/database/moderation/ban'; import IDs from '#utils/ids'; import { addEmptyUser, addExistingUser } from '#utils/database/dbExistingUser'; +import { getGuildMember, getTextBasedChannel } from '#utils/fetcher'; +import { isGuildMember, isTextChannel } from '@sapphire/discord.js-utilities'; export class BanListener extends Listener { public constructor( @@ -82,17 +84,12 @@ export class BanListener extends Listener { const { guild } = ban; // Gets mod's GuildMember - let mod = guild.members.cache.get(executor.id); + const mod = await getGuildMember(executor.id, guild); // Checks if GuildMember is null - if (mod === undefined) { - mod = await guild.members.fetch(executor.id).catch(() => undefined); - if (mod === undefined) { - this.container.logger.error( - 'UnbanListener: Could not fetch moderator.', - ); - return; - } + if (!isGuildMember(mod)) { + this.container.logger.error('UnbanListener: Could not fetch moderator.'); + return; } // Check if mod is in database @@ -118,18 +115,11 @@ export class BanListener extends Listener { await addBan(user.id, mod.id, `${reason}`); // Log the ban - let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted) as - | TextChannel - | undefined; + const logChannel = await getTextBasedChannel(IDs.channels.logs.restricted); - if (logChannel === undefined) { - logChannel = (await guild.channels.fetch( - IDs.channels.logs.restricted, - )) as TextChannel | undefined; - if (logChannel === undefined) { - this.container.logger.error('BanListener: Could not fetch log channel'); - return; - } + if (!isTextChannel(logChannel)) { + this.container.logger.error('BanListener: Could not fetch log channel'); + return; } const log = new EmbedBuilder() diff --git a/src/listeners/ban/unban.ts b/src/listeners/ban/unban.ts index 1e48752..d44aaa3 100644 --- a/src/listeners/ban/unban.ts +++ b/src/listeners/ban/unban.ts @@ -19,10 +19,12 @@ import { Listener } from '@sapphire/framework'; import type { GuildBan } from 'discord.js'; -import { AuditLogEvent, EmbedBuilder, TextChannel } from 'discord.js'; +import { AuditLogEvent, EmbedBuilder } from 'discord.js'; import { addBan, checkBan, removeBan } from '#utils/database/moderation/ban'; import IDs from '#utils/ids'; import { addEmptyUser, addExistingUser } from '#utils/database/dbExistingUser'; +import { getGuildMember, getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class UnbanListener extends Listener { public constructor( @@ -71,17 +73,12 @@ export class UnbanListener extends Listener { const { guild } = ban; // Gets mod's GuildMember - let mod = guild.members.cache.get(executor.id); + const mod = await getGuildMember(executor.id, guild); // Checks if GuildMember is null if (mod === undefined) { - mod = await guild.members.fetch(executor.id).catch(() => undefined); - if (mod === undefined) { - this.container.logger.error( - 'UnbanListener: Could not fetch moderator.', - ); - return; - } + this.container.logger.error('UnbanListener: Could not fetch moderator.'); + return; } // Check if mod is in database @@ -100,20 +97,11 @@ export class UnbanListener extends Listener { await removeBan(user.id, mod.id); // Log the ban - let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted) as - | TextChannel - | undefined; + const logChannel = await getTextBasedChannel(IDs.channels.logs.restricted); - if (logChannel === undefined) { - logChannel = (await guild.channels.fetch( - IDs.channels.logs.restricted, - )) as TextChannel | undefined; - if (logChannel === undefined) { - this.container.logger.error( - 'UnbanListener: Could not fetch log channel', - ); - return; - } + if (!isTextBasedChannel(logChannel)) { + this.container.logger.error('UnbanListener: Could not fetch log channel'); + return; } const log = new EmbedBuilder() diff --git a/src/listeners/counting.ts b/src/listeners/counting.ts index d2966e5..0890ddb 100644 --- a/src/listeners/counting.ts +++ b/src/listeners/counting.ts @@ -44,19 +44,22 @@ export class XpListener extends Listener { return; } + if (!message.channel.isSendable()) { + this.container.logger.error( + 'Counting: The bot does not have permission to send messages in the counting chat!', + ); + return; + } + let lastCount = await getLastCount(); // If no counts exist on the database, then create the first count from the bot if (lastCount === null) { if (this.container.client.id === null) { - if (!message.channel.isSendable()) { - // TODO manage logging/errors properly - return; - } - message.channel.send( 'An unexpected error occurred trying to set up the counting channel, please contact a developer!', ); + return; } @@ -68,14 +71,10 @@ export class XpListener extends Listener { lastCount = await getLastCount(); if (lastCount === null) { - if (!message.channel.isSendable()) { - // TODO manage logging/errors properly - return; - } - message.channel.send( 'An unexpected error occurred, please contact a developer!', ); + return; } } diff --git a/src/listeners/fixRoles.ts b/src/listeners/fixRoles.ts index dcbb69e..867736e 100644 --- a/src/listeners/fixRoles.ts +++ b/src/listeners/fixRoles.ts @@ -22,11 +22,12 @@ import { Listener } from '@sapphire/framework'; import { DurationFormatter } from '@sapphire/time-utilities'; -import { Client } from 'discord.js'; import IDs from '#utils/ids'; import { fetchRoles } from '#utils/database/dbExistingUser'; import { checkActive } from '#utils/database/moderation/restriction'; import { getUser } from '#utils/database/fun/xp'; +import { getGuild, getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class FixRolesOnReady extends Listener { public constructor( @@ -45,13 +46,13 @@ export class FixRolesOnReady extends Listener { }); } - public async run(client: Client) { + public async run() { this.container.logger.info( 'FixRolesOnReady: Preparation before starting to fix the roles for each user...', ); // Fetching the Guild - const guild = await client.guilds.fetch(IDs.guild).catch(() => undefined); + const guild = await getGuild(IDs.guild); if (guild === undefined) { this.container.logger.error('FixRolesOnReady: Could not find the server'); @@ -60,8 +61,8 @@ export class FixRolesOnReady extends Listener { // Fetching the channel for the logs // Leave the snowflake parameter empty for no logs - const logChannel = await client.channels.fetch('').catch(() => null); - const sendLogs = logChannel !== null; + const logChannel = await getTextBasedChannel(''); + const sendLogs = isTextBasedChannel(logChannel); if (!sendLogs) { this.container.logger.error( diff --git a/src/listeners/modMail.ts b/src/listeners/modMail.ts index 68085fa..ce00ea0 100644 --- a/src/listeners/modMail.ts +++ b/src/listeners/modMail.ts @@ -18,7 +18,6 @@ */ import { Listener } from '@sapphire/framework'; -import { ChannelType } from 'discord.js'; import type { GuildChannel, EmbedBuilder } from 'discord.js'; import { setTimeout } from 'timers/promises'; import IDs from '#utils/ids'; @@ -33,6 +32,9 @@ import { createWarningsEmbed, } from '#utils/embeds'; import { fetchWarnings } from '#utils/database/moderation/warnings'; +import { isTextChannel } from '@sapphire/discord.js-utilities'; +import { getUser } from '#utils/fetcher'; +import { isUser } from '#utils/typeChecking'; export class ModMailCreateListener extends Listener { public constructor( @@ -50,8 +52,7 @@ export class ModMailCreateListener extends Listener { if (channel.parentId !== IDs.categories.modMail) return; // Checks if the channel is not a text channel - if (!channel.isTextBased()) return; - if (channel.type !== ChannelType.GuildText) return; + if (!isTextChannel(channel)) return; // Gets the guild const { guild } = channel; @@ -64,13 +65,10 @@ export class ModMailCreateListener extends Listener { const userId = topic[2]; // Gets user who created ModMail - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); - if (user === undefined) { - user = await guild.client.users.fetch(userId); - if (user === undefined) { - return; - } + if (!isUser(user)) { + return; } // Check if the user is currently restricted on the database diff --git a/src/listeners/nonVeganAccess.ts b/src/listeners/nonVeganAccess.ts index 545bd89..b77ed5f 100644 --- a/src/listeners/nonVeganAccess.ts +++ b/src/listeners/nonVeganAccess.ts @@ -20,8 +20,9 @@ import { Listener } from '@sapphire/framework'; import { ButtonStyle, ActionRowBuilder, ButtonBuilder } from 'discord.js'; -import type { Client, TextChannel } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class NonVeganAccessReady extends Listener { public constructor( @@ -35,18 +36,12 @@ export class NonVeganAccessReady extends Listener { }); } - public async run(client: Client) { - let roles = client.channels.cache.get(IDs.channels.information.roles) as - | TextChannel - | undefined; - if (roles === undefined) { - roles = (await client.channels.fetch(IDs.channels.information.roles)) as - | TextChannel - | undefined; - if (roles === undefined) { - this.container.logger.error('nonVeganAccess: Roles not found'); - return; - } + public async run() { + const roles = await getTextBasedChannel(IDs.channels.information.roles); + + if (!isTextBasedChannel(roles)) { + this.container.logger.error('nonVeganAccess: Roles not found'); + return; } const botId = this.container.client.id; diff --git a/src/listeners/ready.ts b/src/listeners/ready.ts index 027cb75..78df1d1 100644 --- a/src/listeners/ready.ts +++ b/src/listeners/ready.ts @@ -23,6 +23,8 @@ import { Listener } from '@sapphire/framework'; import type { Client } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class ReadyListener extends Listener { public constructor( @@ -40,9 +42,9 @@ export class ReadyListener extends Listener { const { username, id } = client.user!; this.container.logger.info(`Successfully logged in as ${username} (${id})`); - const botLogChannel = await client.channels.fetch(IDs.channels.logs.bot); + const botLogChannel = await getTextBasedChannel(IDs.channels.logs.bot); - if (botLogChannel === null) { + if (!isTextBasedChannel(botLogChannel)) { this.container.logger.error( 'ReadyListener: Could not find the channel for bot logs.', ); diff --git a/src/listeners/rolesJoinServer.ts b/src/listeners/rolesJoinServer.ts index b0b65bf..3b7fa42 100644 --- a/src/listeners/rolesJoinServer.ts +++ b/src/listeners/rolesJoinServer.ts @@ -18,14 +18,7 @@ */ import { Listener } from '@sapphire/framework'; -import type { - GuildMember, - Snowflake, - CategoryChannel, - Guild, - TextChannel, -} from 'discord.js'; -import { ChannelType } from 'discord.js'; +import type { GuildMember, Snowflake, CategoryChannel } from 'discord.js'; import { fetchRoles, getLeaveRoles } from '#utils/database/dbExistingUser'; import { blockTime } from '#utils/database/verification'; import { @@ -34,6 +27,12 @@ import { } from '#utils/database/moderation/restriction'; import { blockedRoles, blockedRolesAfterRestricted } from '#utils/blockedRoles'; import IDs from '#utils/ids'; +import { getCategoryChannel, getVoiceChannel } from '#utils/fetcher'; +import { + isCategoryChannel, + isTextChannel, + isVoiceChannel, +} from '@sapphire/discord.js-utilities'; export class RolesJoinServerListener extends Listener { public constructor( @@ -77,14 +76,11 @@ export class RolesJoinServerListener extends Listener { // Add user to the restricted vegan channel if (section === 5) { - const restrictedCategory = member.guild.channels.cache.get( + const restrictedCategory = await getCategoryChannel( IDs.categories.restricted, ); - if ( - restrictedCategory !== undefined && - restrictedCategory.type === ChannelType.GuildCategory - ) { - await this.restrictRun(member.id, restrictedCategory, member.guild); + if (isCategoryChannel(restrictedCategory)) { + await this.restrictRun(member.id, restrictedCategory); } } } @@ -103,74 +99,77 @@ export class RolesJoinServerListener extends Listener { await member.roles.add(roles); } - const privateCategory = member.guild.channels.cache.get( - IDs.categories.private, - ); + const privateCategory = await getCategoryChannel(IDs.categories.private); - if ( - privateCategory !== undefined && - privateCategory.type === ChannelType.GuildCategory - ) { - await this.privateRun(member.id, privateCategory, member.guild); + if (isCategoryChannel(privateCategory)) { + await this.privateRun(member.id, privateCategory); } // TODO add access back to diversity team } - private async restrictRun( - userId: Snowflake, - category: CategoryChannel, - guild: Guild, - ) { - const textChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildText, + private async restrictRun(userId: Snowflake, category: CategoryChannel) { + const textChannels = category.children.cache.filter((channel) => + isTextChannel(channel), ); - textChannels.forEach((c) => { - const textChannel = c as TextChannel; + + for (const c of textChannels) { + const channel = c[1]; + + if (!isTextChannel(channel)) { + continue; + } + // Checks if the channel topic has the user's snowflake - if (textChannel.topic?.includes(userId)) { - const topic = textChannel.topic.split(' '); + if (channel.topic !== null && channel.topic.includes(userId)) { + const topic = channel.topic.split(' '); const vcId = topic[topic.indexOf(userId) + 1]; - const voiceChannel = guild.channels.cache.get(vcId); + const voiceChannel = await getVoiceChannel(vcId); if ( - voiceChannel !== undefined && - voiceChannel.parentId === IDs.categories.restricted && - voiceChannel.isVoiceBased() + isVoiceChannel(voiceChannel) && + voiceChannel.parentId === IDs.categories.restricted ) { - voiceChannel.permissionOverwrites.edit(userId, { ViewChannel: true }); + await voiceChannel.permissionOverwrites.edit(userId, { + ViewChannel: true, + }); } - textChannel.permissionOverwrites.edit(userId, { ViewChannel: true }); + + await channel.permissionOverwrites.edit(userId, { ViewChannel: true }); } - }); + } } - private async privateRun( - userId: Snowflake, - category: CategoryChannel, - guild: Guild, - ) { - const textChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildText, + private async privateRun(userId: Snowflake, category: CategoryChannel) { + const textChannels = category.children.cache.filter((channel) => + isTextChannel(channel), ); - textChannels.forEach((c) => { - const textChannel = c as TextChannel; + + for (const c of textChannels) { + const channel = c[1]; + + if (!isTextChannel(channel)) { + continue; + } + // Checks if the channel topic has the user's snowflake - if (textChannel.topic?.includes(userId)) { - const topic = textChannel.topic.split(' '); + if (channel.topic !== null && channel.topic.includes(userId)) { + const topic = channel.topic.split(' '); const vcId = topic[topic.indexOf(userId) + 2]; - const voiceChannel = guild.channels.cache.get(vcId); + const voiceChannel = await getVoiceChannel(vcId); if ( - voiceChannel !== undefined && - voiceChannel.parentId === IDs.categories.private && - voiceChannel.isVoiceBased() + isVoiceChannel(voiceChannel) && + voiceChannel.parentId === IDs.categories.private ) { - voiceChannel.permissionOverwrites.edit(userId, { ViewChannel: true }); + await voiceChannel.permissionOverwrites.edit(userId, { + ViewChannel: true, + }); } - textChannel.permissionOverwrites.edit(userId, { ViewChannel: true }); + + await channel.permissionOverwrites.edit(userId, { ViewChannel: true }); } - }); + } } private blockedRole(role: Snowflake) { diff --git a/src/listeners/suggestions.ts b/src/listeners/suggestions.ts index 1b9c05f..6a29725 100644 --- a/src/listeners/suggestions.ts +++ b/src/listeners/suggestions.ts @@ -21,6 +21,8 @@ import { Listener } from '@sapphire/framework'; import { EmbedBuilder } from 'discord.js'; import type { Message } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isTextChannel } from '@sapphire/discord.js-utilities'; export class Suggestions extends Listener { public constructor( @@ -38,14 +40,19 @@ export class Suggestions extends Listener { return; } - const mailbox = await this.container.client.channels.cache.get( - IDs.channels.staff.mailbox, - ); + const mailbox = await getTextBasedChannel(IDs.channels.staff.mailbox); - if (mailbox === undefined || !mailbox.isTextBased()) { + if (!isTextChannel(mailbox)) { this.container.logger.error( 'Mailbox is not a TextBased channel or is undefined', ); + + return; + } else if (!mailbox.isSendable()) { + this.container.logger.error( + 'Suggestions: The bot does not have permissions to send messages in the mailbox!', + ); + return; } @@ -83,11 +90,6 @@ export class Suggestions extends Listener { return; } - if (!mailbox.isSendable()) { - // TODO manage logging/errors properly - return; - } - const sent = await mailbox.send({ embeds: [suggestion], content: message.author.toString(), diff --git a/src/listeners/vcMute.ts b/src/listeners/vcMute.ts index d393bf8..ac77361 100644 --- a/src/listeners/vcMute.ts +++ b/src/listeners/vcMute.ts @@ -20,6 +20,7 @@ import { Listener } from '@sapphire/framework'; import type { VoiceState } from 'discord.js'; import { checkActive, removeMute } from '#utils/database/moderation/vcMute'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class VCMuteListener extends Listener { public constructor( @@ -37,7 +38,7 @@ export class VCMuteListener extends Listener { if (oldState.channel === null && newState.channel !== null) { const { member } = newState; - if (member === null) { + if (!isGuildMember(member)) { this.container.logger.error( 'VCMute Listener - GuildMember not found when joining', ); diff --git a/src/listeners/verification/joinVC.ts b/src/listeners/verification/joinVC.ts index b32b2e3..ecebd1d 100644 --- a/src/listeners/verification/joinVC.ts +++ b/src/listeners/verification/joinVC.ts @@ -19,10 +19,8 @@ import { container, Listener } from '@sapphire/framework'; import type { - CategoryChannel, ColorResolvable, TextChannel, - VoiceChannel, VoiceState, GuildMember, Guild, @@ -30,7 +28,6 @@ import type { } from 'discord.js'; import { time, - ChannelType, PermissionsBitField, ButtonBuilder, ButtonInteraction, @@ -54,6 +51,16 @@ import { findNotes } from '#utils/database/moderation/sus'; import { addExistingUser } from '#utils/database/dbExistingUser'; import { rolesToString } from '#utils/formatter'; import IDs from '#utils/ids'; +import { + getCategoryChannel, + getGuildMember, + getVoiceChannel, +} from '#utils/fetcher'; +import { + isCategoryChannel, + isGuildMember, + isVoiceChannel, +} from '@sapphire/discord.js-utilities'; export class VerificationJoinVCListener extends Listener { public constructor( @@ -84,20 +91,19 @@ export class VerificationJoinVCListener extends Listener { const { client } = container; const guild = client.guilds.cache.get(newState.guild.id); - if (channel === null || member === null || guild === undefined) { + if (member === null || guild === undefined) { this.container.logger.error('Verification channel not found'); return; } // Get current category and channel - const categoryGuild = guild.channels.cache.get(IDs.categories.verification); - const currentChannelGuild = guild.channels.cache.get(channel.id); - if (currentChannelGuild === undefined || categoryGuild === undefined) { + const category = await getCategoryChannel(IDs.categories.verification); + const currentChannel = await getVoiceChannel(channel.id); + + if (!isCategoryChannel(category) || !isVoiceChannel(currentChannel)) { this.container.logger.error('Verification channel not found'); return; } - const currentChannel = currentChannelGuild as VoiceChannel; - const category = categoryGuild as CategoryChannel; const roles: Snowflake[] = []; @@ -167,8 +173,8 @@ export class VerificationJoinVCListener extends Listener { } // Check how many voice channels there are - const listVoiceChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildVoice, + const listVoiceChannels = category.children.cache.filter((channel) => + isVoiceChannel(channel), ); // Create a text channel for verifiers only @@ -300,11 +306,12 @@ export class VerificationJoinVCListener extends Listener { i += 1 ) { // Get mod name - const modGuildMember = guild.members.cache.get(notes[i].modId); + const modGuildMember = await getGuildMember(notes[i].modId, guild); let mod = notes[i].modId; - if (modGuildMember !== undefined) { + if (isGuildMember(modGuildMember)) { mod = modGuildMember.displayName; } + // Add sus note to embed embed.addFields({ name: `Sus ID: ${ @@ -495,10 +502,8 @@ export class VerificationJoinVCListener extends Listener { // Confirming and finishing the verification if (button.customId === 'confirm' && info.page >= questionLength) { // Check verifier is on the database - const verifierGuildMember = await guild.members.cache.get( - button.user.id, - ); - if (verifierGuildMember === undefined) { + const verifierGuildMember = await getGuildMember(button.user.id, guild); + if (!isGuildMember(verifierGuildMember)) { await message.edit({ content: 'Verifier not found!' }); return; } diff --git a/src/listeners/verification/leaveVC.ts b/src/listeners/verification/leaveVC.ts index 901170b..51c228d 100644 --- a/src/listeners/verification/leaveVC.ts +++ b/src/listeners/verification/leaveVC.ts @@ -18,13 +18,8 @@ */ import { Listener } from '@sapphire/framework'; -import type { - VoiceState, - CategoryChannel, - VoiceChannel, - TextChannel, -} from 'discord.js'; -import { time, ChannelType } from 'discord.js'; +import type { VoiceState } from 'discord.js'; +import { time } from 'discord.js'; import { createVerificationVoice } from '#utils/verification'; import { maxVCs, leaveBan } from '#utils/verificationConfig'; import { @@ -35,6 +30,13 @@ import { import { fetchRoles } from '#utils/database/dbExistingUser'; import { fibonacci } from '#utils/maths'; import IDs from '#utils/ids'; +import { + isCategoryChannel, + isGuildMember, + isTextChannel, + isVoiceChannel, +} from '@sapphire/discord.js-utilities'; +import { getCategoryChannel, getGuildMember } from '#utils/fetcher'; export class VerificationLeaveVCListener extends Listener { public constructor( @@ -63,46 +65,44 @@ export class VerificationLeaveVCListener extends Listener { const { channel } = oldState; const { guild } = newState; - if (channel === null || guild === undefined) { + if (!isVoiceChannel(channel) || guild === undefined) { this.container.logger.error('Verification channel not found'); return; } // Get the category - const categoryGuild = guild.channels.cache.get(IDs.categories.verification); - if (categoryGuild === null) { + const category = await getCategoryChannel(IDs.categories.verification); + if (!isCategoryChannel(category)) { this.container.logger.error('Verification channel not found'); return; } - const category = categoryGuild as CategoryChannel; // Get the user that was being verified const userSnowflake = await getUser(channel.id); if (userSnowflake === null) { verifier = true; - } - - // Allow more people to join VC if there are less than 10 VCs - - if (!verifier) { - const user = guild.members.cache.get(userSnowflake!); + } else { + // Allow more people to join VC if there are less than 10 VCs + const member = await getGuildMember(userSnowflake, guild); // Remove verify as vegan and give non vegan role - if (!(await checkFinish(channel.id)) && user !== undefined) { + if (!(await checkFinish(channel.id)) && isGuildMember(member)) { // Get roles to give back to the user - const roles = await fetchRoles(user.id); + const roles = await fetchRoles(member.id); roles.push(IDs.roles.verifyBlock); - await user.roles + + await member.roles .add(roles) .catch(() => this.container.logger.error( 'Verification: User left but bot still tried to add roles', ), ); + // Create timeout block for user // Counts the recent times they have incomplete verifications const incompleteCount = - (await countIncomplete(user.id)) % (leaveBan + 1); + (await countIncomplete(member.id)) % (leaveBan + 1); // Creates the length of the time for the ban const banLength = fibonacci(incompleteCount) * 3600_000; @@ -110,14 +110,14 @@ export class VerificationLeaveVCListener extends Listener { { name: 'verifyUnblock', payload: { - userId: user.id, + userId: member.id, guildId: guild.id, }, }, banLength, ); - await user.user + await member.user .send( 'You have been timed out as a verifier had not joined for 15 minutes or you disconnected from verification.\n\n' + `You can verify again at: ${time( @@ -129,8 +129,8 @@ export class VerificationLeaveVCListener extends Listener { } // Check how many voice channels there are - const listVoiceChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildVoice, + const listVoiceChannels = category.children.cache.filter((channel) => + isVoiceChannel(channel), ); // Check that it is not deleting the 'Verification' channel (in case bot crashes) @@ -142,19 +142,22 @@ export class VerificationLeaveVCListener extends Listener { // Delete text channel if (!verifier) { // Gets a list of all the text channels in the verification category - const listTextChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildText, + const listTextChannels = category.children.cache.filter((channel) => + isTextChannel(channel), ); - listTextChannels.forEach((c) => { - const textChannel = c as TextChannel; - // Checks if the channel topic has the user's snowflake - if ( - textChannel.topic !== null && - textChannel.topic.includes(userSnowflake!) - ) { - textChannel.delete(); + + for (const c of listTextChannels) { + const channel = c[1]; + + if (!isTextChannel(channel)) { + continue; } - }); + + // Checks if the channel topic has the user's snowflake + if (channel.topic !== null && channel.topic.includes(userSnowflake!)) { + await channel.delete(); + } + } } // If there are no VCs left in verification after having the channel deleted @@ -168,9 +171,9 @@ export class VerificationLeaveVCListener extends Listener { return; } - const verification = listVoiceChannels.last() as VoiceChannel | undefined; + const verification = listVoiceChannels.last(); - if (verification === undefined) { + if (!isVoiceChannel(verification)) { this.container.logger.error( 'Verification: Verification channel not found.', ); diff --git a/src/listeners/verification/start.ts b/src/listeners/verification/start.ts index c48efe8..092ed5d 100644 --- a/src/listeners/verification/start.ts +++ b/src/listeners/verification/start.ts @@ -18,15 +18,15 @@ */ import { Listener } from '@sapphire/framework'; -import type { - Client, - CategoryChannel, - TextChannel, - VoiceChannel, -} from 'discord.js'; -import { ChannelType } from 'discord.js'; +import type { VoiceChannel } from 'discord.js'; import { createVerificationVoice } from '#utils/verification'; import IDs from '#utils/ids'; +import { getCategoryChannel } from '#utils/fetcher'; +import { + isCategoryChannel, + isTextChannel, + isVoiceChannel, +} from '@sapphire/discord.js-utilities'; export class VerificationReady extends Listener { public constructor( @@ -40,62 +40,67 @@ export class VerificationReady extends Listener { }); } - public async run(client: Client) { + public async run() { // Get verification category - let category = client.channels.cache.get(IDs.categories.verification) as - | CategoryChannel - | undefined; - if (category === undefined) { - category = (await client.channels.fetch(IDs.categories.verification)) as - | CategoryChannel - | undefined; - if (category === undefined) { - this.container.logger.error('verifyStart: Channel not found'); - return; - } + const category = await getCategoryChannel(IDs.categories.verification); + + if (!isCategoryChannel(category)) { + this.container.logger.error('verifyStart: Channel not found'); + return; } // Check how many voice channels there are - const voiceChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildVoice, + const voiceChannels = category.children.cache.filter((channel) => + isVoiceChannel(channel), ); const currentVCs: VoiceChannel[] = []; const emptyVC: string[] = []; + // Delete voice channels - voiceChannels.forEach((c) => { - const voiceChannel = c as VoiceChannel; - if (voiceChannel.members.size === 0) { - emptyVC.push(voiceChannel.id); - voiceChannel.delete(); - } else { - currentVCs.push(voiceChannel); + for (const c of voiceChannels) { + const channel = c[1]; + + if (!isVoiceChannel(channel)) { + continue; } - }); + + if (channel.members.size === 0) { + emptyVC.push(channel.id); + await channel.delete(); + } else { + currentVCs.push(channel); + } + } // Delete text channels - const textChannels = category.children.cache.filter( - (c) => c.type === ChannelType.GuildText, + const textChannels = category.children.cache.filter((channel) => + isTextChannel(channel), ); - textChannels.forEach((c) => { - const textChannel = c as TextChannel; + + for (const c of textChannels) { + const channel = c[1]; + + if (!isTextChannel(channel)) { + continue; + } + // Checks if the channel topic has the user's snowflake - emptyVC.forEach((snowflake) => { - if ( - textChannel.topic !== null && - textChannel.topic.includes(snowflake) - ) { - textChannel.delete(); + for (const snowflake in emptyVC) { + if (channel.topic !== null && channel.topic.includes(snowflake)) { + await channel.delete(); } - }); - }); + } + } // Check if there is no voice channels, create verification let verification = false; - currentVCs.forEach((c) => { - if (c.name === 'Verification') { + + currentVCs.forEach((channel) => { + if (channel.name === 'Verification') { verification = true; } }); + if (!verification) { await createVerificationVoice(category); } diff --git a/src/listeners/verification/trusted.ts b/src/listeners/verification/trusted.ts index 3038d4a..00b35d8 100644 --- a/src/listeners/verification/trusted.ts +++ b/src/listeners/verification/trusted.ts @@ -21,6 +21,8 @@ import { Listener } from '@sapphire/framework'; import { GuildMember } from 'discord.js'; import IDs from '#utils/ids'; import { noModHistory, userPreviouslyHadRole } from '#utils/database/memberMod'; +import { getRole } from '#utils/fetcher'; +import { isRole } from '#utils/typeChecking'; /** * Gives the trusted role to users who have levelled up to level 5 @@ -51,9 +53,9 @@ export class TrustedListener extends Listener { } const { guild } = member; - const trusted = guild.roles.cache.get(IDs.roles.trusted); + const trusted = await getRole(IDs.roles.trusted, guild); - if (trusted === undefined) { + if (!isRole(trusted)) { this.container.logger.error( 'TrustedXP Listener: the Trusted role could not be found in the guild.', ); diff --git a/src/listeners/verification/welcome.ts b/src/listeners/verification/welcome.ts index 9da68ff..32f5b5a 100644 --- a/src/listeners/verification/welcome.ts +++ b/src/listeners/verification/welcome.ts @@ -20,8 +20,9 @@ import { Listener } from '@sapphire/framework'; import { ButtonStyle, ActionRowBuilder, ButtonBuilder } from 'discord.js'; -import type { Client, TextChannel } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class VerificationReady extends Listener { public constructor( @@ -35,19 +36,13 @@ export class VerificationReady extends Listener { }); } - public async run(client: Client) { + public async run() { // Get verification category - let welcome = client.channels.cache.get(IDs.channels.welcome) as - | TextChannel - | undefined; - if (welcome === undefined) { - welcome = (await client.channels.fetch(IDs.channels.welcome)) as - | TextChannel - | undefined; - if (welcome === undefined) { - this.container.logger.error('verifyStart: Welcome not found'); - return; - } + const welcome = await getTextBasedChannel(IDs.channels.welcome); + + if (!isTextBasedChannel(welcome)) { + this.container.logger.error('verifyStart: Welcome not found'); + return; } const botId = this.container.client.id; diff --git a/src/scheduled-tasks/messages/diversityMon.ts b/src/scheduled-tasks/messages/diversityMon.ts index 1109bb1..2923cdd 100644 --- a/src/scheduled-tasks/messages/diversityMon.ts +++ b/src/scheduled-tasks/messages/diversityMon.ts @@ -18,9 +18,9 @@ */ import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks'; -import { container } from '@sapphire/framework'; -import type { TextChannel } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class DiversityMonMessageTask extends ScheduledTask { public constructor( @@ -34,8 +34,6 @@ export class DiversityMonMessageTask extends ScheduledTask { } public async run() { - const { client } = container; - const message = '**📌 Diversity Section Code of Conduct**\n\n' + '❤️ Be *Kind*\n' + @@ -47,12 +45,16 @@ export class DiversityMonMessageTask extends ScheduledTask { '❤️ Respect the creativity of others.\n' + '🧡 Actively seek to include others, especially moderators, in heated discourse for the purpose of de-escalation.'; - const lgbtqia = client.channels.cache.get( - IDs.channels.diversity.lgbtqia, - ) as TextChannel; - const potgm = client.channels.cache.get( - IDs.channels.diversity.potgm, - ) as TextChannel; + const lgbtqia = await getTextBasedChannel(IDs.channels.diversity.lgbtqia); + const potgm = await getTextBasedChannel(IDs.channels.diversity.potgm); + + if (!isTextBasedChannel(lgbtqia) || !isTextBasedChannel(potgm)) { + this.container.logger.error( + 'Diversity Monday: The bot could not find both of the channels!', + ); + + return; + } await lgbtqia.send(message); await potgm.send(message); diff --git a/src/scheduled-tasks/messages/diversityWed.ts b/src/scheduled-tasks/messages/diversityWed.ts index 413cca7..d1e0045 100644 --- a/src/scheduled-tasks/messages/diversityWed.ts +++ b/src/scheduled-tasks/messages/diversityWed.ts @@ -18,9 +18,9 @@ */ import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks'; -import { container } from '@sapphire/framework'; -import type { TextChannel } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class DiversityWedMessageTask extends ScheduledTask { public constructor( @@ -34,8 +34,6 @@ export class DiversityWedMessageTask extends ScheduledTask { } public async run() { - const { client } = container; - const message = '**📌 Diversity Section Code of Conduct**\n\n' + '❤️ Be *Kind*\n' + @@ -47,12 +45,18 @@ export class DiversityWedMessageTask extends ScheduledTask { '❤️ Respect the creativity of others.\n' + '🧡 Actively seek to include others, especially moderators, in heated discourse for the purpose of de-escalation.'; - const women = client.channels.cache.get( - IDs.channels.diversity.women, - ) as TextChannel; - const disabilities = client.channels.cache.get( + const women = await getTextBasedChannel(IDs.channels.diversity.women); + const disabilities = await getTextBasedChannel( IDs.channels.diversity.disabilities, - ) as TextChannel; + ); + + if (!isTextBasedChannel(women) || !isTextBasedChannel(disabilities)) { + this.container.logger.error( + 'Diversity Wednesday: The bot could not find both of the channels!', + ); + + return; + } await women.send(message); await disabilities.send(message); diff --git a/src/scheduled-tasks/messages/restrictedMessage.ts b/src/scheduled-tasks/messages/restrictedMessage.ts index 4e378e2..154bf43 100644 --- a/src/scheduled-tasks/messages/restrictedMessage.ts +++ b/src/scheduled-tasks/messages/restrictedMessage.ts @@ -18,9 +18,9 @@ */ import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks'; -import { container } from '@sapphire/framework'; -import type { TextChannel } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class RestrictedMessageTask extends ScheduledTask { public constructor( @@ -34,14 +34,20 @@ export class RestrictedMessageTask extends ScheduledTask { } public async run() { - const { client } = container; - - const restricted = client.channels.cache.get( + const restricted = await getTextBasedChannel( IDs.channels.restricted.restricted, - ) as TextChannel; - const tolerance = client.channels.cache.get( + ); + const tolerance = await getTextBasedChannel( IDs.channels.restricted.tolerance, - ) as TextChannel; + ); + + if (!isTextBasedChannel(restricted) || !isTextBasedChannel(tolerance)) { + this.container.logger.error( + 'Restricted Reminder: The bot could not find both of the channels!', + ); + + return; + } await restricted.send(this.message(IDs.roles.restrictions.restricted1)); await tolerance.send(this.message(IDs.roles.restrictions.restricted3)); diff --git a/src/scheduled-tasks/messages/standup.ts b/src/scheduled-tasks/messages/standup.ts index 9290430..8b936ff 100644 --- a/src/scheduled-tasks/messages/standup.ts +++ b/src/scheduled-tasks/messages/standup.ts @@ -18,9 +18,9 @@ */ import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks'; -import { container } from '@sapphire/framework'; -import type { TextChannel } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class StandupTask extends ScheduledTask { public constructor( @@ -34,11 +34,15 @@ export class StandupTask extends ScheduledTask { } public async run() { - const { client } = container; + const channel = await getTextBasedChannel(IDs.channels.staff.coordinators); - const channel = client.channels.cache.get( - IDs.channels.staff.coordinators, - ) as TextChannel; + if (!isTextBasedChannel(channel)) { + this.container.logger.error( + 'Standup: The bot could not find the channel to post the standup in!', + ); + + return; + } await channel.send(`Hiya <@&${IDs.roles.staff.coordinator}> it's time for your weekly standup! \nPlease submit it in <#${IDs.channels.staff.standup}> :)`); diff --git a/src/scheduled-tasks/messages/verifyReminder.ts b/src/scheduled-tasks/messages/verifyReminder.ts index c72fed2..6bd1ea5 100644 --- a/src/scheduled-tasks/messages/verifyReminder.ts +++ b/src/scheduled-tasks/messages/verifyReminder.ts @@ -18,9 +18,9 @@ */ import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks'; -import { container } from '@sapphire/framework'; -import type { TextChannel } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { isTextBasedChannel } from '@sapphire/discord.js-utilities'; export class VerifyReminder extends ScheduledTask { public constructor( @@ -34,8 +34,6 @@ export class VerifyReminder extends ScheduledTask { } public async run() { - const { client } = container; - // Get the total messages sent in non-vegan general since last message const redisKey = 'verifyReminderMessageCounter'; @@ -45,9 +43,15 @@ export class VerifyReminder extends ScheduledTask { if (!messageCount || +messageCount < 100) return; // Send verification reminder to non-vegan general - const channel = client.channels.cache.get( - IDs.channels.nonVegan.general, - ) as TextChannel; + const channel = await getTextBasedChannel(IDs.channels.nonVegan.general); + + if (!isTextBasedChannel(channel)) { + this.container.logger.error( + 'Verify Reminder: The bot could not find the channel to post the reminder!', + ); + + return; + } await channel.send( "If you want to have the vegan or activist role, you'll need to do a voice verification. " + diff --git a/src/scheduled-tasks/tempBan.ts b/src/scheduled-tasks/tempBan.ts index a230bdf..f43d399 100644 --- a/src/scheduled-tasks/tempBan.ts +++ b/src/scheduled-tasks/tempBan.ts @@ -25,6 +25,9 @@ import { checkTempBan, removeTempBan, } from '#utils/database/moderation/tempBan'; +import { getGuild, getTextBasedChannel, getUser } from '#utils/fetcher'; +import { isUser } from '#utils/typeChecking'; +import { isTextChannel } from '@sapphire/discord.js-utilities'; export class TempBan extends ScheduledTask { public constructor( @@ -36,30 +39,24 @@ export class TempBan extends ScheduledTask { public async run(payload: { userId: string; guildId: string }) { this.container.logger.debug('Temp Unban Task: Currently running unban'); + // Get the guild where the user is in - let guild = this.container.client.guilds.cache.get(payload.guildId); + const guild = await getGuild(payload.guildId); + if (guild === undefined) { - guild = await this.container.client.guilds - .fetch(payload.guildId) - .catch(() => undefined); - if (guild === undefined) { - this.container.logger.error('Temp Unban Task: Guild not found!'); - return; - } + this.container.logger.error('Temp Unban Task: Guild not found!'); + return; } const { userId } = payload; - let user = guild.client.users.cache.get(userId); + const user = await getUser(userId); - if (user === undefined) { - user = await guild.client.users.fetch(userId).catch(() => undefined); - if (user === undefined) { - this.container.logger.error( - 'Temp Unban Task: Could not fetch banned user!', - ); - return; - } + if (!isUser(user)) { + this.container.logger.error( + 'Temp Unban Task: Could not fetch banned user!', + ); + return; } if ((await checkBan(userId)) || !(await checkTempBan(userId))) { @@ -75,25 +72,11 @@ export class TempBan extends ScheduledTask { await removeTempBan(userId); // Log unban - let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted); + const logChannel = await getTextBasedChannel(IDs.channels.logs.restricted); - if (logChannel === undefined) { - const logChannelFetch = await guild.channels - .fetch(IDs.channels.logs.restricted) - .catch(() => null); - if (logChannelFetch === null) { - this.container.logger.error( - `Temp Ban Listener: Could not fetch log channel. User Snowflake: ${userId}`, - ); - return; - } - - logChannel = logChannelFetch; - } - - if (!logChannel.isTextBased()) { + if (!isTextChannel(logChannel)) { this.container.logger.error( - 'Temp Ban Listener: Log channel is not a text based channel!', + `Temp Ban Listener: Could not fetch log channel. User Snowflake: ${userId}`, ); return; } diff --git a/src/scheduled-tasks/verifyTimeout.ts b/src/scheduled-tasks/verifyTimeout.ts index c38c91f..cc88783 100644 --- a/src/scheduled-tasks/verifyTimeout.ts +++ b/src/scheduled-tasks/verifyTimeout.ts @@ -18,7 +18,11 @@ */ import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks'; -import { ChannelType } from 'discord.js'; +import { getGuildMember, getVoiceBasedChannel } from '#utils/fetcher'; +import { + isGuildMember, + isVoiceBasedChannel, +} from '@sapphire/discord.js-utilities'; export class VerifyTimeout extends ScheduledTask { public constructor( @@ -30,29 +34,17 @@ export class VerifyTimeout extends ScheduledTask { public async run(payload: { channelId: string; userId: string }) { // Get the guild where the user is in - let channel = this.container.client.channels.cache.get(payload.channelId); - if (channel === undefined) { - const channelFetch = await this.container.client.channels - .fetch(payload.channelId) - .catch(() => null); - if (channelFetch === null) { - this.container.logger.error('verifyTimeout: Channel not found!'); - return; - } + const channel = await getVoiceBasedChannel(payload.channelId); - channel = channelFetch; - } - - if (channel.type !== ChannelType.GuildVoice) { - this.container.logger.error( - 'verifyTimeout: Channel is not a voice channel!', - ); + if (!isVoiceBasedChannel(channel)) { + this.container.logger.error('verifyTimeout: Channel not found!'); return; } if (channel.members.size < 2 && channel.members.has(payload.userId)) { - const user = channel.members.get(payload.userId); - if (user === undefined) { + const user = await getGuildMember(payload.userId, channel.guild); + + if (!isGuildMember(user)) { this.container.logger.error('verifyTimeout: GuildMember not found!'); return; } diff --git a/src/scheduled-tasks/verifyUnblock.ts b/src/scheduled-tasks/verifyUnblock.ts index 5577155..b3ace16 100644 --- a/src/scheduled-tasks/verifyUnblock.ts +++ b/src/scheduled-tasks/verifyUnblock.ts @@ -19,6 +19,8 @@ import { ScheduledTask } from '@sapphire/plugin-scheduled-tasks'; import IDs from '#utils/ids'; +import { getGuild, getGuildMember } from '#utils/fetcher'; +import { isGuildMember } from '@sapphire/discord.js-utilities'; export class VerifyUnblock extends ScheduledTask { public constructor( @@ -30,29 +32,23 @@ export class VerifyUnblock extends ScheduledTask { public async run(payload: { userId: string; guildId: string }) { // Get the guild where the user is in - let guild = this.container.client.guilds.cache.get(payload.guildId); + const guild = await getGuild(payload.guildId); + if (guild === undefined) { - guild = await this.container.client.guilds - .fetch(payload.guildId) - .catch(() => undefined); - if (guild === undefined) { - this.container.logger.error('verifyUnblock: Guild not found!'); - return; - } + this.container.logger.error('verifyUnblock: Guild not found!'); + return; } // Find GuildMember for the user - let user = guild.members.cache.get(payload.userId); - if (user === undefined) { - user = await guild.members.fetch(payload.userId).catch(() => undefined); - if (user === undefined) { - this.container.logger.error('verifyUnblock: GuildMember not found!'); - return; - } + const member = await getGuildMember(payload.userId, guild); + + if (!isGuildMember(member)) { + this.container.logger.error('verifyUnblock: GuildMember not found!'); + return; } // Remove the 'verify block' role - await user.roles.remove(IDs.roles.verifyBlock); + await member.roles.remove(IDs.roles.verifyBlock); } } diff --git a/src/utils/checker.ts b/src/utils/checker.ts index 78e1301..3cfbb7e 100644 --- a/src/utils/checker.ts +++ b/src/utils/checker.ts @@ -19,7 +19,8 @@ import type { TextBasedChannel } from 'discord.js'; import IDs from '#utils/ids'; -import { ChannelType } from 'discord.js'; +import { Nullish } from '@sapphire/utilities'; +import { isTextChannel } from '@sapphire/discord.js-utilities'; /** * Checks if the channel is in the staff category. @@ -27,12 +28,8 @@ import { ChannelType } from 'discord.js'; * @returns {boolean} true if is in staff channel */ -export function checkStaff(channel: TextBasedChannel | null) { - if (channel === null) { - return false; - } - - if (channel.type !== ChannelType.GuildText) { +export function checkStaff(channel: TextBasedChannel | Nullish): boolean { + if (!isTextChannel(channel)) { return false; } diff --git a/src/utils/fetcher.ts b/src/utils/fetcher.ts new file mode 100644 index 0000000..ac8b059 --- /dev/null +++ b/src/utils/fetcher.ts @@ -0,0 +1,245 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + Animal Rights Advocates Discord Bot + Copyright (C) 2025 Anthony Berg + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import { + CategoryChannel, + Guild, + GuildMember, + Role, + Snowflake, + TextBasedChannel, + User, + VoiceBasedChannel, + VoiceChannel, +} from 'discord.js'; +import { isRole, isUser } from '#utils/typeChecking'; +import { container } from '@sapphire/framework'; +import { + isCategoryChannel, + isGuildMember, + isTextBasedChannel, + isVoiceBasedChannel, + isVoiceChannel, +} from '@sapphire/discord.js-utilities'; + +/** + * Gets a User from their Snowflake. + * Checks the cache first, if it is not in the cache, will fetch from Discord. + * @param userId Snowflake of the user fetch + */ +export async function getUser(userId: Snowflake): Promise { + // Attempts to get the User from the cache first + let user = container.client.users.cache.get(userId); + + // If the user is not in the cache, fetch from Discord + if (!isUser(user)) { + user = await container.client.users.fetch(userId).catch(() => undefined); + } + + return user; +} + +/** + * Gets the Guild from the Snowflake. + * Checks if it is in the cache first, if not, attempts to fetch from Discord. + * @param guildId the Snowflake of the Guild + */ +export async function getGuild(guildId: Snowflake): Promise { + // Attempts to get the GuildMember from the cache first + let guild = container.client.guilds.cache.get(guildId); + + // If the Role is not in the cache, fetch from Discord + if (!(guild instanceof Guild)) { + guild = await container.client.guilds.fetch(guildId).catch(() => undefined); + } + + return guild; +} + +/** + * Gets the GuildMember from their Snowflake. + * Checks if it is in the cache first, if not, attempts to fetch from Discord. + * @param memberId the Snowflake of the GuildMember + * @param guild the Guild to get the GuildMember from + */ +export async function getGuildMember( + memberId: Snowflake, + guild: Guild, +): Promise { + // Attempts to get the GuildMember from the cache first + let member = guild.members.cache.get(memberId); + + // If the GuildMember is not in the cache, fetch from Discord + if (!isGuildMember(member)) { + member = await guild.members.fetch(memberId).catch(() => undefined); + } + + return member; +} + +/** + * Gets the Role from the Snowflake. + * Checks if it is in the cache first, if not, attempts to fetch from Discord. + * @param roleId the Snowflake of the Role + * @param guild the Guild to get the Role from + */ +export async function getRole( + roleId: Snowflake, + guild: Guild, +): Promise { + // Attempts to get the Role from the cache first + const role = guild.roles.cache.get(roleId); + + // If the Role is not in the cache, fetch from Discord + if (isRole(role)) { + return role; + } + + const fetchRole = await guild.roles.fetch(roleId).catch(() => undefined); + + if (isRole(fetchRole)) { + return fetchRole; + } else { + return undefined; + } +} + +/** + * Gets a TextBasedChannel from a Snowflake. + * Checks if it is in the cache first, if not, attempts to fetch from Discord. + * @param channelId the Snowflake of the TextBasedChannel + */ +export async function getTextBasedChannel( + channelId: Snowflake, +): Promise { + // Attempts to get the TextChannel from the cache first + const channel = container.client.channels.cache.get(channelId); + + if (channel !== undefined) { + if (channel.isTextBased()) { + return channel; + } else { + return undefined; + } + } + + // Fetches the Channel from Discord if the channel is not found in cache + const fetchChannel = await container.client.channels + .fetch(channelId) + .catch(() => undefined); + + if (isTextBasedChannel(fetchChannel)) { + return fetchChannel; + } else { + return undefined; + } +} + +/** + * Gets a CategoryChannel from a Snowflake. + * Checks if it is in the cache first, if not, attempts to fetch from Discord. + * @param categoryId the Snowflake of the Category + */ +export async function getCategoryChannel( + categoryId: Snowflake, +): Promise { + // Attempts to get the CategoryChannel from the cache first + const category = container.client.channels.cache.get(categoryId); + + if (category !== undefined) { + if (isCategoryChannel(category)) { + return category; + } else { + return undefined; + } + } + + // Fetches the Channel from Discord if the channel is not found in cache + const fetchCategory = await container.client.channels + .fetch(categoryId) + .catch(() => undefined); + + if (isCategoryChannel(fetchCategory)) { + return fetchCategory; + } else { + return undefined; + } +} + +/** + * Gets a VoiceChannel from a Snowflake. + * Checks if it is in the cache first, if not, attempts to fetch from Discord. + * @param vcId the Snowflake of the VoiceChannel + */ +export async function getVoiceChannel( + vcId: Snowflake, +): Promise { + // Attempts to get the VoiceChannel from the cache first + const vc = container.client.channels.cache.get(vcId); + + if (vc !== undefined) { + if (isVoiceChannel(vc)) { + return vc; + } else { + return undefined; + } + } + + // Fetches the Channel from Discord if the channel is not found in cache + const fetchVC = await container.client.channels + .fetch(vcId) + .catch(() => undefined); + + if (isVoiceChannel(fetchVC)) { + return fetchVC; + } else { + return undefined; + } +} + +/** + * Gets a VoiceBasedChannel from a Snowflake. + * Checks if it is in the cache first, if not, attempts to fetch from Discord. + * @param vcId the Snowflake of the VoiceBasedChannel + */ +export async function getVoiceBasedChannel( + vcId: Snowflake, +): Promise { + // Attempts to get the VoiceBasedChannel from the cache first + const vc = container.client.channels.cache.get(vcId); + + if (vc !== undefined) { + if (isVoiceBasedChannel(vc)) { + return vc; + } else { + return undefined; + } + } + + // Fetches the Channel from Discord if the channel is not found in cache + const fetchVC = await container.client.channels + .fetch(vcId) + .catch(() => undefined); + + if (isVoiceBasedChannel(fetchVC)) { + return fetchVC; + } else { + return undefined; + } +} diff --git a/src/utils/typeChecking.ts b/src/utils/typeChecking.ts new file mode 100644 index 0000000..5ef9de7 --- /dev/null +++ b/src/utils/typeChecking.ts @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + Animal Rights Advocates Discord Bot + Copyright (C) 2025 Anthony Berg + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import { Role, User } from 'discord.js'; +import { Nullish } from '@sapphire/utilities'; + +/** + * Checks if a user is a `User`, and that they are not `undefined`/`null`. + * @param user The user to check + */ +export function isUser(user: User | Nullish): user is User { + return user instanceof User; +} + +/** + * Checks if the role is a `Role` type, and they are not `undefined`/`null`. + * @param role the role to check + */ +export function isRole(role: Role | Nullish): role is Role { + return role instanceof Role; +} diff --git a/src/utils/verification.ts b/src/utils/verification.ts index faa2a34..bed0a6c 100644 --- a/src/utils/verification.ts +++ b/src/utils/verification.ts @@ -23,12 +23,16 @@ import { ChannelType, GuildMember, PermissionsBitField, - TextChannel, time, User, } from 'discord.js'; import type { Snowflake, VoiceBasedChannel } from 'discord.js'; import IDs from '#utils/ids'; +import { getTextBasedChannel } from '#utils/fetcher'; +import { + isDMChannel, + isTextBasedChannel, +} from '@sapphire/discord.js-utilities'; export async function createVerificationText( member: GuildMember, @@ -252,12 +256,16 @@ export async function finishVerifyMessages( // Not vegan if (!roles.vegan) { - const general = container.client.channels.cache.get( - IDs.channels.nonVegan.general, - ) as TextChannel | undefined; - if (general === undefined) { + const general = await getTextBasedChannel(IDs.channels.nonVegan.general); + + if (!isTextBasedChannel(general)) { + container.logger.error( + 'Verification: Could not find general chat to welcome a non-vegan!', + ); + return; } + let msg = `${user}, you have been verified! Please check <#${IDs.channels.information.roles}> ` + `and remember to follow the <#${IDs.channels.information.conduct}> and to respect ongoing discussion and debates.`; @@ -270,10 +278,13 @@ export async function finishVerifyMessages( } // Vegan - const general = container.client.channels.cache.get( - IDs.channels.vegan.general, - ) as TextChannel | undefined; - if (general === undefined) { + const general = await getTextBasedChannel(IDs.channels.vegan.general); + + if (!isTextBasedChannel(general)) { + container.logger.error( + 'Verification: Could not find general chat to welcome a vegan!', + ); + return; } const msg = `Welcome ${user}! Please check out <#${IDs.channels.information.roles}> :)`; @@ -289,14 +300,16 @@ export async function finishVerifyMessages( '3. Have evidence for claims you make. "I don\'t know" is an acceptable answer. Chances are someone here knows or you can take time to find out\n' + "4. Don't advocate for baby steps towards veganism. Participation in exploitation can stop today\n" + '5. Differences in opinion between activists should be resolved in vegan spaces, not in the chat with non-vegans'; - await user.send(activistMsg).catch(() => { - const activist = container.client.channels.cache.get( + await user.send(activistMsg).catch(async () => { + const activist = await getTextBasedChannel( IDs.channels.activism.activism, - ) as TextChannel | undefined; - if (activist === undefined) { + ); + + if (!isDMChannel(activist)) { return; } - activist.send(activistMsg); + + await activist.send(activistMsg); }); } }