diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts
new file mode 100644
index 0000000..f33b34c
--- /dev/null
+++ b/src/commands/mod/restriction/restrict.ts
@@ -0,0 +1,355 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+/*
+ Animal Rights Advocates Discord Bot
+ Copyright (C) 2023 Anthony Berg
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+import {
+ Args,
+ Command,
+ RegisterBehavior,
+ container,
+} from '@sapphire/framework';
+import {
+ ChannelType,
+ EmbedBuilder,
+ PermissionsBitField,
+ time,
+} from 'discord.js';
+import type {
+ User,
+ Message,
+ TextChannel,
+ Guild,
+ Snowflake,
+} from 'discord.js';
+import IDs from '#utils/ids';
+import { randint } from '#utils/random';
+import {
+ addEmptyUser,
+ updateUser,
+ userExists,
+ fetchRoles,
+} from '#utils/database/dbExistingUser';
+import { restrict, checkActive } from '#utils/database/restriction';
+
+export async function restrictRun(
+ userId: Snowflake,
+ modId: Snowflake,
+ reason: string,
+ guild: Guild,
+ tolerance = false,
+) {
+ const info = {
+ message: '',
+ success: false,
+ };
+
+ // Gets mod's GuildMember
+ const mod = guild.members.cache.get(modId);
+
+ // Checks if guildMember is null
+ if (mod === undefined) {
+ info.message = 'Error fetching mod';
+ return info;
+ }
+
+ // Check if mod is in database
+ await updateUser(mod);
+
+ if (await checkActive(userId)) {
+ info.message = `<@${userId}> is already restricted!`;
+ return info;
+ }
+
+ // Gets guildMember
+ let member = guild.members.cache.get(userId);
+
+ if (member === undefined) {
+ member = await guild.members.fetch(userId);
+ }
+
+ const restrictRoles = IDs.roles.restrictions.restricted;
+
+ let section = tolerance ? randint(3, 4) : randint(1, 2);
+
+ if (member !== undefined) {
+ // Checks if the user is not restricted
+ if (member.roles.cache.hasAny(...restrictRoles)) {
+ info.message = `${member} is already restricted!`;
+ return info;
+ }
+
+ // Check if user and mod are on the database
+ await updateUser(member);
+
+ if (member.roles.cache.has(IDs.roles.vegan.vegan)) {
+ // TODO remove this error before enabling vegan restricts
+ info.message = `${member} is vegan, can't restrict them yet 😭`;
+ return info;
+ section = 5;
+ }
+
+ await member.roles.add(restrictRoles[section - 1]);
+
+ if (member.roles.cache.has(IDs.roles.vegan.vegan)) {
+ const voiceChannel = await guild.channels.create({
+ name: 'Restricted Voice Channel',
+ type: ChannelType.GuildVoice,
+ parent: IDs.categories.restricted,
+ permissionOverwrites: [
+ {
+ id: guild.roles.everyone,
+ deny: [PermissionsBitField.Flags.ViewChannel],
+ },
+ {
+ id: member.id,
+ allow: [PermissionsBitField.Flags.ViewChannel],
+ },
+ {
+ id: IDs.roles.staff.restricted,
+ allow: [PermissionsBitField.Flags.SendMessages,
+ PermissionsBitField.Flags.ViewChannel,
+ PermissionsBitField.Flags.Connect,
+ PermissionsBitField.Flags.MuteMembers],
+ },
+ ],
+ });
+
+ let restrictedChannel: TextChannel;
+ let bannedName = false;
+ try {
+ restrictedChannel = await guild.channels.create({
+ name: `⛔┃${member.user.username}-restricted`,
+ type: ChannelType.GuildText,
+ topic: `Restricted channel. ${member.id} ${voiceChannel.id} (Please do not change this)`,
+ parent: IDs.categories.restricted,
+ permissionOverwrites: [
+ {
+ id: guild.roles.everyone,
+ allow: [PermissionsBitField.Flags.ReadMessageHistory],
+ deny: [PermissionsBitField.Flags.ViewChannel],
+ },
+ {
+ id: member.id,
+ allow: [PermissionsBitField.Flags.ViewChannel],
+ },
+ {
+ id: IDs.roles.staff.restricted,
+ allow: [PermissionsBitField.Flags.SendMessages,
+ PermissionsBitField.Flags.ViewChannel],
+ },
+ ],
+ });
+ } catch {
+ restrictedChannel = await guild.channels.create({
+ name: `⛔┃${member.user.id}-restricted`,
+ type: ChannelType.GuildText,
+ topic: `Restricted channel. ${member.id} ${voiceChannel.id} (Please do not change this)`,
+ parent: IDs.categories.restricted,
+ permissionOverwrites: [
+ {
+ id: guild.roles.everyone,
+ allow: [PermissionsBitField.Flags.ReadMessageHistory],
+ deny: [PermissionsBitField.Flags.ViewChannel],
+ },
+ {
+ id: member.id,
+ allow: [PermissionsBitField.Flags.ViewChannel],
+ },
+ {
+ id: IDs.roles.staff.restricted,
+ allow: [PermissionsBitField.Flags.SendMessages,
+ PermissionsBitField.Flags.ViewChannel],
+ },
+ ],
+ });
+ bannedName = true;
+ }
+
+ if (!bannedName) {
+ await voiceChannel.setName(`${member.user.username}-restricted`);
+ } else {
+ await voiceChannel.setName(`${member.user.id}-restricted`);
+ }
+
+ const joinTime = time(member.joinedAt!);
+ const registerTime = time(member.user.createdAt);
+
+ const embed = new EmbedBuilder()
+ .setColor(member.displayHexColor)
+ .setTitle(`Restricted channel for ${member.user.username}`)
+ .setDescription(`${member}`)
+ .setThumbnail(member.user.avatarURL()!)
+ .addFields(
+ { name: 'Joined:', value: `${joinTime}`, inline: true },
+ { name: 'Created:', value: `${registerTime}`, inline: true },
+ );
+
+ await restrictedChannel.send({ embeds: [embed] });
+ }
+
+ await member.roles.remove([
+ IDs.roles.vegan.vegan,
+ IDs.roles.vegan.plus,
+ IDs.roles.vegan.activist,
+ IDs.roles.trusted,
+ IDs.roles.nonvegan.nonvegan,
+ IDs.roles.nonvegan.convinced,
+ IDs.roles.nonvegan.vegCurious,
+ ]);
+ } else if (!await userExists(userId)) {
+ // TODO remove this error before replacing other bot role replacement
+ info.message = `<@${userId}> is not on this server, can't restrict them yet! 😭`;
+ return info;
+ await addEmptyUser(userId);
+ const dbRoles = await fetchRoles(userId);
+ if (dbRoles.includes(IDs.roles.vegan.vegan)) {
+ section = 5;
+ }
+ }
+
+ // Restrict the user on the database
+ await restrict(userId, modId, reason, section);
+
+ info.message = `Restricted ${member}`;
+ info.success = true;
+
+ // Log the ban
+ let logChannel = guild.channels.cache
+ .get(IDs.channels.logs.restricted) as TextChannel | undefined;
+
+ 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 ${member} but could not find the log channel. This has been logged to the database.`;
+ return info;
+ }
+ }
+
+ const message = new EmbedBuilder()
+ .setColor('#FF6700')
+ .setAuthor({ name: `Restricted ${member.user.tag}`, iconURL: `${member.user.avatarURL()}` })
+ .addFields(
+ { name: 'User', value: `${member}`, inline: true },
+ { name: 'Moderator', value: `${mod}`, inline: true },
+ { name: 'Reason', value: reason },
+ )
+ .setTimestamp()
+ .setFooter({ text: `ID: ${member.id}` });
+
+ await logChannel.send({ embeds: [message] });
+
+ return info;
+}
+
+export class RestrictCommand extends Command {
+ public constructor(context: Command.Context, options: Command.Options) {
+ super(context, {
+ ...options,
+ name: 'restrict',
+ aliases: ['r', 'rest', 'rr'], // TODO add 'rv' when enabling vegan restrictions
+ description: 'Restricts a user',
+ preconditions: ['ModOnly'],
+ });
+ }
+
+ // Registers that this is a slash command
+ public override registerApplicationCommands(registry: Command.Registry) {
+ registry.registerChatInputCommand(
+ (builder) => builder
+ .setName(this.name)
+ .setDescription(this.description)
+ .addUserOption((option) => option.setName('user')
+ .setDescription('User to restrict')
+ .setRequired(true))
+ .addStringOption((option) => option.setName('reason')
+ .setDescription('Reason for restricting the user')
+ .setRequired(true)),
+ {
+ behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
+ },
+ );
+ }
+
+ // Command run
+ public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
+ // Get the arguments
+ const user = interaction.options.getUser('user', true);
+ const reason = interaction.options.getString('reason', true);
+ const mod = interaction.member;
+ const { guild } = interaction;
+
+ // Checks if all the variables are of the right type
+ if (guild === null || mod === null) {
+ await interaction.reply({
+ content: 'Error fetching user!',
+ ephemeral: true,
+ fetchReply: true,
+ });
+ return;
+ }
+
+ const info = await restrictRun(user?.id, mod.user.id, reason, guild);
+
+ await interaction.reply({
+ content: info.message,
+ fetchReply: true,
+ });
+ }
+
+ // Non Application Command method of banning a user
+ public async messageRun(message: Message, args: Args) {
+ // Get arguments
+ let user: User;
+ try {
+ user = await args.pick('user');
+ } catch {
+ await message.react('❌');
+ await message.reply('User was not provided!');
+ return;
+ }
+ const reason = args.finished ? null : await args.rest('string');
+ const mod = message.member;
+
+ if (reason === null) {
+ await message.react('❌');
+ await message.reply('Restrict reason was not provided!');
+ 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) {
+ await message.react('❌');
+ await message.reply('Guild not found! Try again or contact a developer!');
+ return;
+ }
+
+ const info = await restrictRun(user?.id, mod.user.id, reason, guild);
+
+ await message.reply(info.message);
+ await message.react(info.success ? '✅' : '❌');
+ }
+}
diff --git a/src/commands/mod/restriction/restrictTolerance.ts b/src/commands/mod/restriction/restrictTolerance.ts
new file mode 100644
index 0000000..113e2d5
--- /dev/null
+++ b/src/commands/mod/restriction/restrictTolerance.ts
@@ -0,0 +1,118 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+/*
+ Animal Rights Advocates Discord Bot
+ Copyright (C) 2023 Anthony Berg
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+import { Args, Command, RegisterBehavior } from '@sapphire/framework';
+import type { User, Message } from 'discord.js';
+import { restrictRun } from './restrict';
+
+export class RestrictToleranceCommand extends Command {
+ public constructor(context: Command.Context, options: Command.Options) {
+ super(context, {
+ ...options,
+ name: 'restricttolerance',
+ aliases: ['rt'],
+ description: 'Restricts a user for bigoted reasons',
+ preconditions: ['ModOnly'],
+ });
+ }
+
+ // Registers that this is a slash command
+ public override registerApplicationCommands(registry: Command.Registry) {
+ registry.registerChatInputCommand(
+ (builder) => builder
+ .setName(this.name)
+ .setDescription(this.description)
+ .addUserOption((option) => option.setName('user')
+ .setDescription('User to restrict')
+ .setRequired(true))
+ .addStringOption((option) => option.setName('reason')
+ .setDescription('Reason for restricting the user')
+ .setRequired(true)),
+ {
+ behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
+ },
+ );
+ }
+
+ // Command run
+ public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
+ // Get the arguments
+ const user = interaction.options.getUser('user', true);
+ const reason = interaction.options.getString('reason', true);
+ const mod = interaction.member;
+ const { guild } = interaction;
+
+ // Checks if all the variables are of the right type
+ if (guild === null || mod === null) {
+ await interaction.reply({
+ content: 'Error fetching user!',
+ ephemeral: true,
+ fetchReply: true,
+ });
+ return;
+ }
+
+ const info = await restrictRun(user?.id, mod.user.id, reason, guild, true);
+
+ await interaction.reply({
+ content: info.message,
+ fetchReply: true,
+ });
+ }
+
+ // Non Application Command method of banning a user
+ public async messageRun(message: Message, args: Args) {
+ // Get arguments
+ let user: User;
+ try {
+ user = await args.pick('user');
+ } catch {
+ await message.react('❌');
+ await message.reply('User was not provided!');
+ return;
+ }
+ const reason = args.finished ? null : await args.rest('string');
+ const mod = message.member;
+
+ if (reason === null) {
+ await message.react('❌');
+ await message.reply('Restrict reason was not provided!');
+ 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) {
+ await message.react('❌');
+ await message.reply('Guild not found! Try again or contact a developer!');
+ return;
+ }
+
+ const info = await restrictRun(user?.id, mod.user.id, reason, guild, true);
+
+ await message.reply(info.message);
+ await message.react(info.success ? '✅' : '❌');
+ }
+}
diff --git a/src/commands/mod/restriction/unrestrict.ts b/src/commands/mod/restriction/unrestrict.ts
new file mode 100644
index 0000000..ac4831f
--- /dev/null
+++ b/src/commands/mod/restriction/unrestrict.ts
@@ -0,0 +1,236 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+/*
+ Animal Rights Advocates Discord Bot
+ Copyright (C) 2023 Anthony Berg
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+import { Args, Command, RegisterBehavior } from '@sapphire/framework';
+import { CategoryChannel, ChannelType, EmbedBuilder } from 'discord.js';
+import type {
+ User,
+ Message,
+ TextChannel,
+ Guild,
+ Snowflake,
+} from 'discord.js';
+import IDs from '#utils/ids';
+import { fetchRoles, addExistingUser, userExists } from '#utils/database/dbExistingUser';
+import { unRestrict, checkActive, unRestrictLegacy } from '#utils/database/restriction';
+
+export class UnRestrictCommand extends Command {
+ public constructor(context: Command.Context, options: Command.Options) {
+ super(context, {
+ ...options,
+ name: 'unrestrict',
+ aliases: ['ur', 'urv'],
+ description: 'Unrestricts a user',
+ preconditions: ['ModOnly'],
+ });
+ }
+
+ // Registers that this is a slash command
+ public override registerApplicationCommands(registry: Command.Registry) {
+ registry.registerChatInputCommand(
+ (builder) => builder
+ .setName(this.name)
+ .setDescription(this.description)
+ .addUserOption((option) => option.setName('user')
+ .setDescription('User to unrestrict')
+ .setRequired(true)),
+ {
+ behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
+ },
+ );
+ }
+
+ // Command run
+ public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
+ // Get the arguments
+ 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 (guild === null || mod === null) {
+ await interaction.reply({
+ content: 'Error fetching user!',
+ ephemeral: true,
+ fetchReply: true,
+ });
+ return;
+ }
+
+ const info = await this.unRestrictRun(user?.id, mod.user.id, guild);
+
+ await interaction.reply({
+ content: info.message,
+ fetchReply: true,
+ });
+ }
+
+ // Non Application Command method of banning a user
+ public async messageRun(message: Message, args: Args) {
+ // Get arguments
+ let user: User;
+ try {
+ user = await args.pick('user');
+ } catch {
+ await message.react('❌');
+ await message.reply('User was not provided!');
+ return;
+ }
+
+ const mod = message.member;
+
+ 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) {
+ await message.react('❌');
+ await message.reply('Guild not found! Try again or contact a developer!');
+ return;
+ }
+
+ const info = await this.unRestrictRun(user?.id, mod.user.id, guild);
+
+ await message.reply(info.message);
+ await message.react(info.success ? '✅' : '❌');
+ }
+
+ private async unRestrictRun(userId: Snowflake, modId: Snowflake, guild: Guild) {
+ const info = {
+ message: '',
+ success: false,
+ };
+
+ // Gets mod's GuildMember
+ const mod = guild.members.cache.get(modId);
+
+ // Checks if guildMember is null
+ if (mod === undefined) {
+ info.message = 'Error fetching mod';
+ return info;
+ }
+
+ // Check if mod is in database
+ if (!await userExists(mod.id)) {
+ await addExistingUser(mod);
+ }
+
+ // Gets guildMember
+ let member = guild.members.cache.get(userId);
+
+ if (member === undefined) {
+ member = await guild.members.fetch(userId);
+ }
+
+ if (member === undefined) {
+ info.message = 'Can\'t unrestrict the user as they are not on this server';
+ return info;
+ }
+
+ const restrictRoles = IDs.roles.restrictions.restricted;
+
+ // Checks if the user is not restricted
+ if (!member.roles.cache.hasAny(...restrictRoles)) {
+ info.message = `${member} is not restricted!`;
+ return info;
+ }
+
+ 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 += 0) {
+ 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);
+ }
+
+ await member.roles.remove(restrictRoles);
+
+ // 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;
+
+ 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)) {
+ topic = textChannel.topic.split(' ');
+ const vcId = topic[topic.indexOf(userId) + 1];
+ const voiceChannel = guild.channels.cache.get(vcId);
+
+ if (voiceChannel !== undefined
+ && voiceChannel.parentId === IDs.categories.restricted) {
+ voiceChannel.delete();
+ }
+ textChannel.delete();
+ }
+ });
+ }
+ }
+
+ info.success = true;
+
+ // Log the ban
+ let logChannel = guild.channels.cache
+ .get(IDs.channels.logs.restricted) as TextChannel | undefined;
+
+ 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 ${member} but could not find the log channel. This has been logged to the database.`;
+ return info;
+ }
+ }
+
+ const message = new EmbedBuilder()
+ .setColor('#28A745')
+ .setAuthor({ name: `Unrestricted ${member.user.tag}`, iconURL: `${member.user.avatarURL()}` })
+ .addFields(
+ { name: 'User', value: `${member}`, inline: true },
+ { name: 'Moderator', value: `${mod}`, inline: true },
+ )
+ .setTimestamp()
+ .setFooter({ text: `ID: ${member.id}` });
+
+ await logChannel.send({ embeds: [message] });
+
+ info.message = `Unrestricted ${member}`;
+ return info;
+ }
+}
diff --git a/src/listeners/verification/joinServer.ts b/src/listeners/rolesJoinServer.ts
similarity index 64%
rename from src/listeners/verification/joinServer.ts
rename to src/listeners/rolesJoinServer.ts
index d9ae243..fa30a45 100644
--- a/src/listeners/verification/joinServer.ts
+++ b/src/listeners/rolesJoinServer.ts
@@ -19,11 +19,12 @@
import { Listener } from '@sapphire/framework';
import type { GuildMember } from 'discord.js';
-// import { fetchRoles } from '../../utils/database/dbExistingUser';
-import IDs from '#utils/ids';
-import { blockTime } from '#utils/database/verification';
+import { fetchRoles } from '#utils/database/dbExistingUser';
+// import IDs from '#utils/ids';
+// import { blockTime } from '#utils/database/verification';
+// import { checkActive, getSection } from '#utils/database/restriction';
-export class VerificationReady extends Listener {
+export class RolesJoinServerListener extends Listener {
public constructor(context: Listener.Context, options: Listener.Options) {
super(context, {
...options,
@@ -31,20 +32,27 @@ export class VerificationReady extends Listener {
});
}
- public async run(user: GuildMember) {
+ public async run(member: GuildMember) {
// Add basic roles
- // Removed this because it can give restricted people access back,
- // Currently using another bot for this
- // const roles = await fetchRoles(user.id);
- const roles: string[] = [];
+
+ const roles = await fetchRoles(member.id);
+
+ /*
+ // Check if the user is restricted
+ if (await checkActive(member.id)) {
+ const section = await getSection(member.id);
+ roles.length = 0;
+ roles.push(IDs.roles.restrictions.restricted[section - 1]);
+ }
// Check if the user has a verification block
- const timeout = await blockTime(user.id);
+ const timeout = await blockTime(member.id);
if (timeout > 0) {
roles.push(IDs.roles.verifyBlock);
}
+ */
// Add roles if they don't have verification block
- await user.roles.add(roles);
+ await member.roles.add(roles);
}
}
diff --git a/src/utils/database/dbExistingUser.ts b/src/utils/database/dbExistingUser.ts
index f64c58d..153c2c2 100644
--- a/src/utils/database/dbExistingUser.ts
+++ b/src/utils/database/dbExistingUser.ts
@@ -17,12 +17,12 @@
along with this program. If not, see .
*/
-import type { GuildMember, GuildMemberRoleManager } from 'discord.js';
+import type { GuildMember, GuildMemberRoleManager, Snowflake } from 'discord.js';
import { container } from '@sapphire/framework';
import IDs from '#utils/ids';
// Checks if the user exists on the database
-export async function userExists(userId: string) {
+export async function userExists(userId: Snowflake) {
// Counts if the user is on the database by their snowflake
const userQuery = await container.database.user.count({
where: {
@@ -51,11 +51,11 @@ function getRoles(roles: GuildMemberRoleManager) {
}
// Adds the user to the database if they were already on the server before the bot/database
-export async function addExistingUser(user: GuildMember) {
+export async function addExistingUser(member: GuildMember) {
// Counts if the user is on the database by their snowflake
const userQuery = await container.database.user.count({
where: {
- id: user.id,
+ id: member.id,
},
});
@@ -65,12 +65,12 @@ export async function addExistingUser(user: GuildMember) {
}
// Parse all the roles into a dictionary
- const roles = getRoles(user.roles);
+ const roles = getRoles(member.roles);
// Create the user in the database
await container.database.user.create({
data: {
- id: user.id,
+ id: member.id,
vegan: roles.vegan,
trusted: roles.trusted,
activist: roles.activist,
@@ -84,7 +84,7 @@ export async function addExistingUser(user: GuildMember) {
}
// Add an empty user to database in case they are not on the server
-export async function addEmptyUser(userId: string) {
+export async function addEmptyUser(userId: Snowflake) {
// Counts if the user is on the database by their snowflake
const userQuery = await container.database.user.count({
where: {
@@ -105,22 +105,22 @@ export async function addEmptyUser(userId: string) {
});
}
-export async function updateUser(user: GuildMember) {
+export async function updateUser(member: GuildMember) {
// Check if the user is already on the database
- if (!(await userExists(user.id))) {
- await addExistingUser(user);
+ if (!(await userExists(member.id))) {
+ await addExistingUser(member);
return;
}
// Parse all the roles into a dictionary
- const roles = getRoles(user.roles);
+ const roles = getRoles(member.roles);
await container.database.user.update({
where: {
- id: user.id,
+ id: member.id,
},
data: {
- id: user.id,
+ id: member.id,
vegan: roles.vegan,
trusted: roles.trusted,
activist: roles.activist,
@@ -133,11 +133,11 @@ export async function updateUser(user: GuildMember) {
});
}
-export async function fetchRoles(user: string) {
+export async function fetchRoles(userId: Snowflake) {
// Get the user's roles
const roleQuery = await container.database.user.findUnique({
where: {
- id: user,
+ id: userId,
},
select: {
vegan: true,
diff --git a/src/utils/database/restriction.ts b/src/utils/database/restriction.ts
new file mode 100644
index 0000000..5117401
--- /dev/null
+++ b/src/utils/database/restriction.ts
@@ -0,0 +1,124 @@
+import { container } from '@sapphire/framework';
+import type { Snowflake } from 'discord.js';
+
+export async function restrict(
+ userId: Snowflake,
+ modId: Snowflake,
+ reason: string,
+ section: number,
+) {
+ // Add the user to the database
+ await container.database.restrict.create({
+ data: {
+ user: {
+ connect: {
+ id: userId,
+ },
+ },
+ mod: {
+ connect: {
+ id: modId,
+ },
+ },
+ reason,
+ section,
+ },
+ });
+}
+
+export async function unRestrict(userId: Snowflake, modId: Snowflake) {
+ const restriction = await container.database.restrict.findFirst({
+ where: {
+ userId,
+ },
+ select: {
+ id: true,
+ },
+ orderBy: {
+ id: 'desc',
+ },
+ });
+
+ if (restriction === null) {
+ return;
+ }
+
+ await container.database.restrict.update({
+ where: {
+ id: restriction.id,
+ },
+ data: {
+ endMod: {
+ connect: {
+ id: modId,
+ },
+ },
+ endTime: new Date(),
+ },
+ });
+}
+
+export async function checkActive(userId: Snowflake) {
+ const restriction = await container.database.restrict.findFirst({
+ where: {
+ userId,
+ },
+ select: {
+ endTime: true,
+ },
+ orderBy: {
+ id: 'desc',
+ },
+ });
+
+ if (restriction === null) {
+ return false;
+ }
+
+ return restriction.endTime === null;
+}
+
+export async function getSection(userId: Snowflake) {
+ const restriction = await container.database.restrict.findFirst({
+ where: {
+ userId,
+ },
+ select: {
+ section: true,
+ },
+ orderBy: {
+ id: 'desc',
+ },
+ });
+
+ if (restriction === null) {
+ return 0;
+ }
+
+ return restriction.section;
+}
+
+// This is only for restrictions created with the old bot
+export async function unRestrictLegacy(userId: Snowflake, modId: Snowflake, section: number) {
+ await container.database.restrict.create({
+ data: {
+ user: {
+ connect: {
+ id: userId,
+ },
+ },
+ mod: {
+ connect: {
+ id: modId,
+ },
+ },
+ endMod: {
+ connect: {
+ id: modId,
+ },
+ },
+ reason: 'This user was restricted with the old bot. Restrict reason, time and mod unknown, check old bot logs.',
+ section,
+ },
+ });
+}
diff --git a/src/utils/devIDs.ts b/src/utils/devIDs.ts
index efe6e5d..4d6e1ef 100644
--- a/src/utils/devIDs.ts
+++ b/src/utils/devIDs.ts
@@ -38,6 +38,13 @@ const devIDs = {
restricted2: '999431674997788676',
restricted3: '999431674997788675',
restricted4: '999431674997788674',
+ restricted: [
+ '999431674997788677', // Restricted 1
+ '999431674997788676', // Restricted 2
+ '999431674997788675', // Restricted 3
+ '999431674997788674', // Restricted 4
+ '999431674997788677', // Restricted Vegan
+ ],
},
staff: {
coordinator: '999431675165556822',
diff --git a/src/utils/ids.ts b/src/utils/ids.ts
index 98a282d..351f857 100644
--- a/src/utils/ids.ts
+++ b/src/utils/ids.ts
@@ -41,6 +41,13 @@ let IDs = {
restricted2: '872482843304001566',
restricted3: '856582673258774538',
restricted4: '872472182888992858',
+ restricted: [
+ '809769217477050369', // Restricted 1
+ '872482843304001566', // Restricted 2
+ '856582673258774538', // Restricted 3
+ '872472182888992858', // Restricted 4
+ '809769217477050369', // Restricted Vegan
+ ],
},
staff: {
coordinator: '993636242019323904',
diff --git a/src/utils/random.ts b/src/utils/random.ts
new file mode 100644
index 0000000..d214d7a
--- /dev/null
+++ b/src/utils/random.ts
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-3.0-or-later
+/*
+ Animal Rights Advocates Discord Bot
+ Copyright (C) 2023 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 .
+*/
+
+// Random integer between min and max
+export function randint(min: number, max: number) {
+ return Math.floor(Math.random() * (max - min + 1)) + min;
+}