31 Commits

Author SHA1 Message Date
Anthony Berg
132e3cc62b refactor(arabot): create separate functions for fetching and checking types of users, channels, etc
Some checks failed
Prettier / Run prettier scanning (push) Successful in 3m4s
ESLint / Run eslint scanning (push) Failing after 3m47s
CodeQL / Analyze (javascript) (push) Failing after 11m2s
2025-01-19 16:56:07 +01:00
Anthony Berg
db9204c115 build(arabot): update dependencies 2025-01-19 16:48:27 +01:00
Anthony Berg
0419488b6f refactor(arabot): change deprecated reply options 2025-01-19 13:21:33 +01:00
Anthony Berg
2207d996a1 docs(arabot): add TypeScript docs returns for dbExistingUser
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
ESLint / Run eslint scanning (push) Waiting to run
Prettier / Run prettier scanning (push) Waiting to run
2025-01-18 13:07:14 +01:00
Anthony Berg
8f60c3eac9 feat(arabot): add user to database when they join the server 2025-01-18 12:38:34 +01:00
Anthony Berg
c976905104 refactor(arabot): make checks in WelcomeButtonHandler more thorough 2025-01-18 12:29:28 +01:00
Anthony Berg
cd4526bfb1 refactor(arabot): remove deprecated options in nonVeganAccess 2025-01-18 12:00:50 +01:00
Anthony Berg
e44c0c2037 revert(arabot): undo the fixRoles command being only for giving nonvegan roles 2025-01-18 11:57:54 +01:00
Anthony Berg
9b3908cdc4 fix(arabot): add delay to skipping in fixRoles
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
ESLint / Run eslint scanning (push) Waiting to run
Prettier / Run prettier scanning (push) Waiting to run
2025-01-17 22:55:47 +01:00
Anthony Berg
513d3ec581 fix(arabot): prevent readding nonvegan role in fixRoles 2025-01-17 22:54:35 +01:00
Anthony Berg
2518376f3b fix(arabot): prevent readding nonvegan role in fixRoles 2025-01-17 22:51:14 +01:00
Anthony Berg
f4f83e51b2 refactor(arabot): change timings for fixRoles 2025-01-17 22:48:19 +01:00
Anthony Berg
616334f123 fix(arabot): counting for fixRoles
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
ESLint / Run eslint scanning (push) Waiting to run
Prettier / Run prettier scanning (push) Waiting to run
2025-01-17 16:16:08 +01:00
Anthony Berg
cd319609b0 fix(arabot): add snowflake for channel logs 2025-01-17 16:14:09 +01:00
Anthony Berg
a1469e0596 feat(arabot): process non-vegan roles 2025-01-17 16:07:05 +01:00
Anthony Berg
ca0e43a70e feat(arabot): add role for restricted vegan 2025-01-17 15:59:54 +01:00
Anthony Berg
dc16dee92c fix(arabot): typo in fixRoles 2025-01-17 15:34:22 +01:00
Anthony Berg
71f0ee9f01 refactor(arabot): cleanup sus command from deprecated functions
Some checks failed
CodeQL / Analyze (javascript) (push) Has been cancelled
ESLint / Run eslint scanning (push) Has been cancelled
Prettier / Run prettier scanning (push) Has been cancelled
2025-01-16 00:58:11 +01:00
Anthony Berg
881f9bfc24 fix(arabot): new sus role was put in the wrong place 2025-01-16 00:57:25 +01:00
Anthony Berg
98b9ac6fde feat(arabot): add a check if the user is restricted when trying to gain access to the server 2025-01-15 20:46:25 +01:00
Anthony Berg
1f92bf5d68 fix: update the role snowflake to new ones 2025-01-15 20:35:47 +01:00
Anthony Berg
d9f04e8d49 Revert "fix: remove accidentally given nv roles from vegans"
This reverts commit b4c8f0785c.
2025-01-15 20:31:08 +01:00
Anthony Berg
b4c8f0785c fix: remove accidentally given nv roles from vegans 2025-01-15 20:26:49 +01:00
Anthony Berg
7918f73e7d feat: turn off the fixer for the roles reassignment 2025-01-15 20:06:22 +01:00
Anthony Berg
ea211a9111 feat: add fixer for when roles get recreated 2025-01-15 19:20:07 +01:00
Anthony Berg
32776a2311 feat: add log on Discord when bot has started
Some checks are pending
CodeQL / Analyze (javascript) (push) Waiting to run
ESLint / Run eslint scanning (push) Waiting to run
Prettier / Run prettier scanning (push) Waiting to run
2025-01-15 17:42:54 +01:00
Anthony Berg
d72b66f988 refactor: update activist role to new snowflake 2025-01-15 16:47:48 +01:00
Anthony Berg
e03bd6e85e refactor: update roles to new snowflake 2025-01-15 16:22:31 +01:00
Anthony Berg
a400cf9507 refactor: update roles to new snowflake 2025-01-15 16:13:19 +01:00
Anthony Berg
2fbb6c9265 feat: update deps and breaking changes 2025-01-15 16:07:21 +01:00
Anthony Berg
fc8c12b346 Merge pull request #212 from veganhacktivists/coolify
Coolify support
2025-01-15 15:25:03 +01:00
93 changed files with 2740 additions and 2245 deletions

View File

@@ -34,30 +34,30 @@
"pnpm": ">=9"
},
"dependencies": {
"@prisma/client": "^5.18.0",
"@sapphire/discord.js-utilities": "^7.3.0",
"@sapphire/framework": "^5.2.1",
"@prisma/client": "^6.2.1",
"@sapphire/discord.js-utilities": "^7.3.2",
"@sapphire/framework": "^5.3.2",
"@sapphire/plugin-logger": "^4.0.2",
"@sapphire/plugin-scheduled-tasks": "^10.0.1",
"@sapphire/plugin-subcommands": "^6.0.3",
"@sapphire/stopwatch": "^1.5.2",
"@sapphire/time-utilities": "^1.7.12",
"@sapphire/plugin-scheduled-tasks": "^10.0.2",
"@sapphire/plugin-subcommands": "^7.0.1",
"@sapphire/stopwatch": "^1.5.4",
"@sapphire/time-utilities": "^1.7.14",
"@sapphire/ts-config": "^5.0.1",
"@sapphire/utilities": "^3.17.0",
"bullmq": "^5.12.10",
"discord.js": "^14.15.3",
"ioredis": "^5.4.1",
"@sapphire/utilities": "^3.18.1",
"bullmq": "^5.34.10",
"discord.js": "^14.17.3",
"ioredis": "^5.4.2",
"ts-node": "^10.9.2",
"typescript": "~5.4.5"
},
"devDependencies": {
"@types/node": "^20.16.1",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"eslint": "8.56.0",
"eslint-config-prettier": "^9.1.0",
"prettier": "3.2.4",
"prisma": "^5.18.0"
"@types/node": "^22.10.7",
"@typescript-eslint/eslint-plugin": "^8.20.0",
"@typescript-eslint/parser": "^8.20.0",
"eslint": "9.18.0",
"eslint-config-prettier": "^10.0.1",
"prettier": "3.4.2",
"prisma": "^6.2.1"
},
"packageManager": "pnpm@9.12.2+sha512.22721b3a11f81661ae1ec68ce1a7b879425a1ca5b991c975b074ac220b187ce56c708fe5db69f4c962c989452eee76c82877f4ee80f474cebd61ee13461b6228"
}

1168
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -18,8 +18,10 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { ChannelType } 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) {
@@ -79,7 +81,7 @@ export class AccessCommand extends Command {
if (!interaction.inCachedGuild()) {
await interaction.reply({
content: 'This command can only be run in a server!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -91,32 +93,29 @@ 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!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
// 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.',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
// 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!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -129,7 +128,7 @@ export class AccessCommand extends Command {
) {
await interaction.reply({
content: 'Channel is not in ModMail/Private/Restricted category!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -143,13 +142,13 @@ export class AccessCommand extends Command {
} else {
await interaction.reply({
content: 'Could not find the role to edit permissions!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
// Set permissions of voice channel
if (channel.type === ChannelType.GuildVoice) {
if (channel.isVoiceBased()) {
switch (permission) {
case 'add':
await channel.permissionOverwrites.create(permId, {
@@ -185,7 +184,7 @@ export class AccessCommand extends Command {
default:
await interaction.reply({
content: 'Incorrect permission option!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -220,7 +219,7 @@ export class AccessCommand extends Command {
default:
await interaction.reply({
content: 'Incorrect permission option!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}

View File

@@ -18,8 +18,9 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Message } from 'discord.js';
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) {
@@ -67,18 +68,27 @@ export class AnonymousCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
if (channel === null) {
if (interaction.channel === null) {
if (!isTextChannel(interaction.channel)) {
await interaction.reply({
content: 'Error getting the channel!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
if (!interaction.channel.isSendable()) {
await interaction.reply({
content: `I do not have sufficient permissions to send a message in ${interaction.channel}!`,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -86,8 +96,8 @@ export class AnonymousCommand extends Command {
await interaction.channel.send(message);
await interaction.reply({
content: 'Sent the message',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -95,8 +105,8 @@ export class AnonymousCommand extends Command {
if (channel.type !== ChannelType.GuildText) {
await interaction.reply({
content: 'Could not send, unsupported text channel!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
}
@@ -105,8 +115,8 @@ export class AnonymousCommand extends Command {
await interaction.reply({
content: 'Sent the message',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
}
@@ -121,7 +131,7 @@ export class AnonymousCommand extends Command {
return;
}
if (channel.isTextBased()) {
if (channel.isSendable()) {
await channel.send(text);
} else {
await message.react('❌');

View File

@@ -18,7 +18,8 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Message } from 'discord.js';
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,11 +58,11 @@ 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -70,8 +71,8 @@ export class ClearCommand extends Command {
await interaction.reply({
content: `Successfully deleted ${messages} messages!`,
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
}
@@ -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;

View File

@@ -19,9 +19,8 @@
import { RegisterBehavior } from '@sapphire/framework';
import { Subcommand } from '@sapphire/plugin-subcommands';
import type { Guild, TextChannel, Snowflake } from 'discord.js';
import { TextChannel, Snowflake, MessageFlagsBitField } from 'discord.js';
import {
CategoryChannel,
ChannelType,
EmbedBuilder,
GuildMember,
@@ -29,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(
@@ -93,7 +105,9 @@ export class PrivateCommand extends Subcommand {
const modUser = interaction.user;
const { guild } = interaction;
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
// Checks if all the variables are of the right type
if (guild === null) {
@@ -103,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!',
});
@@ -116,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!',
});
@@ -235,20 +249,22 @@ export class PrivateCommand extends Subcommand {
const modUser = interaction.user;
const { guild, channel } = interaction;
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
// 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!',
});
@@ -259,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!',
@@ -287,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();
@@ -298,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({
@@ -309,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}`,
@@ -369,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;
}
}

View File

@@ -18,10 +18,12 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import type { User, Guild, Message } from 'discord.js';
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) {
@@ -50,7 +52,7 @@ export class BalanceCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Could not find the guild!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -66,15 +68,9 @@ export class BalanceCommand extends Command {
}
public async messageRun(message: Message) {
const user = message.member?.user;
const user = message.author;
const { guild } = message;
if (user === undefined) {
await message.react('❌');
await message.reply('Could not find your user!');
return;
}
if (guild === null) {
await message.react('❌');
await message.reply('Could not find the guild!');
@@ -99,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;
}

View File

@@ -19,11 +19,19 @@
import { Command, RegisterBehavior } from '@sapphire/framework';
import { Time } from '@sapphire/time-utilities';
import type { User, Guild, GuildMember, Message } from 'discord.js';
import {
User,
Guild,
GuildMember,
Message,
MessageFlagsBitField,
} from 'discord.js';
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) {
@@ -51,7 +59,7 @@ export class DailyCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Could not find the guild!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -67,15 +75,9 @@ export class DailyCommand extends Command {
}
public async messageRun(message: Message) {
const user = message.member?.user;
const user = message.author;
const { guild } = message;
if (user === undefined) {
await message.react('❌');
await message.reply('Could not find your user!');
return;
}
if (guild === null) {
await message.react('❌');
await message.reply('Could not find the guild!');
@@ -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;
}

View File

@@ -18,11 +18,13 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { User, Guild, Message } from 'discord.js';
import { User, Guild, Message, MessageFlagsBitField } from 'discord.js';
import { updateUser } from '#utils/database/dbExistingUser';
import { getBalance, transfer } from '#utils/database/fun/economy';
import { EmbedBuilder, TextChannel } from 'discord.js';
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) {
@@ -75,7 +77,7 @@ export class BalanceCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Could not find the guild!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -117,15 +119,9 @@ export class BalanceCommand extends Command {
return;
}
const user = message.member?.user;
const user = message.author;
const { guild } = message;
if (user === undefined) {
await message.react('❌');
await message.reply('Could not find your user!');
return;
}
if (guild === null) {
await message.react('❌');
await message.reply('Could not find the guild!');
@@ -164,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;
}
@@ -206,18 +202,16 @@ 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) as
| TextChannel
| undefined;
const logChannel = await getTextBasedChannel(IDs.channels.logs.economy);
if (logChannel === undefined) {
logChannel = (await guild.channels.fetch(IDs.channels.logs.economy)) as
| TextChannel
| undefined;
if (logChannel === undefined) {
this.container.logger.error('Pay Error: Could not fetch log channel');
return info;
}
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',
);
return info;
}
const logEmbed = new EmbedBuilder(embed.data);

View File

@@ -18,9 +18,10 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { EmbedBuilder, GuildMember } 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,10 +49,10 @@ export class N1984Command extends Command {
const { member } = interaction;
// Type checks
if (!(member instanceof GuildMember)) {
if (!isGuildMember(member)) {
await interaction.reply({
ephemeral: true,
content: 'Failed to fetch your user on the bot!',
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -79,6 +80,6 @@ export class N1984Command extends Command {
.setFooter({ text: embedFooter });
// Send the embed
await interaction.reply({ embeds: [n1984Embed], fetchReply: true });
await interaction.reply({ embeds: [n1984Embed], withResponse: true });
}
}

View File

@@ -18,9 +18,10 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { EmbedBuilder, GuildMember } 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,10 +48,10 @@ export class CringeCommand extends Command {
const { member } = interaction;
// Type check
if (!(member instanceof GuildMember)) {
if (!isGuildMember(member)) {
await interaction.reply({
ephemeral: true,
content: 'Failed to fetch your user on the bot!',
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -74,6 +75,6 @@ export class CringeCommand extends Command {
.setFooter({ text: embedFooter });
// Send the embed
await interaction.reply({ embeds: [cringeEmbed], fetchReply: true });
await interaction.reply({ embeds: [cringeEmbed], withResponse: true });
}
}

View File

@@ -18,8 +18,9 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { EmbedBuilder, GuildMember } 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,10 +47,10 @@ export class HappyCommand extends Command {
const { member } = interaction;
// Type checks
if (!(member instanceof GuildMember)) {
if (!isGuildMember(member)) {
await interaction.reply({
ephemeral: true,
content: 'Failed to fetch your user on the bot!',
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -62,6 +63,6 @@ export class HappyCommand extends Command {
.setImage(randomHappy);
// Send the embed
await interaction.reply({ embeds: [happyEmbed], fetchReply: true });
await interaction.reply({ embeds: [happyEmbed], withResponse: true });
}
}

View File

@@ -18,9 +18,10 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { EmbedBuilder, GuildMember } 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,10 +59,10 @@ export class HugCommand extends Command {
// Type Checks
if (!(hugger instanceof GuildMember)) {
if (!isGuildMember(hugger)) {
await interaction.reply({
ephemeral: true,
content: 'Failed to fetch your user on the bot!',
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -96,7 +97,7 @@ export class HugCommand extends Command {
await interaction.reply({
content: `${user}`,
embeds: [hugEmbed],
fetchReply: true,
withResponse: true,
});
}
}

View File

@@ -18,9 +18,10 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { EmbedBuilder, GuildMember } 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,10 +58,10 @@ export class KillCommand extends Command {
const sender = interaction.member;
// Type checks
if (!(sender instanceof GuildMember)) {
if (!isGuildMember(sender)) {
await interaction.reply({
ephemeral: true,
content: 'Failed to fetch your user on the bot!',
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -92,7 +93,7 @@ export class KillCommand extends Command {
await interaction.reply({
content: `${user}`,
embeds: [killEmbed],
fetchReply: true,
withResponse: true,
});
}
}

View File

@@ -18,9 +18,10 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { EmbedBuilder, GuildMember } 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,10 +58,10 @@ export class PokeCommand extends Command {
const sender = interaction.member;
// Type checks
if (!(sender instanceof GuildMember)) {
if (!isGuildMember(sender)) {
await interaction.reply({
ephemeral: true,
content: 'Failed to fetch your user on the bot!',
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -95,7 +96,7 @@ export class PokeCommand extends Command {
await interaction.reply({
content: `${user}`,
embeds: [pokeEmbed],
fetchReply: true,
withResponse: true,
});
}
}

View File

@@ -18,8 +18,9 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { EmbedBuilder, GuildMember } 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,10 +47,10 @@ export class SadCommand extends Command {
const { member } = interaction;
// Type checks
if (!(member instanceof GuildMember)) {
if (!isGuildMember(member)) {
await interaction.reply({
ephemeral: true,
content: 'Failed to fetch your user on the bot!',
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -62,6 +63,6 @@ export class SadCommand extends Command {
.setImage(randomSad);
// Send the embed
await interaction.reply({ embeds: [sadEmbed], fetchReply: true });
await interaction.reply({ embeds: [sadEmbed], withResponse: true });
}
}

View File

@@ -18,8 +18,9 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import { EmbedBuilder, GuildMember } 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,10 +47,10 @@ export class ShrugCommand extends Command {
const { member } = interaction;
// Type checks
if (!(member instanceof GuildMember)) {
if (!isGuildMember(member)) {
await interaction.reply({
ephemeral: true,
content: 'Failed to fetch your user on the bot!',
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -62,6 +63,6 @@ export class ShrugCommand extends Command {
.setImage(randomShrug);
// Send the embed
await interaction.reply({ embeds: [shrugEmbed], fetchReply: true });
await interaction.reply({ embeds: [shrugEmbed], withResponse: true });
}
}

View File

@@ -18,7 +18,13 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { User, Message, Snowflake, TextChannel, Guild } from 'discord.js';
import {
User,
Message,
Snowflake,
Guild,
MessageFlagsBitField,
} from 'discord.js';
import { EmbedBuilder } from 'discord.js';
import IDs from '#utils/ids';
import { addBan, checkBan } from '#utils/database/moderation/ban';
@@ -27,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) {
@@ -75,13 +87,15 @@ export class BanCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const ban = await this.ban(user.id, mod.id, reason, guild);
@@ -142,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;
}
@@ -166,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!';
@@ -206,19 +218,20 @@ export class BanCommand extends Command {
info.success = true;
// 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('Ban Error: 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;
}
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`;
return info;
} else if (!logChannel.isSendable()) {
this.container.logger.error(
'Ban: The bot does not have permission to send in the logs channel!',
);
info.message = `${user} has been banned. This hasn't been logged in a text channel as the bot does not have permission to send logs!`;
return info;
}
const log = new EmbedBuilder()

View File

@@ -19,11 +19,17 @@
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import { Duration, DurationFormatter } from '@sapphire/time-utilities';
import type { User, Snowflake, TextChannel, Guild } from 'discord.js';
import { User, Snowflake, Guild, MessageFlagsBitField } from 'discord.js';
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) {
@@ -79,8 +85,8 @@ export class TempBanCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -94,7 +100,9 @@ export class TempBanCommand extends Command {
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const ban = await this.ban(user.id, mod.id, time, reason, guild);
@@ -201,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;
}
@@ -225,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!';
@@ -273,23 +279,25 @@ export class TempBanCommand extends Command {
info.success = true;
// 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(
'Temp Ban Error: 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";
return info;
}
if (!isTextBasedChannel(logChannel)) {
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";
return info;
} else if (!logChannel.isSendable()) {
this.container.logger.error(
'Temp Ban: The bot does not have permission to send in the logs channel!',
);
info.message =
`${user} has been temporarily banned for ${banLength}. ` +
"This hasn't been logged in a text channel as the bot does not have permission to send logs!";
return info;
}
const log = new EmbedBuilder()

View File

@@ -18,13 +18,13 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type {
import {
User,
Message,
Snowflake,
TextChannel,
Guild,
GuildBan,
MessageFlagsBitField,
} from 'discord.js';
import { EmbedBuilder } from 'discord.js';
import IDs from '#utils/ids';
@@ -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) {
@@ -75,8 +81,8 @@ export class UnbanCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -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,19 +194,21 @@ export class UnbanCommand extends Command {
info.success = true;
// Log unban
let logChannel = guild.channels.cache.get(IDs.channels.logs.restricted) as
| TextChannel
| undefined;
let 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('Ban Error: 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;
}
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`;
return info;
}
if (!logChannel.isSendable()) {
this.container.logger.error(
'Unban: The bot does not have permission to send in the logs channel!',
);
info.message = `${user} has been unbanned. This hasn't been logged in a text channel as the bot does not have permission to send logs!`;
return info;
}
const log = new EmbedBuilder()

View File

@@ -22,13 +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(
@@ -89,47 +96,25 @@ 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!',
ephemeral: true,
fetchReply: 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!',
ephemeral: true,
fetchReply: 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
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({
content: 'Command was not run in the Diversity section!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -140,13 +125,13 @@ 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,
});
await interaction.reply({
content: `${!open ? 'Opened' : 'Closed'} this channel.`,
fetchReply: true,
withResponse: true,
});
}
@@ -155,51 +140,69 @@ 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!',
ephemeral: true,
fetchReply: true,
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!',
ephemeral: true,
fetchReply: true,
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,
});
return;
}
// 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}`,
fetchReply: true,
withResponse: true,
});
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!`,
fetchReply: true,
withResponse: true,
});
await user
.send(`You have been given the ${diversity.name} role by ${mod}!`)
@@ -219,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!',
@@ -235,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;
@@ -270,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;
}

View File

@@ -21,8 +21,13 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Message } from 'discord.js';
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) {
@@ -61,7 +66,9 @@ export class MoveAllCommand extends Command {
const { member } = interaction;
const { guild } = interaction;
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
if (
channel.type !== ChannelType.GuildVoice &&
@@ -81,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({
@@ -135,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;
@@ -155,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;

View File

@@ -18,7 +18,9 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { GuildMember, Message } from 'discord.js';
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) {
@@ -68,21 +70,21 @@ export class RenameUserCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
// 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -93,15 +95,15 @@ export class RenameUserCommand extends Command {
} catch {
await interaction.reply({
content: "Bot doesn't have permission to change the user's name!",
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.reply({
content: `Changed ${user}'s nickname`,
fetchReply: true,
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
}

View File

@@ -26,6 +26,7 @@ import {
import {
ChannelType,
EmbedBuilder,
MessageFlagsBitField,
PermissionsBitField,
time,
} from 'discord.js';
@@ -39,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,
@@ -52,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);
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;
}
@@ -80,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!`;
@@ -218,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();
}
@@ -242,19 +241,20 @@ export async function restrictRun(
await user.send({ embeds: [dmEmbed] }).catch(() => {});
// 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) {
container.logger.error('Restrict Error: 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;
}
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 if (!logChannel.isSendable()) {
container.logger.error(
'Restrict: The bot does not have permission to send in the logs channel!',
);
info.message = `${user} has been restricted. This hasn't been logged in a text channel as the bot does not have permission to send logs!`;
return info;
}
const message = new EmbedBuilder()
@@ -324,15 +324,17 @@ export class RestrictCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await restrictRun(user?.id, mod.id, reason, guild);
const info = await restrictRun(user.id, mod.id, reason, guild);
await interaction.editReply({
content: info.message,
@@ -367,7 +369,7 @@ export class RestrictCommand extends Command {
return;
}
const info = await restrictRun(user?.id, mod.id, reason, guild);
const info = await restrictRun(user.id, mod.id, reason, guild);
await message.reply(info.message);
await message.react(info.success ? '✅' : '❌');

View File

@@ -18,11 +18,14 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import { ChannelType, EmbedBuilder } 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,47 +59,48 @@ 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
if (guild === null || channel === null) {
await interaction.reply({
content: 'Error fetching guild or channel!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
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(' ');
// eslint-disable-next-line prefer-destructuring
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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -106,8 +110,8 @@ export class RestrictLogsCommand extends Command {
await interaction.reply({
embeds: info.embeds,
content: info.message,
fetchReply: true,
ephemeral: !staffChannel,
withResponse: true,
flags: staffChannel ? undefined : MessageFlagsBitField.Flags.Ephemeral,
});
}
}
@@ -131,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;
@@ -142,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];
}
}
@@ -173,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);
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);
@@ -205,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;
}
}

View File

@@ -18,7 +18,7 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { User, Message } from 'discord.js';
import { User, Message, MessageFlagsBitField } from 'discord.js';
import { restrictRun } from './restrict';
export class RestrictToleranceCommand extends Command {
@@ -69,15 +69,17 @@ export class RestrictToleranceCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await restrictRun(user?.id, mod.id, reason, guild, true);
const info = await restrictRun(user.id, mod.id, reason, guild, true);
await interaction.editReply({
content: info.message,
@@ -112,7 +114,7 @@ export class RestrictToleranceCommand extends Command {
return;
}
const info = await restrictRun(user?.id, mod.id, reason, guild, true);
const info = await restrictRun(user.id, mod.id, reason, guild, true);
await message.reply(info.message);
await message.react(info.success ? '✅' : '❌');

View File

@@ -19,9 +19,15 @@
import { RegisterBehavior } from '@sapphire/framework';
import { Subcommand } from '@sapphire/plugin-subcommands';
import type { 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(
@@ -78,7 +84,9 @@ export class RestrictToolsCommand extends Subcommand {
const user = interaction.options.getUser('user');
const { guild, channel } = interaction;
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
// Checks if all the variables are of the right type
if (guild === null || channel === null) {
@@ -90,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!',
@@ -130,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();
@@ -142,37 +150,47 @@ export class RestrictToolsCommand extends Subcommand {
return;
}
const category = guild.channels.cache.get(IDs.categories.restricted) as
| CategoryChannel
| undefined;
const category = await getCategoryChannel(IDs.categories.restricted);
if (category === undefined) {
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),
);
textChannels.forEach((c) => {
const textChannel = c as TextChannel;
for (const c of textChannels) {
const channel = c[1];
// Checks that the channel is a text channel
if (!isTextChannel(channel)) {
continue;
}
// Checks that the channel has a topic
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(' ');
const vcId = topic[topic.indexOf(user?.id) + 1];
const voiceChannel = guild.channels.cache.get(vcId);
if (channel.topic.includes(user.id)) {
topic = channel.topic.split(' ');
const vcId = topic[topic.indexOf(user.id) + 1];
const voiceChannel = await getVoiceChannel(vcId);
if (
voiceChannel !== undefined &&
isVoiceChannel(voiceChannel) &&
voiceChannel.parentId === IDs.categories.restricted
) {
voiceChannel.delete();
await voiceChannel.delete();
}
textChannel.delete();
await channel.delete();
}
});
}
await interaction.editReply({
content: `Successfully deleted the channel for ${user}`,

View File

@@ -18,15 +18,30 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import { CategoryChannel, ChannelType, EmbedBuilder } from 'discord.js';
import type { User, Message, TextChannel, Guild, Snowflake } from 'discord.js';
import type { Guild, Message, Snowflake, User } from 'discord.js';
import { EmbedBuilder, MessageFlagsBitField } from 'discord.js';
import IDs from '#utils/ids';
import { fetchRoles, addExistingUser } from '#utils/database/dbExistingUser';
import { addExistingUser, fetchRoles } from '#utils/database/dbExistingUser';
import {
unRestrict,
checkActive,
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) {
@@ -69,14 +84,14 @@ export class UnRestrictCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
await interaction.deferReply();
const info = await this.unRestrictRun(user?.id, mod.id, guild);
const info = await this.unRestrictRun(user.id, mod.id, guild);
await interaction.editReply({
content: info.message,
@@ -108,7 +123,7 @@ export class UnRestrictCommand extends Command {
const channelRun = message.channel;
const info = await this.unRestrictRun(
user?.id,
user.id,
mod.id,
guild,
channelRun.id,
@@ -132,21 +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);
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;
}
@@ -155,13 +167,9 @@ 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) {
if (!isGuildMember(member)) {
info.message = "Can't unrestrict the user as they are not on this server";
return info;
}
@@ -180,16 +188,20 @@ export class UnRestrictCommand extends Command {
if (await checkActive(userId)) {
const roles = await fetchRoles(userId);
await member.roles.add(roles);
// Unrestricts the user on the database
await unRestrict(userId, modId);
} else {
let section = 1;
for (let i = 0; i < restrictRoles.length; i += 1) {
if (member.roles.cache.has(restrictRoles[i])) {
section = i + 1;
}
}
await member.roles.add(IDs.roles.nonvegan.nonvegan);
// Unrestricts the user on the database but for restricts done on the old bot
await unRestrictLegacy(userId, modId, section);
}
@@ -198,57 +210,73 @@ 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) as
| CategoryChannel
| undefined;
const category = await getCategoryChannel(IDs.categories.restricted);
if (!isCategoryChannel(category)) {
info.message =
'Could not find the restricted category! The channels will have to be deleted manually.';
return info;
}
let topic: string[];
if (category !== undefined) {
const textChannels = category.children.cache.filter(
(c) => c.type === ChannelType.GuildText,
);
textChannels.forEach((c) => {
const textChannel = c as TextChannel;
// Checks if the channel topic has the user's snowflake
if (textChannel.topic?.includes(userId)) {
if (textChannel.id === channelRun) {
info.runInVeganRestrict = true;
}
topic = textChannel.topic.split(' ');
const vcId = topic[topic.indexOf(userId) + 1];
const voiceChannel = guild.channels.cache.get(vcId);
const textChannels = category.children.cache.filter((channel) =>
isTextChannel(channel),
);
if (
voiceChannel !== undefined &&
voiceChannel.parentId === IDs.categories.restricted
) {
voiceChannel.delete();
}
textChannel.delete();
for (const c of textChannels) {
const channel = c[1];
// Checks that the channel is a text channel
if (!isTextChannel(channel)) {
continue;
}
// Checks that the channel has a topic
if (channel.topic === null) {
continue;
}
// Checks if the channel topic has the user's snowflake
if (channel.topic.includes(userId)) {
if (channel.id === channelRun) {
info.runInVeganRestrict = true;
}
});
topic = channel.topic.split(' ');
const vcId = topic[topic.indexOf(user.id) + 1];
const voiceChannel = await getVoiceChannel(vcId);
if (
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 channel.delete();
}
}
}
info.success = true;
// 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(
'Restrict Error: 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;
}
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 if (!logChannel.isSendable()) {
this.container.logger.error(
'Unrestrict: The bot does not have permission to send in the logs channel!',
);
info.message = `Unrestricted ${user} but could not find the log channel. This hasn't been logged in a text channel as the bot does not have permission to send logs!`;
return info;
}
const message = new EmbedBuilder()

View File

@@ -18,10 +18,13 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Message, TextBasedChannel } from 'discord.js';
import { ChannelType } from 'discord.js';
import { Message, MessageFlagsBitField, TextBasedChannel } 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,11 +61,11 @@ 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -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;
}

View File

@@ -18,8 +18,10 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { GuildMember, Message } from 'discord.js';
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) {
@@ -64,39 +66,39 @@ export class SoftMuteCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching the guild!',
ephemeral: true,
fetchReply: true,
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);
// 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
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}`,
fetchReply: true,
withResponse: true,
});
return;
}
await guildMember.roles.add(IDs.roles.restrictions.softMute);
await member.roles.add(IDs.roles.restrictions.softMute);
await interaction.reply({
content: `Soft muted ${user}`,
fetchReply: true,
withResponse: true,
});
}

View File

@@ -27,12 +27,10 @@ import {
ButtonStyle,
User,
Guild,
TextChannel,
GuildMember,
Snowflake,
MessageFlagsBitField,
} from 'discord.js';
import type { Message } from 'discord.js';
import { isMessageInstance } from '@sapphire/discord.js-utilities';
import {
addSusNoteDB,
findNotes,
@@ -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
@@ -157,10 +162,10 @@ export class SusCommand extends Subcommand {
const { guild } = interaction;
// Checks if all the variables are of the right type
if (!(guild instanceof Guild)) {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -169,7 +174,7 @@ export class SusCommand extends Subcommand {
await interaction.reply({
content: info.message,
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
}
@@ -195,7 +200,7 @@ export class SusCommand extends Subcommand {
const guild = message.guild;
if (!(guild instanceof Guild)) {
if (guild === null) {
await message.react('❌');
await message.reply(
'Could not find guild! Make sure you run this command in a server.',
@@ -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,10 +289,10 @@ 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!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -306,8 +306,8 @@ export class SusCommand extends Subcommand {
if (notes.length === 0) {
await interaction.reply({
content: `${user} has no sus notes!`,
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -318,8 +318,8 @@ export class SusCommand extends Subcommand {
// Sends the notes to the user
await interaction.reply({
embeds: [noteEmbed],
ephemeral: !staffChannel,
fetchReply: true,
flags: staffChannel ? undefined : MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
}
@@ -330,11 +330,11 @@ 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -346,8 +346,8 @@ export class SusCommand extends Subcommand {
if (note === null) {
await interaction.reply({
content: 'Error fetching note from database!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -356,28 +356,21 @@ 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
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()
@@ -407,16 +400,21 @@ export class SusCommand extends Subcommand {
const message = await interaction.reply({
embeds: [noteEmbed],
components: [buttons],
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
// Checks if the message is not an APIMessage
if (!isMessageInstance(message)) {
if (message.resource === null) {
await interaction.editReply('Failed to retrieve the message :(');
return;
}
if (!channel.isSendable()) {
await interaction.editReply('Cannot send messages in this channel!');
return;
}
// Listen for the button presses
const collector = channel.createMessageComponentCollector({
max: 1, // Maximum of 1 button press
@@ -438,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);
}
});
@@ -462,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 instanceof User)) {
user = await guild.client.users.fetch(userId).catch(() => undefined);
}
if (!(user instanceof User)) 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()
@@ -516,23 +501,23 @@ 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -545,8 +530,8 @@ export class SusCommand extends Subcommand {
if (notes.length === 0) {
await interaction.reply({
content: `${user} had no notes!`,
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -565,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: ${
@@ -596,16 +582,21 @@ export class SusCommand extends Subcommand {
const message = await interaction.reply({
embeds: [noteEmbed],
components: [buttons],
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
// Checks if the message is not an APIMessage
if (!isMessageInstance(message)) {
if (message.resource === null) {
await interaction.editReply('Failed to retrieve the message :(');
return;
}
if (!channel.isSendable()) {
await interaction.editReply('Cannot send messages in this channel!');
return;
}
// Listen for the button presses
const collector = channel.createMessageComponentCollector({
max: 1, // Maximum of 1 button press
@@ -623,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
@@ -638,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()

View File

@@ -18,13 +18,15 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { GuildMember, Message } from 'discord.js';
import { GuildMember, Message, MessageFlagsBitField } from 'discord.js';
import {
addMute,
removeMute,
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,29 +68,38 @@ 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
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
// 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
// Checks if `mod` was found
if (!isGuildMember(mod)) {
await interaction.reply({
content: 'Error fetching your user!',
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -102,8 +113,8 @@ export class VCMuteCommand extends Command {
await interaction.reply({
content: `Removed server mute from ${user}`,
fetchReply: true,
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -118,8 +129,8 @@ export class VCMuteCommand extends Command {
await addMute(member.id, mod.id, reason);
await interaction.reply({
content: `Server muted ${user}`,
fetchReply: true,
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
}
@@ -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!',

View File

@@ -18,14 +18,17 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import { EmbedBuilder, TextChannel } from 'discord.js';
import type { Message, Guild, User } from 'discord.js';
import { EmbedBuilder, MessageFlagsBitField } 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,23 +65,14 @@ 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!',
ephemeral: true,
fetchReply: true,
});
return;
}
const staffChannel = checkStaff(interaction.channel);
await interaction.deferReply({ ephemeral: !staffChannel });
await interaction.deferReply({
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,
@@ -108,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) {
@@ -116,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[],
@@ -134,34 +128,34 @@ 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) 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(
'Delete Warning Error: Could not fetch log channel',
);
info.message =
`Deleted warning for ${user} (Warning ID: ${warningId} but ` +
'could not find the log channel.';
return info;
}
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 if (!logChannel.isSendable()) {
this.container.logger.error(
'Delete Warning: The bot does not have permission to send in the logs channel!',
);
info.message =
`Deleted warning for ${user} (Warning ID: ${warningId} but ` +
"But this hasn't been logged in a text channel as the bot does not have permission to send logs!";
return info;
}
const message = new EmbedBuilder()

View File

@@ -17,17 +17,21 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import {
Args,
Command,
container,
RegisterBehavior,
} from '@sapphire/framework';
import type { User, Message, Snowflake, Guild, TextChannel } from 'discord.js';
EmbedBuilder,
Guild,
Message,
MessageFlagsBitField,
Snowflake,
User,
} from 'discord.js';
import { updateUser } from '#utils/database/dbExistingUser';
import { addWarn } from '#utils/database/moderation/warnings';
import { EmbedBuilder } from 'discord.js';
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) {
@@ -76,8 +80,8 @@ export class WarnCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -103,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('❌');
@@ -111,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) {
@@ -149,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;
}
@@ -161,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);
@@ -190,19 +183,20 @@ export class WarnCommand extends Command {
await user.send({ embeds: [dmEmbed] }).catch(() => {});
// Log the ban
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) {
container.logger.error('Warn Error: 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;
}
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.`;
return info;
} else if (!logChannel.isSendable()) {
this.container.logger.error(
'Warn: The bot does not have permission to send in the logs channel!',
);
info.message = `Warned ${user}, but the bot does not have permission to send in the logs channel!`;
return info;
}
const message = new EmbedBuilder()

View File

@@ -18,12 +18,15 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import { ChannelType, EmbedBuilder } 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) {
@@ -65,15 +68,17 @@ export class WarningsCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
const staffChannel = checkStaff(interaction.channel);
await interaction.deferReply({ ephemeral: !staffChannel });
await interaction.deferReply({
flags: staffChannel ? undefined : MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.warnings(user, guild);
@@ -101,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;
@@ -119,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;
}

View File

@@ -19,7 +19,12 @@
import { Subcommand } from '@sapphire/plugin-subcommands';
import { RegisterBehavior } from '@sapphire/framework';
import { ChannelType, PermissionsBitField, Snowflake } from 'discord.js';
import {
ChannelType,
MessageFlagsBitField,
PermissionsBitField,
Snowflake,
} from 'discord.js';
import { updateUser } from '#utils/database/dbExistingUser';
import {
addStatUser,
@@ -36,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(
@@ -184,23 +194,21 @@ 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) {
await interaction.reply({
content: 'Mod or guild was not found!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
const mod = guild.members.cache.get(modUser.id);
if (mod === undefined) {
if (!isGuildMember(mod)) {
await interaction.reply({
content: 'Outreach Leader was not found!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -208,7 +216,7 @@ export class OutreachCommand extends Subcommand {
if (!mod.roles.cache.has(IDs.roles.staff.outreachLeader)) {
await interaction.reply({
content: 'You need to be an Outreach Leader to run this command!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -216,39 +224,37 @@ export class OutreachCommand extends Subcommand {
if (await checkActiveEvent()) {
await interaction.reply({
content: 'There is already an active event!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
await updateUser(mod);
await createEvent(modUser.id);
await createEvent(mod.id);
await interaction.reply({
content: 'Created the event!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
}
public async eventEnd(interaction: Subcommand.ChatInputCommandInteraction) {
const modUser = interaction.user;
const mod = interaction.member;
const { guild } = interaction;
if (guild === null) {
await interaction.reply({
content: 'Guild not found!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
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!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -256,12 +262,14 @@ export class OutreachCommand extends Subcommand {
if (!mod.roles.cache.has(IDs.roles.staff.outreachLeader)) {
await interaction.reply({
content: 'You need to be an Outreach Leader to run this command!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const event = await getCurrentEvent();
@@ -299,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!',
);
@@ -351,12 +359,14 @@ export class OutreachCommand extends Subcommand {
if (guild === null) {
await interaction.reply({
content: 'Guild not found!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
if ((await getStatFromLeader(leader.id)) !== null) {
await interaction.editReply(
@@ -377,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.`,
});
@@ -456,18 +466,27 @@ 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) {
await interaction.reply({
content: 'Could not find guild!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
await interaction.deferReply({ ephemeral: true });
if (!isGuildMember(leader)) {
await interaction.editReply({
content: 'Could not find your GuildMember!',
});
return;
}
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
let statId: number;
let roleId: Snowflake | undefined;
@@ -483,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}`,
@@ -526,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!',
});
@@ -568,15 +578,9 @@ export class OutreachCommand extends Subcommand {
educated: educated !== null ? educated : 0,
};
if (leader === null) {
await interaction.reply({
content: 'Could not find your user!',
ephemeral: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const stat = await getStatFromLeader(leader.id);

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -62,13 +65,15 @@ export class BookClubCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageBookClub(user, mod, guild);
@@ -88,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) {
@@ -115,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -63,13 +66,15 @@ export class DebateHostCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageDebateHost(user, mod, guild);
@@ -89,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) {
@@ -116,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -62,13 +65,15 @@ export class GameNightHostCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageGameNight(user, mod, guild);
@@ -88,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) {
@@ -115,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -62,13 +65,15 @@ export class GuestCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageGuest(user, mod, guild);
@@ -88,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) {
@@ -115,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -63,13 +66,15 @@ export class MentorCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageMentor(user, mod, guild);
@@ -89,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) {
@@ -116,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -62,13 +65,15 @@ export class ModCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageMod(user, mod, guild);
@@ -88,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) {
@@ -115,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -63,13 +66,15 @@ export class RestrictedAccessCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageRestrictedAccess(user, mod, guild);
@@ -89,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) {
@@ -116,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -62,13 +65,15 @@ export class StageHostCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageStageHost(user, mod, guild);
@@ -88,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) {
@@ -115,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -63,13 +66,15 @@ export class TrialModCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageMod(user, mod, guild);
@@ -89,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) {
@@ -116,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -62,13 +65,15 @@ export class TrialVerifierCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageTrialVerifier(user, mod, guild);
@@ -88,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) {
@@ -115,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -62,13 +65,15 @@ export class VerifierCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageVerifier(user, mod, guild);
@@ -88,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) {
@@ -115,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -65,13 +68,15 @@ export class ActivistCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageActivist(user, mod, guild);
@@ -91,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) {
@@ -116,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -64,13 +67,15 @@ export class ARAVeganCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageVegan(user, mod, guild);
@@ -90,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) {
@@ -115,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -63,13 +66,15 @@ export class ConvincedCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageConvinced(user, mod, guild);
@@ -89,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) {
@@ -114,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -63,7 +66,7 @@ export class PlusCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -63,13 +66,15 @@ export class TrustedCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageTrusted(user, mod, guild);
@@ -89,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) {
@@ -114,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -65,13 +68,15 @@ export class VeganCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageVegan(user, mod, guild);
@@ -91,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) {
@@ -116,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;
}

View File

@@ -18,9 +18,12 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Guild, User, Message } from 'discord.js';
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) {
@@ -63,13 +66,15 @@ export class VegCuriousCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
flags: MessageFlagsBitField.Flags.Ephemeral,
});
const info = await this.manageVegCurious(user, mod, guild);
@@ -89,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) {
@@ -114,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;
}

View File

@@ -18,7 +18,7 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import type { Message } from 'discord.js';
import { Message, MessageFlagsBitField } from 'discord.js';
export class ApplyCommand extends Command {
public constructor(context: Command.LoaderContext, options: Command.Options) {
@@ -47,8 +47,8 @@ export class ApplyCommand extends Command {
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
await interaction.reply({
content: this.message,
fetchReply: true,
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
}

View File

@@ -18,8 +18,10 @@
*/
import { Command, RegisterBehavior } from '@sapphire/framework';
import type { Message } from 'discord.js';
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) {
@@ -47,22 +49,22 @@ export class RenameUserCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await guild.members.fetch();
const vegan = await guild.roles.cache.get(IDs.roles.vegan.vegan);
const notVegan = await 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!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -71,7 +73,7 @@ export class RenameUserCommand extends Command {
content:
`${vegan.name}s: \`${vegan.members.size}\`` +
`\n${notVegan.name}s: \`${notVegan.members.size}\``,
fetchReply: true,
withResponse: true,
});
}
@@ -88,8 +90,8 @@ export class RenameUserCommand extends Command {
await guild.members.fetch();
const vegan = await guild.roles.cache.get(IDs.roles.vegan.vegan);
const notVegan = await guild.roles.cache.get(IDs.roles.nonvegan.nonvegan);
const vegan = guild.roles.cache.get(IDs.roles.vegan.vegan);
const notVegan = guild.roles.cache.get(IDs.roles.nonvegan.nonvegan);
if (vegan === undefined || notVegan === undefined) {
await message.react('❌');

View File

@@ -19,6 +19,7 @@
import { Command, RegisterBehavior } from '@sapphire/framework';
import IDs from '#utils/ids';
import { MessageFlagsBitField } from 'discord.js';
export class InfoCommand extends Command {
public constructor(context: Command.LoaderContext, options: Command.Options) {
@@ -66,11 +67,7 @@ export class InfoCommand extends Command {
const option = interaction.options.getString('info', true);
let ephemeral = interaction.options.getBoolean('visible');
if (ephemeral === null) {
ephemeral = true;
} else {
ephemeral = !ephemeral;
}
ephemeral = ephemeral !== true;
let message: string;
@@ -115,7 +112,7 @@ export class InfoCommand extends Command {
await interaction.reply({
content: message,
ephemeral,
flags: ephemeral ? MessageFlagsBitField.Flags.Ephemeral : undefined,
});
}
}

View File

@@ -17,9 +17,8 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import { isMessageInstance } from '@sapphire/discord.js-utilities';
import { Command } from '@sapphire/framework';
import type { Message } from 'discord.js';
import { Message, MessageFlagsBitField } from 'discord.js';
export class PingCommand extends Command {
public constructor(context: Command.LoaderContext, options: Command.Options) {
@@ -41,12 +40,13 @@ export class PingCommand extends Command {
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
const msg = await interaction.reply({
content: 'Ping?',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
if (isMessageInstance(msg)) {
const diff = msg.createdTimestamp - interaction.createdTimestamp;
if (msg.resource !== null && msg.resource.message !== null) {
const diff =
msg.resource.message.createdTimestamp - interaction.createdTimestamp;
const ping = Math.round(this.container.client.ws.ping);
return interaction.editReply(
`Pong 🏓! (Round trip took: ${diff}ms. Heartbeat: ${ping}ms.)`,
@@ -57,6 +57,11 @@ export class PingCommand extends Command {
}
public async messageRun(message: Message) {
if (!message.channel.isSendable()) {
// TODO manage logging/errors properly
return;
}
const msg = await message.channel.send('Ping?');
const diff = msg.createdTimestamp - message.createdTimestamp;

View File

@@ -18,13 +18,21 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { Message, User, Guild, Snowflake } from 'discord.js';
import {
Message,
User,
Guild,
Snowflake,
MessageFlagsBitField,
} from 'discord.js';
import IDs from '#utils/ids';
import {
finishVerifyMessages,
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) {
@@ -77,8 +85,8 @@ export class VerifyCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
@@ -93,7 +101,7 @@ export class VerifyCommand extends Command {
await interaction.reply({
content: verify.message,
fetchReply: true,
withResponse: true,
});
}
@@ -159,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)) {
@@ -175,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(' ');

View File

@@ -20,6 +20,9 @@
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) {
@@ -60,22 +63,21 @@ export class VerifyTimeoutRemoveCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Error fetching guild!',
ephemeral: true,
fetchReply: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
withResponse: true,
});
return;
}
await interaction.deferReply({ ephemeral: true });
await interaction.deferReply({
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)) {

View File

@@ -18,9 +18,11 @@
*/
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
import type { User, Guild, Message } from 'discord.js';
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) {
@@ -55,7 +57,7 @@ export class RankCommand extends Command {
if (guild === null) {
await interaction.reply({
content: 'Could not find the guild!',
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
@@ -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);

View File

@@ -21,8 +21,9 @@ import {
InteractionHandler,
InteractionHandlerTypes,
} from '@sapphire/framework';
import type { ButtonInteraction, GuildMember } 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(
@@ -42,54 +43,42 @@ 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,
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
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!',
ephemeral: true,
});
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.',
ephemeral: true,
});
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:
'Your access to the non vegan section has been given back. ' +
'If you want to remove access again, click this button again.',
ephemeral: true,
});
} catch (error) {
this.container.logger.error(`Non Vegan Access Interaction: ${error}`);
await interaction.reply({
content: errorMessage,
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
}
}

View File

@@ -21,8 +21,12 @@ import {
InteractionHandler,
InteractionHandlerTypes,
} from '@sapphire/framework';
import type { ButtonInteraction, GuildMember, TextChannel } 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(
@@ -42,50 +46,88 @@ export class WelcomeButtonHandler extends InteractionHandler {
}
public async run(interaction: ButtonInteraction) {
let { member } = interaction;
const general = this.container.client.channels.cache.get(
IDs.channels.nonVegan.general,
) as TextChannel | undefined;
if (general === undefined) {
return;
}
const { member } = interaction;
const general = await getTextBasedChannel(IDs.channels.nonVegan.general);
if (member === null) {
// Messages that are used multiple times
const roleErrorMessage =
'There was an error giving you the role, please try again later or contact ModMail to be let into this server.';
const welcomeMessage =
`${member} Welcome to ARA! :D Please check <#${IDs.channels.information.roles}> ` +
`and remember to follow the <#${IDs.channels.information.conduct}> and to respect ongoing discussions and debates.` +
`\n\nIf you are vegan, you can join the 'Verification' voice channel, or use \`/apply\` with the Appy bot in <#${IDs.channels.nonVegan.vcText}>, ` +
'to be verified and gain access to more channels.';
// Checks if general is not in the cache
if (!isTextChannel(general)) {
this.container.logger.error(
'WelcomeButtonHandler: Could not find and fetch the general channel!',
);
await interaction.reply({
content:
'There was an error giving you the role, please try again later or contact ModMail to be let into this server.',
ephemeral: true,
'Sorry there was a problem trying to give you access to the server. Please try again later.',
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
try {
member = member as GuildMember;
// If the member could not be found
if (!isGuildMember(member)) {
await interaction.reply({
content: roleErrorMessage,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
// Give non-vegan role
if (!member.voice.channel) {
await member.roles.add(IDs.roles.nonvegan.nonvegan);
return;
}
await general.send(
`${member} Welcome to ARA! :D Please check <#${IDs.channels.information.roles}> ` +
`and remember to follow the <#${IDs.channels.information.conduct}> and to respect ongoing discussions and debates.` +
`\n\nIf you are vegan, you can join the 'Verification' voice channel, or use \`/apply\` with the Appy bot in <#${IDs.channels.nonVegan.vcText}>, ` +
'to be verified and gain access to more channels.',
);
return;
}
// Checks if the user is currently restricted
if (await checkActive(member.id)) {
await interaction.reply({
content: `You are currently restricted from this server! Contact the moderators by sending a DM to <@${IDs.modMail}>.`,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
// Give non-vegan role
if (member.voice.channel) {
await interaction.reply({
content:
"You're currently in a verification, you'll have to leave the verification or get verified before being able to access the server again.",
ephemeral: true,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
} catch (error) {
return;
}
// Add the user to the database
await addUser(member.id);
// Give the role to the member
const role = await member.roles
.add(IDs.roles.nonvegan.nonvegan)
.catch(() => undefined);
// If the role could not be given
if (role === undefined) {
await interaction.reply({
content:
'There was an error giving you the role, please try again later or contact ModMail to be let into this server.',
ephemeral: true,
content: roleErrorMessage,
flags: MessageFlagsBitField.Flags.Ephemeral,
});
return;
}
if (general.isSendable()) {
await general.send(welcomeMessage);
} else {
this.container.logger.error(
'WelcomeButtonHandler: The bot does not have permission to send in general!',
);
await member.send(welcomeMessage);
}
}
}

View File

@@ -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()

View File

@@ -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()

View File

@@ -44,6 +44,13 @@ 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
@@ -52,6 +59,7 @@ export class XpListener extends Listener {
message.channel.send(
'An unexpected error occurred trying to set up the counting channel, please contact a developer!',
);
return;
}
@@ -66,6 +74,7 @@ export class XpListener extends Listener {
message.channel.send(
'An unexpected error occurred, please contact a developer!',
);
return;
}
}

186
src/listeners/fixRoles.ts Normal file
View File

@@ -0,0 +1,186 @@
// 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 <https://www.gnu.org/licenses/>.
I used the Sapphire documentation and parts of the code from the Sapphire CLI to
create this file.
*/
import { Listener } from '@sapphire/framework';
import { DurationFormatter } from '@sapphire/time-utilities';
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(
context: Listener.LoaderContext,
options: Listener.Options,
) {
super(context, {
...options,
once: true,
event: 'ready',
// !!!!!!!!!!!! WARNING !!!!!!!!!!!!
// THIS SHOULD BE DISABLED BY DEFAULT
// THIS IS ONLY USED FOR RESTORING ROLES TO THE SERVER!
// ENABLING THIS UNINTENTIONALLY WILL CAUSE SLOWDOWNS TO THE BOT DUE TO RATE LIMITING!
enabled: false,
});
}
public async run() {
this.container.logger.info(
'FixRolesOnReady: Preparation before starting to fix the roles for each user...',
);
// Fetching the Guild
const guild = await getGuild(IDs.guild);
if (guild === undefined) {
this.container.logger.error('FixRolesOnReady: Could not find the server');
return;
}
// Fetching the channel for the logs
// Leave the snowflake parameter empty for no logs
const logChannel = await getTextBasedChannel('');
const sendLogs = isTextBasedChannel(logChannel);
if (!sendLogs) {
this.container.logger.error(
'FixRolesOnReady: Could not find the channel for bot logs.',
);
} else if (sendLogs && !logChannel.isSendable()) {
this.container.logger.info(
'FixRolesOnReady: No permission to send in bots logs channel.',
);
return;
}
// Get all the current users
this.container.logger.info('FixRolesOnReady: Fetching all the members...');
if (sendLogs) {
logChannel.send('Fetching all the users in ARA!');
}
const members = await guild.members.fetch().catch(() => undefined);
if (members === undefined) {
this.container.logger.error(
'FixRolesOnReady: Could not fetch all the members, this function is stopping now.',
);
if (sendLogs) {
logChannel.send("Never mind, something went wrong :'(");
}
return;
}
const totalMembers = members.size;
this.container.logger.info(
`FixRolesOnReady: Done fetching ${totalMembers} members!`,
);
// Giving the roles to each user
let count = 0;
const startTime = new Date().getTime();
this.container.logger.info(
'FixRolesOnReady: Starting the process of fixing the roles for every member...',
);
for (const [userId, member] of members) {
// Update the counter for the total number of users processed
count += 1;
// Send a message with an update for every 50 completions
// Checks if `channelLog` has been set to null
// The RHS of the modulo should be around 100
if (sendLogs && count % 250 === 0) {
const currentTime = new Date().getTime();
const runningTime = currentTime - startTime;
const remaining = totalMembers - count;
// Basing this on the fact that
const eta = remaining * (runningTime / count);
const estimate = new DurationFormatter().format(eta);
logChannel.send(
`Given roles to ${count} out of ${totalMembers} members. Estimated time until completion: ${estimate}`,
);
}
// Checks if the user already has vegan or non-vegan role
// Checks if the user is restricted, and skips over them if they are
const restricted = await checkActive(userId);
if (
restricted ||
member.roles.cache.hasAny(...IDs.roles.restrictions.restricted)
) {
continue;
}
// Fetch the roles for the member in the database
const dbRoles = await fetchRoles(userId);
// Filters out the roles that the member does not have
const roles = dbRoles.filter((role) => !member.roles.cache.has(role));
if (!roles.includes(IDs.roles.nonvegan.nonvegan)) {
const xp = await getUser(userId);
if (xp !== null && xp.xp > 0) {
roles.push(IDs.roles.nonvegan.nonvegan);
}
}
// Give the roles to the member
if (roles.length > 0) {
await member.roles.add(roles);
}
// Log the completion
this.container.logger.info(
`FixRolesOnReady: Given roles to ${count}/${totalMembers}.`,
);
// Add a delay so that there's around 4 users processed a second
await this.delay(1000);
}
// Send the logs that the fix has finished.
const endTime = new Date().getTime();
const totalTime = endTime - startTime;
const totalTimeWritten = new DurationFormatter().format(totalTime);
const finishMessage = `Finished fixing roles for all ${totalMembers} members! It took ${totalTimeWritten} to complete.`;
this.container.logger.info(`FixRolesOnReady: ${finishMessage}`);
if (sendLogs) {
logChannel.send(finishMessage);
}
}
private delay(ms: number) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}

View File

@@ -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

View File

@@ -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;

View File

@@ -22,6 +22,9 @@
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(
@@ -35,8 +38,24 @@ export class ReadyListener extends Listener {
});
}
public run(client: Client) {
public async run(client: Client) {
const { username, id } = client.user!;
this.container.logger.info(`Successfully logged in as ${username} (${id})`);
const botLogChannel = await getTextBasedChannel(IDs.channels.logs.bot);
if (!isTextBasedChannel(botLogChannel)) {
this.container.logger.error(
'ReadyListener: Could not find the channel for bot logs.',
);
return;
} else if (!botLogChannel.isSendable()) {
this.container.logger.info(
'ReadyListener: No permission to send in bots logs channel.',
);
return;
}
botLogChannel.send('The bot has started up!');
}
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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',
);

View File

@@ -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;
}

View File

@@ -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.',
);

View File

@@ -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);
}

View File

@@ -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.',
);

View File

@@ -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;

View File

@@ -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);

View File

@@ -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);

View File

@@ -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));

View File

@@ -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}> :)`);

View File

@@ -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. " +

View File

@@ -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;
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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;
}

View File

@@ -17,20 +17,18 @@
along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import type {
GuildMember,
GuildMemberRoleManager,
Snowflake,
} from 'discord.js';
import { GuildMember, GuildMemberRoleManager, Snowflake } from 'discord.js';
import { container } from '@sapphire/framework';
import IDs from '#utils/ids';
import { Prisma } from '@prisma/client';
import { DefaultArgs, GetResult } from '@prisma/client/runtime/binary';
/**
* Checks if the user exists on the User table in the database
* @param {string} userId Snowflake for the user being checked
* @return {boolean} If the user was found
* @return {Promise<boolean>} If the user was found
*/
export async function userExists(userId: Snowflake) {
export async function userExists(userId: Snowflake): Promise<boolean> {
// Counts if the user is on the database by their snowflake
const userQuery = await container.database.user.findFirst({
where: {
@@ -66,6 +64,26 @@ function getRoles(roles: GuildMemberRoleManager) {
return rolesDict;
}
/**
* Adds a new user to the server
* @param {Snowflake} userId the `User` snowflake to be added to the database
*/
export async function addUser(userId: Snowflake) {
// Uses upsert just in case the user has joined the server previously but has not gotten the roles previously
await container.database.user.upsert({
where: {
id: userId,
},
update: {
notVegan: true,
},
create: {
id: userId,
notVegan: true,
},
});
}
/**
* Add user to the database, if they have not been added beforehand
* @param {GuildMember} member GuildMember for the user to be added to the database
@@ -155,9 +173,9 @@ export async function updateUser(member: GuildMember) {
/**
* Gets the roles that the user that is on the User table.
* @param {string} userId Snowflake of the user to fetch roles from
* @return {Snowflake[]} Array of Role Snowflakes
* @return {Promise<Snowflake[]>} Array of Role Snowflakes
*/
export async function fetchRoles(userId: Snowflake) {
export async function fetchRoles(userId: Snowflake): Promise<Snowflake[]> {
// Get the user's roles
const roleQuery = await container.database.user.findUnique({
where: {
@@ -230,12 +248,26 @@ export async function logLeaving(member: GuildMember) {
});
}
// The type returned by `getLeaveRoles`.
// Includes a list of all the role Snowflakes when the user last left the server
type GetLeaveRoles = Prisma.Prisma__LeaveLogClient<
GetResult<
Prisma.$LeaveLogPayload<DefaultArgs>,
{
select: { roles: boolean };
},
'findFirst'
> | null,
null,
DefaultArgs
>;
/**
* Get the roles that the user had prior to when they left the server.
* @param {string} userId Snowflake of the user who joined the server
* @return {string[]} Array of Role Snowflakes
* @return {GetLeaveRoles} Array of Role Snowflakes
*/
export async function getLeaveRoles(userId: Snowflake) {
export function getLeaveRoles(userId: Snowflake): GetLeaveRoles {
const roles = container.database.leaveLog.findFirst({
where: {
userId,

View File

@@ -18,6 +18,7 @@
*/
const devIDs = {
guild: '999431674972618792',
roles: {
trusted: '999431675081666599',
booster: '',
@@ -41,6 +42,7 @@ const devIDs = {
restricted2: '999431674997788676',
restricted3: '999431674997788675',
restricted4: '999431674997788674',
restrictedVegan: '1075952207091994726',
restricted: [
'999431674997788677', // Restricted 1
'999431674997788676', // Restricted 2
@@ -126,6 +128,7 @@ const devIDs = {
},
logs: {
restricted: '999431681217937513',
bot: '999431681217937516',
economy: '999431681599623198',
sus: '999431681599623199',
},
@@ -139,6 +142,7 @@ const devIDs = {
private: '999431679527628818',
restricted: '999431679812845654',
},
modMail: '575252669443211264',
};
export default devIDs;

245
src/utils/fetcher.ts Normal file
View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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<User | undefined> {
// 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<Guild | undefined> {
// 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<GuildMember | undefined> {
// 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<Role | undefined> {
// 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<TextBasedChannel | undefined> {
// 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<CategoryChannel | undefined> {
// 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<VoiceChannel | undefined> {
// 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<VoiceBasedChannel | undefined> {
// 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;
}
}

View File

@@ -20,34 +20,36 @@
import devIDs from '#utils/devIDs';
let IDs = {
guild: '730907954345279591',
roles: {
trusted: '731563158011117590',
trusted: '1329089675977035879',
booster: '731213264540795012',
nonvegan: {
nonvegan: '774763753308815400',
vegCurious: '832656046572961803',
nonvegan: '1329093962153332848',
vegCurious: '1329107984227369020',
convinced: '797132019166871612',
},
vegan: {
vegan: '788114978020392982',
activist: '730915638746546257',
activist: '1329112833115295815',
nvAccess: '1076857105648209971',
plus: '798682625619132428',
araVegan: '995394977658044506',
},
restrictions: {
sus: '859145930640457729',
sus: '1329125130949103626',
muted: '730924813681688596',
softMute: '775934741139554335',
restricted1: '809769217477050369',
restricted2: '872482843304001566',
restricted3: '856582673258774538',
restricted4: '872472182888992858',
restricted3: '1329126085207789658',
restricted4: '1329126181164945499',
restrictedVegan: '1075951477379567646',
restricted: [
'809769217477050369', // Restricted 1
'872482843304001566', // Restricted 2
'856582673258774538', // Restricted 3
'872472182888992858', // Restricted 4
'1329126085207789658', // Restricted 3
'1329126181164945499', // Restricted 4
'1075951477379567646', // Restricted Vegan
],
},
@@ -75,7 +77,7 @@ let IDs = {
stageHost: '854893757593419786',
patron: '765370219207852055',
patreon: '993848684640997406',
verifyBlock: '1032765019269640203',
verifyBlock: '1329107805130461247',
bookClub: '955516408249352212',
debateHost: '935508325615931443',
gameNightHost: '952779915701415966',
@@ -128,6 +130,7 @@ let IDs = {
},
logs: {
restricted: '920993034462715925',
bot: '872126272015314966',
economy: '932050015034159174',
sus: '872884989950324826',
},
@@ -141,6 +144,7 @@ let IDs = {
private: '992581296901599302',
restricted: '809765577236283472',
},
modMail: '575252669443211264',
};
// Check if the bot is in development mode

37
src/utils/typeChecking.ts Normal file
View File

@@ -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 <https://www.gnu.org/licenses/>.
*/
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;
}

View File

@@ -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);
});
}
}