From 91631995a950399527b0fe4e547f06bc25b8693e Mon Sep 17 00:00:00 2001 From: smyalygames Date: Fri, 10 Feb 2023 00:59:10 +0000 Subject: [PATCH 01/24] feat(arabot): start restriction command --- src/commands/mod/restriction/restrict.ts | 254 +++++++++++++++++++++++ src/utils/database/restriction.ts | 79 +++++++ 2 files changed, 333 insertions(+) create mode 100644 src/commands/mod/restriction/restrict.ts create mode 100644 src/utils/database/restriction.ts diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts new file mode 100644 index 0000000..78be63d --- /dev/null +++ b/src/commands/mod/restriction/restrict.ts @@ -0,0 +1,254 @@ +// 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, TextChannel } from 'discord.js'; +import IDs from '#utils/ids'; +import { addEmptyUser, addExistingUser, userExists } from '#utils/database/dbExistingUser'; + +export class RestrictCommand extends Command { + public constructor(context: Command.Context, options: Command.Options) { + super(context, { + ...options, + name: 'restrict', + aliases: ['r', 'rest', 'rr'], + 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; + } + + // Gets mod's GuildMember + const modGuildMember = guild.members.cache.get(mod.user.id); + + // Checks if guildMember is null + if (modGuildMember === undefined) { + await interaction.reply({ + content: 'Error fetching mod!', + ephemeral: true, + fetchReply: true, + }); + return; + } + + // Check if mod is in database + if (!await userExists(modGuildMember.id)) { + await addExistingUser(modGuildMember); + } + + // Gets guildMember + let guildMember = guild.members.cache.get(user.id); + + if (guildMember === undefined) { + guildMember = await guild.members.fetch(user.id); + } + + if (guildMember !== undefined) { + // Checks if the user is not restricted + if (guildMember.roles.cache.has(IDs.roles.vegan.vegan)) { + await interaction.reply({ + content: 'You need to restrict the user first!', + ephemeral: true, + fetchReply: true, + }); + return; + } + + // Check if user and mod are on the database + if (!await userExists(guildMember.id)) { + await addExistingUser(guildMember); + } + + // Send DM for reason of ban + await user.send(`You have been banned from ARA for: ${reason}` + + '\n\nhttps://vbcamp.org/ARA') + .catch(() => {}); + + // Ban the user + await guildMember.ban({ reason }); + } else if (!await userExists(user.id)) { + await addEmptyUser(user.id); + } + + await interaction.reply({ + content: `${user} has been banned.`, + ephemeral: true, + fetchReply: true, + }); + + // Add ban to database + await addBan(user.id, mod.user.id, reason); + + // 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('Ban Error: Could not fetch log channel'); + return; + } + } + + await logChannel.send(`${user} was banned for: ${reason} by ${mod}`); + } + + // 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('Ban 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; + } + + if (await checkActive(user.id)) { + await message.react('❌'); + await message.reply(`${user} is already banned!`); + return; + } + + if (message.channel.id !== IDs.channels.restricted.moderators) { + await message.react('❌'); + await message.reply(`You can only run this command in <#${IDs.channels.restricted.moderators}> ` + + 'or alternatively use the slash command!'); + return; + } + + // Check if mod is in database + if (!await userExists(mod.id)) { + await addExistingUser(mod); + } + + // Gets guildMember + let guildMember = await guild.members.cache.get(user.id); + + if (guildMember === undefined) { + guildMember = await guild.members.fetch(user.id); + } + + if (guildMember !== undefined) { + // Checks if the user is not restricted + if (guildMember.roles.cache.has(IDs.roles.vegan.vegan)) { + await message.react('❌'); + await message.reply({ + content: 'You need to restrict the user first!', + }); + return; + } + + // Check if user and mod are on the database + if (!await userExists(guildMember.id)) { + await addExistingUser(guildMember); + } + + // Send DM for reason of ban + await user.send(`You have been banned from ARA for: ${reason}` + + '\n\nhttps://vbcamp.org/ARA') + .catch(() => {}); + + // Ban the user + await guildMember.ban({ reason }); + } else if (!await userExists(user.id)) { + await addEmptyUser(user.id); + } + + // Add ban to database + await addBan(user.id, mod.id, reason); + + await message.react('✅'); + + // 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('Ban Error: Could not fetch log channel'); + return; + } + } + + await logChannel.send(`${user} was banned for: ${reason} by ${mod}`); + } +} diff --git a/src/utils/database/restriction.ts b/src/utils/database/restriction.ts new file mode 100644 index 0000000..44953da --- /dev/null +++ b/src/utils/database/restriction.ts @@ -0,0 +1,79 @@ +import { container } from '@sapphire/framework'; + +export async function addRestriction(userId: string, modId: string, reason: string) { + await container.database.restrict.create({ + data: { + user: { + connect: { + id: userId, + }, + }, + mod: { + connect: { + id: modId, + }, + }, + reason, + }, + }); +} + +export async function removeRestriction(userId: string, modId: string) { + const restrict = await container.database.restrict.findFirst({ + where: { + userId, + }, + orderBy: { + id: 'desc', + }, + }); + + if (restrict === null) { + return; + } + + // Query to deactivate the specific sus note + await container.database.restrict.update({ + where: { + id: restrict.id, + }, + data: { + endModId: modId, + endTime: new Date(), + }, + }); +} + +export async function checkActive(userId: string) { + const restrict = await container.database.ban.findFirst({ + where: { + userId, + }, + orderBy: { + id: 'desc', + }, + }); + + if (restrict === null) { + return false; + } + + return restrict.active; +} + +export async function getReason(userId: string) { + const restrict = await container.database.restrict.findFirst({ + where: { + userId, + }, + orderBy: { + id: 'desc', + }, + }); + + if (restrict === null) { + return ''; + } + + return restrict.reason; +} From 2339ae6784b16a7242e5582cf232f26e5d81fa16 Mon Sep 17 00:00:00 2001 From: smyalygames Date: Fri, 10 Feb 2023 23:39:18 +0000 Subject: [PATCH 02/24] feat(db): add mod who unrestricted --- prisma/schema.prisma | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 129ad31..d698ebe 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -41,6 +41,7 @@ model User { SusMod Sus[] @relation("susMod") RestrictUser Restrict[] @relation("restUser") RestrictMod Restrict[] @relation("restMod") + RestrictEndMod Restrict[] @relation("endRestMod") BanUser Ban[] @relation("banUser") BanMod Ban[] @relation("banMod") BanEndMod Ban[] @relation("endBanMod") @@ -95,6 +96,8 @@ model Restrict { mod User @relation("restMod", fields: [modId], references: [id]) modId String startTime DateTime @default(now()) + endMod User? @relation("endRestMod", fields: [endModId], references: [id]) + endModId String? endedTime DateTime? reason String } From c0c1c16c379625a3d6d6dd5884d397642dc761ae Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 18:17:23 +0000 Subject: [PATCH 03/24] refactor(db): fix typos in column names --- .../20230211163443_restrict/migration.sql | 5 +++++ .../20230211163734_fix_typo/migration.sql | 15 +++++++++++++++ prisma/schema.prisma | 4 ++-- 3 files changed, 22 insertions(+), 2 deletions(-) create mode 100644 prisma/migrations/20230211163443_restrict/migration.sql create mode 100644 prisma/migrations/20230211163734_fix_typo/migration.sql diff --git a/prisma/migrations/20230211163443_restrict/migration.sql b/prisma/migrations/20230211163443_restrict/migration.sql new file mode 100644 index 0000000..a9f047e --- /dev/null +++ b/prisma/migrations/20230211163443_restrict/migration.sql @@ -0,0 +1,5 @@ +-- AlterTable +ALTER TABLE "Restrict" ADD COLUMN "endModId" TEXT; + +-- AddForeignKey +ALTER TABLE "Restrict" ADD CONSTRAINT "Restrict_endModId_fkey" FOREIGN KEY ("endModId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20230211163734_fix_typo/migration.sql b/prisma/migrations/20230211163734_fix_typo/migration.sql new file mode 100644 index 0000000..930458b --- /dev/null +++ b/prisma/migrations/20230211163734_fix_typo/migration.sql @@ -0,0 +1,15 @@ +/* + Warnings: + + - You are about to drop the column `endedTime` on the `Restrict` table. All the data in the column will be lost. + - You are about to drop the column `endedTime` on the `TempBan` table. All the data in the column will be lost. + - Added the required column `endTime` to the `TempBan` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Restrict" DROP COLUMN "endedTime", +ADD COLUMN "endTime" TIMESTAMP(3); + +-- AlterTable +ALTER TABLE "TempBan" DROP COLUMN "endedTime", +ADD COLUMN "endTime" TIMESTAMP(3) NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index db3f3ff..9566290 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -100,7 +100,7 @@ model Restrict { startTime DateTime @default(now()) endMod User? @relation("endRestMod", fields: [endModId], references: [id]) endModId String? - endedTime DateTime? + endTime DateTime? reason String } @@ -125,7 +125,7 @@ model TempBan { mod User @relation("tbanMod", fields: [modId], references: [id]) modId String startTime DateTime @default(now()) - endedTime DateTime + endTime DateTime active Boolean @default(true) reason String } From 5ed15e6206275f6e82b9be98eb3228c30b61940f Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 20:25:14 +0000 Subject: [PATCH 04/24] feat(arabot): add restrict command --- src/commands/mod/restriction/restrict.ts | 301 ++++++++++++++--------- src/utils/database/restriction.ts | 49 ++-- 2 files changed, 212 insertions(+), 138 deletions(-) diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index 78be63d..5175f40 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -18,16 +18,29 @@ */ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; -import type { User, Message, TextChannel } from 'discord.js'; +import { + ChannelType, + EmbedBuilder, + PermissionsBitField, + time, +} from 'discord.js'; +import type { + User, + Message, + TextChannel, + Guild, + Snowflake, +} from 'discord.js'; import IDs from '#utils/ids'; import { addEmptyUser, addExistingUser, userExists } from '#utils/database/dbExistingUser'; +import { restrict, checkActive } from '#utils/database/restriction'; export class RestrictCommand extends Command { public constructor(context: Command.Context, options: Command.Options) { super(context, { ...options, name: 'restrict', - aliases: ['r', 'rest', 'rr'], + aliases: ['r', 'rest', 'rr', 'rv'], description: 'Restricts a user', preconditions: ['ModOnly'], }); @@ -69,81 +82,12 @@ export class RestrictCommand extends Command { return; } - // Gets mod's GuildMember - const modGuildMember = guild.members.cache.get(mod.user.id); - - // Checks if guildMember is null - if (modGuildMember === undefined) { - await interaction.reply({ - content: 'Error fetching mod!', - ephemeral: true, - fetchReply: true, - }); - return; - } - - // Check if mod is in database - if (!await userExists(modGuildMember.id)) { - await addExistingUser(modGuildMember); - } - - // Gets guildMember - let guildMember = guild.members.cache.get(user.id); - - if (guildMember === undefined) { - guildMember = await guild.members.fetch(user.id); - } - - if (guildMember !== undefined) { - // Checks if the user is not restricted - if (guildMember.roles.cache.has(IDs.roles.vegan.vegan)) { - await interaction.reply({ - content: 'You need to restrict the user first!', - ephemeral: true, - fetchReply: true, - }); - return; - } - - // Check if user and mod are on the database - if (!await userExists(guildMember.id)) { - await addExistingUser(guildMember); - } - - // Send DM for reason of ban - await user.send(`You have been banned from ARA for: ${reason}` - + '\n\nhttps://vbcamp.org/ARA') - .catch(() => {}); - - // Ban the user - await guildMember.ban({ reason }); - } else if (!await userExists(user.id)) { - await addEmptyUser(user.id); - } + const info = await this.restrictRun(user?.id, mod.user.id, reason, guild); await interaction.reply({ - content: `${user} has been banned.`, - ephemeral: true, + content: info.message, fetchReply: true, }); - - // Add ban to database - await addBan(user.id, mod.user.id, reason); - - // 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('Ban Error: Could not fetch log channel'); - return; - } - } - - await logChannel.send(`${user} was banned for: ${reason} by ${mod}`); } // Non Application Command method of banning a user @@ -180,17 +124,25 @@ export class RestrictCommand extends Command { return; } - if (await checkActive(user.id)) { - await message.react('❌'); - await message.reply(`${user} is already banned!`); - return; - } + const info = await this.restrictRun(user?.id, mod.user.id, reason, guild); - if (message.channel.id !== IDs.channels.restricted.moderators) { - await message.react('❌'); - await message.reply(`You can only run this command in <#${IDs.channels.restricted.moderators}> ` - + 'or alternatively use the slash command!'); - return; + await message.reply(info.message); + await message.react(info.success ? '✅' : '❌'); + } + + private async restrictRun(userId: Snowflake, modId: Snowflake, reason: string, 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 @@ -198,43 +150,155 @@ export class RestrictCommand extends Command { await addExistingUser(mod); } - // Gets guildMember - let guildMember = await guild.members.cache.get(user.id); - - if (guildMember === undefined) { - guildMember = await guild.members.fetch(user.id); + if (await checkActive(userId)) { + info.message = `<@${userId}> is already restricted!`; + return info; } - if (guildMember !== undefined) { + // Gets guildMember + let member = guild.members.cache.get(userId); + + if (member === undefined) { + member = await guild.members.fetch(userId); + } + + if (member !== undefined) { // Checks if the user is not restricted - if (guildMember.roles.cache.has(IDs.roles.vegan.vegan)) { - await message.react('❌'); - await message.reply({ - content: 'You need to restrict the user first!', - }); - return; + if (member.roles.cache.hasAny( + IDs.roles.restrictions.restricted1, + IDs.roles.restrictions.restricted2, + IDs.roles.restrictions.restricted3, + IDs.roles.restrictions.restricted4, + )) { + info.message = `${member} is already restricted!`; + return info; } // Check if user and mod are on the database - if (!await userExists(guildMember.id)) { - await addExistingUser(guildMember); + if (!await userExists(member.id)) { + await addExistingUser(member); } - // Send DM for reason of ban - await user.send(`You have been banned from ARA for: ${reason}` - + '\n\nhttps://vbcamp.org/ARA') - .catch(() => {}); + if (member.roles.cache.has(IDs.roles.vegan.vegan)) { + await member.roles.add(IDs.roles.restrictions.restricted1); - // Ban the user - await guildMember.ban({ reason }); - } else if (!await userExists(user.id)) { - await addEmptyUser(user.id); + 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.private, + 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.private, + 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(`Private 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] }); + } else { + await member.roles.add(Math.random() > 0.5 + ? IDs.roles.restrictions.restricted1 + : IDs.roles.restrictions.restricted2); + } + + 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)) { + await addEmptyUser(userId); } - // Add ban to database - await addBan(user.id, mod.id, reason); + // Restrict the user on the database + await restrict(userId, modId, reason); - await message.react('✅'); + info.success = true; // Log the ban let logChannel = guild.channels.cache @@ -244,11 +308,26 @@ export class RestrictCommand extends Command { 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'); - return; + this.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; } } - await logChannel.send(`${user} was banned for: ${reason} by ${mod}`); + const message = new EmbedBuilder() + .setColor('#FF6700') + .setAuthor({ name: `Restricted ${member.user.tag}`, iconURL: `${member.user.avatarURL()}` }) + .addFields( + { name: 'User', value: `${member}` }, + { name: 'Moderator', value: `${mod}` }, + ) + .addFields({ name: 'Reason', value: reason }) + .setTimestamp() + .setFooter({ text: `ID: ${member.id}` }); + + await logChannel.send({ embeds: [message] }); + + info.message = `Restricted ${member}`; + return info; } } diff --git a/src/utils/database/restriction.ts b/src/utils/database/restriction.ts index 44953da..a86deb0 100644 --- a/src/utils/database/restriction.ts +++ b/src/utils/database/restriction.ts @@ -1,6 +1,8 @@ import { container } from '@sapphire/framework'; +import type { Snowflake } from 'discord.js'; -export async function addRestriction(userId: string, modId: string, reason: string) { +export async function restrict(userId: Snowflake, modId: Snowflake, reason: string) { + // Add the user to the database await container.database.restrict.create({ data: { user: { @@ -18,62 +20,55 @@ export async function addRestriction(userId: string, modId: string, reason: stri }); } -export async function removeRestriction(userId: string, modId: string) { - const restrict = await container.database.restrict.findFirst({ +export async function unRestrict(userId: Snowflake, modId: Snowflake) { + const restriction = await container.database.restrict.findFirst({ where: { userId, }, + select: { + id: true, + }, orderBy: { id: 'desc', }, }); - if (restrict === null) { + if (restriction === null) { return; } // Query to deactivate the specific sus note await container.database.restrict.update({ where: { - id: restrict.id, + id: restriction.id, }, data: { - endModId: modId, + endMod: { + connect: { + id: modId, + }, + }, endTime: new Date(), }, }); } -export async function checkActive(userId: string) { - const restrict = await container.database.ban.findFirst({ +export async function checkActive(userId: Snowflake) { + const restriction = await container.database.restrict.findFirst({ where: { userId, }, + select: { + endTime: true, + }, orderBy: { id: 'desc', }, }); - if (restrict === null) { + if (restriction === null) { return false; } - return restrict.active; -} - -export async function getReason(userId: string) { - const restrict = await container.database.restrict.findFirst({ - where: { - userId, - }, - orderBy: { - id: 'desc', - }, - }); - - if (restrict === null) { - return ''; - } - - return restrict.reason; + return restriction.endTime === null; } From e89fcb53142b2eecc6ca534f6a2f3f451983dcaf Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 20:50:32 +0000 Subject: [PATCH 05/24] feat(arabot): change user and mod in embed to inline --- src/commands/mod/restriction/restrict.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index 5175f40..45136a9 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -318,10 +318,10 @@ export class RestrictCommand extends Command { .setColor('#FF6700') .setAuthor({ name: `Restricted ${member.user.tag}`, iconURL: `${member.user.avatarURL()}` }) .addFields( - { name: 'User', value: `${member}` }, - { name: 'Moderator', value: `${mod}` }, + { name: 'User', value: `${member}`, inline: true }, + { name: 'Moderator', value: `${mod}`, inline: true }, + { name: 'Reason', value: reason }, ) - .addFields({ name: 'Reason', value: reason }) .setTimestamp() .setFooter({ text: `ID: ${member.id}` }); From 1bacef1f08c3cf732ceee11e48350bfdaea77abb Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 20:51:03 +0000 Subject: [PATCH 06/24] feat(arabot): add unrestrict command --- src/commands/mod/restriction/unrestrict.ts | 209 +++++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 src/commands/mod/restriction/unrestrict.ts diff --git a/src/commands/mod/restriction/unrestrict.ts b/src/commands/mod/restriction/unrestrict.ts new file mode 100644 index 0000000..37e3539 --- /dev/null +++ b/src/commands/mod/restriction/unrestrict.ts @@ -0,0 +1,209 @@ +// 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 { 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 } 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; + } + + // Checks if the user is not restricted + if (!member.roles.cache.hasAny( + IDs.roles.restrictions.restricted1, + IDs.roles.restrictions.restricted2, + IDs.roles.restrictions.restricted3, + IDs.roles.restrictions.restricted4, + )) { + info.message = `${member} is not restricted!`; + return info; + } + + if (await checkActive(userId)) { + const roles = await fetchRoles(userId); + await member.roles.add(roles); + } else { + await member.roles.add(IDs.roles.nonvegan.nonvegan); + } + + await member.roles.remove([ + IDs.roles.restrictions.restricted1, + IDs.roles.restrictions.restricted2, + IDs.roles.restrictions.restricted3, + IDs.roles.restrictions.restricted4, + ]); + + // Unrestricts the user on the database + await unRestrict(userId, modId); + + 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; + } +} From c0da7cb1fa9608d437bf0d14af058d391433b19d Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 20:53:11 +0000 Subject: [PATCH 07/24] fix(arabot): change restrict category from private to restrict in vegan restrict --- src/commands/mod/restriction/restrict.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index 45136a9..61d7c57 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -106,7 +106,7 @@ export class RestrictCommand extends Command { if (reason === null) { await message.react('❌'); - await message.reply('Ban reason was not provided!'); + await message.reply('Restrict reason was not provided!'); return; } @@ -212,7 +212,7 @@ export class RestrictCommand extends Command { name: `⛔┃${member.user.username}-restricted`, type: ChannelType.GuildText, topic: `Restricted channel. ${member.id} ${voiceChannel.id} (Please do not change this)`, - parent: IDs.categories.private, + parent: IDs.categories.restricted, permissionOverwrites: [ { id: guild.roles.everyone, @@ -235,7 +235,7 @@ export class RestrictCommand extends Command { name: `⛔┃${member.user.id}-restricted`, type: ChannelType.GuildText, topic: `Restricted channel. ${member.id} ${voiceChannel.id} (Please do not change this)`, - parent: IDs.categories.private, + parent: IDs.categories.restricted, permissionOverwrites: [ { id: guild.roles.everyone, From a8250eb514c8af676f94bd0a4788e495d30c3782 Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 21:13:25 +0000 Subject: [PATCH 08/24] refactor(arabot): change adding user to database --- src/commands/mod/restriction/restrict.ts | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index 61d7c57..1b6526d 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -32,7 +32,7 @@ import type { Snowflake, } from 'discord.js'; import IDs from '#utils/ids'; -import { addEmptyUser, addExistingUser, userExists } from '#utils/database/dbExistingUser'; +import { addEmptyUser, updateUser, userExists } from '#utils/database/dbExistingUser'; import { restrict, checkActive } from '#utils/database/restriction'; export class RestrictCommand extends Command { @@ -146,9 +146,7 @@ export class RestrictCommand extends Command { } // Check if mod is in database - if (!await userExists(mod.id)) { - await addExistingUser(mod); - } + await updateUser(mod); if (await checkActive(userId)) { info.message = `<@${userId}> is already restricted!`; @@ -175,9 +173,7 @@ export class RestrictCommand extends Command { } // Check if user and mod are on the database - if (!await userExists(member.id)) { - await addExistingUser(member); - } + await updateUser(member); if (member.roles.cache.has(IDs.roles.vegan.vegan)) { await member.roles.add(IDs.roles.restrictions.restricted1); From 63f161b2d1340e46a3be53853a133dfcdfd2e84a Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 21:13:38 +0000 Subject: [PATCH 09/24] feat(arabot): add remove vegan restrict channel --- src/commands/mod/restriction/unrestrict.ts | 30 +++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/src/commands/mod/restriction/unrestrict.ts b/src/commands/mod/restriction/unrestrict.ts index 37e3539..18a6ee8 100644 --- a/src/commands/mod/restriction/unrestrict.ts +++ b/src/commands/mod/restriction/unrestrict.ts @@ -18,7 +18,7 @@ */ import { Args, Command, RegisterBehavior } from '@sapphire/framework'; -import { EmbedBuilder } from 'discord.js'; +import { CategoryChannel, ChannelType, EmbedBuilder } from 'discord.js'; import type { User, Message, @@ -175,6 +175,34 @@ export class UnRestrictCommand extends Command { // Unrestricts the user on the database await unRestrict(userId, modId); + // 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 From 733a6609a8a4bec25aa614646d10f6cd62449ada Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 21:14:13 +0000 Subject: [PATCH 10/24] fix(arabot): change wording for vegan restrict channel embed --- src/commands/mod/restriction/restrict.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index 1b6526d..87dae8f 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -263,7 +263,7 @@ export class RestrictCommand extends Command { const embed = new EmbedBuilder() .setColor(member.displayHexColor) - .setTitle(`Private channel for ${member.user.username}`) + .setTitle(`Restricted channel for ${member.user.username}`) .setDescription(`${member}`) .setThumbnail(member.user.avatarURL()!) .addFields( From a79cc496d851c4f2a8900b266d6c7f0a7b305933 Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 21:23:02 +0000 Subject: [PATCH 11/24] refactor(arabot): make restrictRun exported function --- src/commands/mod/restriction/restrict.ts | 415 ++++++++++++----------- 1 file changed, 215 insertions(+), 200 deletions(-) diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index 87dae8f..b207f7c 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -17,7 +17,12 @@ along with this program. If not, see . */ -import { Args, Command, RegisterBehavior } from '@sapphire/framework'; +import { + Args, + Command, + RegisterBehavior, + container, +} from '@sapphire/framework'; import { ChannelType, EmbedBuilder, @@ -35,6 +40,213 @@ import IDs from '#utils/ids'; import { addEmptyUser, updateUser, userExists } 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); + } + + if (member !== undefined) { + // Checks if the user is not restricted + if (member.roles.cache.hasAny( + IDs.roles.restrictions.restricted1, + IDs.roles.restrictions.restricted2, + IDs.roles.restrictions.restricted3, + IDs.roles.restrictions.restricted4, + )) { + 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)) { + await member.roles.add(IDs.roles.restrictions.restricted1); + + 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] }); + } else if (tolerance) { + await member.roles.add(Math.random() > 0.5 + ? IDs.roles.restrictions.restricted3 + : IDs.roles.restrictions.restricted4); + } else { + await member.roles.add(Math.random() > 0.5 + ? IDs.roles.restrictions.restricted1 + : IDs.roles.restrictions.restricted2); + } + + 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)) { + await addEmptyUser(userId); + } + + // Restrict the user on the database + await restrict(userId, modId, reason); + + 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] }); + + info.message = `Restricted ${member}`; + return info; +} + export class RestrictCommand extends Command { public constructor(context: Command.Context, options: Command.Options) { super(context, { @@ -82,7 +294,7 @@ export class RestrictCommand extends Command { return; } - const info = await this.restrictRun(user?.id, mod.user.id, reason, guild); + const info = await restrictRun(user?.id, mod.user.id, reason, guild); await interaction.reply({ content: info.message, @@ -124,206 +336,9 @@ export class RestrictCommand extends Command { return; } - const info = await this.restrictRun(user?.id, mod.user.id, reason, guild); + const info = await restrictRun(user?.id, mod.user.id, reason, guild); await message.reply(info.message); await message.react(info.success ? '✅' : '❌'); } - - private async restrictRun(userId: Snowflake, modId: Snowflake, reason: string, 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 - 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); - } - - if (member !== undefined) { - // Checks if the user is not restricted - if (member.roles.cache.hasAny( - IDs.roles.restrictions.restricted1, - IDs.roles.restrictions.restricted2, - IDs.roles.restrictions.restricted3, - IDs.roles.restrictions.restricted4, - )) { - 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)) { - await member.roles.add(IDs.roles.restrictions.restricted1); - - 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] }); - } else { - await member.roles.add(Math.random() > 0.5 - ? IDs.roles.restrictions.restricted1 - : IDs.roles.restrictions.restricted2); - } - - 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)) { - await addEmptyUser(userId); - } - - // Restrict the user on the database - await restrict(userId, modId, reason); - - 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 = `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] }); - - info.message = `Restricted ${member}`; - return info; - } } From 51678a570117d42e5d5878d702e13c86f2371f4b Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 21:23:21 +0000 Subject: [PATCH 12/24] feat(arabot): add restrict tolerance command --- .../mod/restriction/restrictTolerance.ts | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/commands/mod/restriction/restrictTolerance.ts 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 ? '✅' : '❌'); + } +} From de1d800341582894dfdecdfd7ddd970f378b7cbc Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 23:02:30 +0000 Subject: [PATCH 13/24] feat(arabot): log to database the roles a user had when leaving --- src/listeners/dbLeaveServer.ts | 43 ++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/listeners/dbLeaveServer.ts diff --git a/src/listeners/dbLeaveServer.ts b/src/listeners/dbLeaveServer.ts new file mode 100644 index 0000000..fece687 --- /dev/null +++ b/src/listeners/dbLeaveServer.ts @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +/* + Animal Rights Advocates Discord Bot + Copyright (C) 2022 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 { Listener } from '@sapphire/framework'; +import type { GuildMember } from 'discord.js'; +import IDs from '#utils/ids'; +import { updateUser } from '#utils/database/dbExistingUser'; + +export class DbLeaveServerListener extends Listener { + public constructor(context: Listener.Context, options: Listener.Options) { + super(context, { + ...options, + event: 'guildMemberRemove', + }); + } + + public async run(member: GuildMember) { + if (!member.roles.cache.hasAny( + IDs.roles.vegan.vegan, + IDs.roles.nonvegan.nonvegan, + )) { + return; + } + + await updateUser(member); + } +} From a259d8a06744738e86471524c56741847a2b8347 Mon Sep 17 00:00:00 2001 From: smyalygames Date: Sat, 11 Feb 2023 23:02:45 +0000 Subject: [PATCH 14/24] feat(arabot): add roles when user joins server --- .../joinServer.ts => rolesJoinServer.ts} | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) rename src/listeners/{verification/joinServer.ts => rolesJoinServer.ts} (66%) diff --git a/src/listeners/verification/joinServer.ts b/src/listeners/rolesJoinServer.ts similarity index 66% rename from src/listeners/verification/joinServer.ts rename to src/listeners/rolesJoinServer.ts index d9ae243..8448b67 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 { fetchRoles } from '#utils/database/dbExistingUser'; import IDs from '#utils/ids'; import { blockTime } from '#utils/database/verification'; +import { checkActive } 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,29 @@ 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)) { + if (roles.includes(IDs.roles.vegan.vegan)) { + roles.length = 0; + roles.push(IDs.roles.restrictions.restricted1); + } else { + roles.length = 0; + // TODO add role push for restricted roles + } + } // 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); } } From b57a25454bb4b6dabad7d05740bada41e1462e5f Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 00:01:58 +0000 Subject: [PATCH 15/24] feat(db): add restrict section to restrict table --- .../20230214000132_restrict_section/migration.sql | 8 ++++++++ prisma/schema.prisma | 1 + 2 files changed, 9 insertions(+) create mode 100644 prisma/migrations/20230214000132_restrict_section/migration.sql diff --git a/prisma/migrations/20230214000132_restrict_section/migration.sql b/prisma/migrations/20230214000132_restrict_section/migration.sql new file mode 100644 index 0000000..5c80fad --- /dev/null +++ b/prisma/migrations/20230214000132_restrict_section/migration.sql @@ -0,0 +1,8 @@ +/* + Warnings: + + - Added the required column `section` to the `Restrict` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Restrict" ADD COLUMN "section" INTEGER NOT NULL; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 9566290..d42e3d0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -102,6 +102,7 @@ model Restrict { endModId String? endTime DateTime? reason String + section Int } model Ban { From 383e644318207a1a12e9e650c750a1f7b169a8dc Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 00:59:19 +0000 Subject: [PATCH 16/24] refactor(db): change variable naming convention and types --- src/utils/database/dbExistingUser.ts | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) 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, From 3632aaa27620fb476bbd0ac49cc1c7ccffbfff58 Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 01:07:11 +0000 Subject: [PATCH 17/24] feat(arabot): create random integer function --- src/utils/random.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/utils/random.ts 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; +} From 9e833b87fde0df1108adcebde24ee95a14b91ab2 Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 01:07:32 +0000 Subject: [PATCH 18/24] feat(arabot): add unrestrict legacy restrictions to database --- src/utils/database/restriction.ts | 34 +++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/src/utils/database/restriction.ts b/src/utils/database/restriction.ts index a86deb0..e7c37ff 100644 --- a/src/utils/database/restriction.ts +++ b/src/utils/database/restriction.ts @@ -1,7 +1,12 @@ import { container } from '@sapphire/framework'; import type { Snowflake } from 'discord.js'; -export async function restrict(userId: Snowflake, modId: Snowflake, reason: string) { +export async function restrict( + userId: Snowflake, + modId: Snowflake, + reason: string, + section: number, +) { // Add the user to the database await container.database.restrict.create({ data: { @@ -16,6 +21,7 @@ export async function restrict(userId: Snowflake, modId: Snowflake, reason: stri }, }, reason, + section, }, }); } @@ -37,7 +43,6 @@ export async function unRestrict(userId: Snowflake, modId: Snowflake) { return; } - // Query to deactivate the specific sus note await container.database.restrict.update({ where: { id: restriction.id, @@ -72,3 +77,28 @@ export async function checkActive(userId: Snowflake) { return restriction.endTime === null; } + +// 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, + }, + }); +} From e544fa2eae82532e275be7121c659340b3ff088c Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 01:08:10 +0000 Subject: [PATCH 19/24] feat(arabot): add selecting a restricted section to database --- src/commands/mod/restriction/restrict.ts | 47 +++++++++++++++--------- 1 file changed, 29 insertions(+), 18 deletions(-) diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index b207f7c..b7b6cab 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -37,7 +37,13 @@ import type { Snowflake, } from 'discord.js'; import IDs from '#utils/ids'; -import { addEmptyUser, updateUser, userExists } from '#utils/database/dbExistingUser'; +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( @@ -76,14 +82,19 @@ export async function restrictRun( member = await guild.members.fetch(userId); } + const restrictRoles = [ + IDs.roles.restrictions.restricted1, + IDs.roles.restrictions.restricted2, + IDs.roles.restrictions.restricted3, + IDs.roles.restrictions.restricted4, + IDs.roles.restrictions.restricted1, // Vegan Restrict + ]; + + let section = tolerance ? randint(3, 4) : randint(1, 2); + if (member !== undefined) { // Checks if the user is not restricted - if (member.roles.cache.hasAny( - IDs.roles.restrictions.restricted1, - IDs.roles.restrictions.restricted2, - IDs.roles.restrictions.restricted3, - IDs.roles.restrictions.restricted4, - )) { + if (member.roles.cache.hasAny(...restrictRoles)) { info.message = `${member} is already restricted!`; return info; } @@ -92,8 +103,12 @@ export async function restrictRun( await updateUser(member); if (member.roles.cache.has(IDs.roles.vegan.vegan)) { - await member.roles.add(IDs.roles.restrictions.restricted1); + 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, @@ -188,14 +203,6 @@ export async function restrictRun( ); await restrictedChannel.send({ embeds: [embed] }); - } else if (tolerance) { - await member.roles.add(Math.random() > 0.5 - ? IDs.roles.restrictions.restricted3 - : IDs.roles.restrictions.restricted4); - } else { - await member.roles.add(Math.random() > 0.5 - ? IDs.roles.restrictions.restricted1 - : IDs.roles.restrictions.restricted2); } await member.roles.remove([ @@ -209,11 +216,16 @@ export async function restrictRun( ]); } else if (!await userExists(userId)) { 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); + await restrict(userId, modId, reason, section); + info.message = `Restricted ${member}`; info.success = true; // Log the ban @@ -243,7 +255,6 @@ export async function restrictRun( await logChannel.send({ embeds: [message] }); - info.message = `Restricted ${member}`; return info; } From eba68015dbbec73ec9c2c955a3cb878e136b82fd Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 01:20:08 +0000 Subject: [PATCH 20/24] feat(arabot): add database logs for legacy restrictions --- src/commands/mod/restriction/unrestrict.ts | 31 +++++++++++++--------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/src/commands/mod/restriction/unrestrict.ts b/src/commands/mod/restriction/unrestrict.ts index 18a6ee8..9425707 100644 --- a/src/commands/mod/restriction/unrestrict.ts +++ b/src/commands/mod/restriction/unrestrict.ts @@ -28,7 +28,7 @@ import type { } from 'discord.js'; import IDs from '#utils/ids'; import { fetchRoles, addExistingUser, userExists } from '#utils/database/dbExistingUser'; -import { unRestrict, checkActive } from '#utils/database/restriction'; +import { unRestrict, checkActive, unRestrictLegacy } from '#utils/database/restriction'; export class UnRestrictCommand extends Command { public constructor(context: Command.Context, options: Command.Options) { @@ -147,13 +147,16 @@ export class UnRestrictCommand extends Command { return info; } - // Checks if the user is not restricted - if (!member.roles.cache.hasAny( + const restrictRoles = [ IDs.roles.restrictions.restricted1, IDs.roles.restrictions.restricted2, IDs.roles.restrictions.restricted3, IDs.roles.restrictions.restricted4, - )) { + IDs.roles.restrictions.restricted1, // Vegan restricted + ]; + + // Checks if the user is not restricted + if (!member.roles.cache.hasAny(...restrictRoles)) { info.message = `${member} is not restricted!`; return info; } @@ -161,19 +164,21 @@ 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 += 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([ - IDs.roles.restrictions.restricted1, - IDs.roles.restrictions.restricted2, - IDs.roles.restrictions.restricted3, - IDs.roles.restrictions.restricted4, - ]); - - // Unrestricts the user on the database - await unRestrict(userId, modId); + await member.roles.remove(restrictRoles); // Remove vegan restrict channels if (member.roles.cache.has(IDs.roles.vegan.vegan)) { From c60ba056ffa6d7d5932e9a02c6c416dee40ecfe3 Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 01:29:20 +0000 Subject: [PATCH 21/24] feat(arabot): add an array for restricted ids --- src/utils/devIDs.ts | 7 +++++++ src/utils/ids.ts | 7 +++++++ 2 files changed, 14 insertions(+) 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', From 7d54976cfd07e18f9c14fec2f25e3837a69f3cbb Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 01:30:13 +0000 Subject: [PATCH 22/24] feat(arabot): add specific restrict role on joining server --- src/listeners/rolesJoinServer.ts | 12 ++++-------- src/utils/database/restriction.ts | 20 ++++++++++++++++++++ 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/listeners/rolesJoinServer.ts b/src/listeners/rolesJoinServer.ts index 8448b67..0c2871c 100644 --- a/src/listeners/rolesJoinServer.ts +++ b/src/listeners/rolesJoinServer.ts @@ -22,7 +22,7 @@ import type { GuildMember } from 'discord.js'; import { fetchRoles } from '#utils/database/dbExistingUser'; import IDs from '#utils/ids'; import { blockTime } from '#utils/database/verification'; -import { checkActive } from '#utils/database/restriction'; +import { checkActive, getSection } from '#utils/database/restriction'; export class RolesJoinServerListener extends Listener { public constructor(context: Listener.Context, options: Listener.Options) { @@ -39,13 +39,9 @@ export class RolesJoinServerListener extends Listener { // Check if the user is restricted if (await checkActive(member.id)) { - if (roles.includes(IDs.roles.vegan.vegan)) { - roles.length = 0; - roles.push(IDs.roles.restrictions.restricted1); - } else { - roles.length = 0; - // TODO add role push for restricted roles - } + 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 diff --git a/src/utils/database/restriction.ts b/src/utils/database/restriction.ts index e7c37ff..5117401 100644 --- a/src/utils/database/restriction.ts +++ b/src/utils/database/restriction.ts @@ -78,6 +78,26 @@ export async function checkActive(userId: Snowflake) { 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({ From 71cc4d461cecc98b209a3e99160d5ad97be7f392 Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 01:31:11 +0000 Subject: [PATCH 23/24] refactor(arabot): change restricted roles array to array from IDs --- src/commands/mod/restriction/restrict.ts | 8 +------- src/commands/mod/restriction/unrestrict.ts | 8 +------- 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index b7b6cab..87ffd0e 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -82,13 +82,7 @@ export async function restrictRun( member = await guild.members.fetch(userId); } - const restrictRoles = [ - IDs.roles.restrictions.restricted1, - IDs.roles.restrictions.restricted2, - IDs.roles.restrictions.restricted3, - IDs.roles.restrictions.restricted4, - IDs.roles.restrictions.restricted1, // Vegan Restrict - ]; + const restrictRoles = IDs.roles.restrictions.restricted; let section = tolerance ? randint(3, 4) : randint(1, 2); diff --git a/src/commands/mod/restriction/unrestrict.ts b/src/commands/mod/restriction/unrestrict.ts index 9425707..ac4831f 100644 --- a/src/commands/mod/restriction/unrestrict.ts +++ b/src/commands/mod/restriction/unrestrict.ts @@ -147,13 +147,7 @@ export class UnRestrictCommand extends Command { return info; } - const restrictRoles = [ - IDs.roles.restrictions.restricted1, - IDs.roles.restrictions.restricted2, - IDs.roles.restrictions.restricted3, - IDs.roles.restrictions.restricted4, - IDs.roles.restrictions.restricted1, // Vegan restricted - ]; + const restrictRoles = IDs.roles.restrictions.restricted; // Checks if the user is not restricted if (!member.roles.cache.hasAny(...restrictRoles)) { From dd008c1ea55215da9cd6fa37dcfeb6d8e80cb66f Mon Sep 17 00:00:00 2001 From: smyalygames Date: Tue, 14 Feb 2023 14:26:52 +0000 Subject: [PATCH 24/24] feat(arabot): remove vegan restricts and restrict after left --- src/commands/mod/restriction/restrict.ts | 8 +++++++- src/listeners/rolesJoinServer.ts | 8 +++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/commands/mod/restriction/restrict.ts b/src/commands/mod/restriction/restrict.ts index 87ffd0e..f33b34c 100644 --- a/src/commands/mod/restriction/restrict.ts +++ b/src/commands/mod/restriction/restrict.ts @@ -97,6 +97,9 @@ export async function restrictRun( 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; } @@ -209,6 +212,9 @@ export async function restrictRun( 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)) { @@ -257,7 +263,7 @@ export class RestrictCommand extends Command { super(context, { ...options, name: 'restrict', - aliases: ['r', 'rest', 'rr', 'rv'], + aliases: ['r', 'rest', 'rr'], // TODO add 'rv' when enabling vegan restrictions description: 'Restricts a user', preconditions: ['ModOnly'], }); diff --git a/src/listeners/rolesJoinServer.ts b/src/listeners/rolesJoinServer.ts index 0c2871c..fa30a45 100644 --- a/src/listeners/rolesJoinServer.ts +++ b/src/listeners/rolesJoinServer.ts @@ -20,9 +20,9 @@ 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 { checkActive, getSection } from '#utils/database/restriction'; +// import IDs from '#utils/ids'; +// import { blockTime } from '#utils/database/verification'; +// import { checkActive, getSection } from '#utils/database/restriction'; export class RolesJoinServerListener extends Listener { public constructor(context: Listener.Context, options: Listener.Options) { @@ -37,6 +37,7 @@ export class RolesJoinServerListener extends Listener { const roles = await fetchRoles(member.id); + /* // Check if the user is restricted if (await checkActive(member.id)) { const section = await getSection(member.id); @@ -49,6 +50,7 @@ export class RolesJoinServerListener extends Listener { if (timeout > 0) { roles.push(IDs.roles.verifyBlock); } + */ // Add roles if they don't have verification block await member.roles.add(roles);