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