mirror of
https://github.com/veganhacktivists/arabot.git
synced 2025-05-18 16:54:16 +02:00
Merge pull request #173 from veganhacktivists/feat/warnings
feat(arabot): add warning commands
This commit is contained in:
commit
d00fddd51a
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `active` on the `Warning` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE "Warning" DROP COLUMN "active";
|
@ -256,7 +256,6 @@ model Warning {
|
|||||||
mod User @relation("warnMod", fields: [modId], references: [id])
|
mod User @relation("warnMod", fields: [modId], references: [id])
|
||||||
modId String
|
modId String
|
||||||
time DateTime @default(now())
|
time DateTime @default(now())
|
||||||
active Boolean @default(true)
|
|
||||||
note String
|
note String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,7 +29,7 @@ export class RestrictLogsCommand extends Command {
|
|||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
name: 'restrictlogs',
|
name: 'restrictlogs',
|
||||||
description: 'Unrestricts a user',
|
description: 'Shows restriction history for a user',
|
||||||
preconditions: ['ModOnly'],
|
preconditions: ['ModOnly'],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -75,10 +75,9 @@ export class RestrictLogsCommand extends Command {
|
|||||||
userId = user.id;
|
userId = user.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
let staffChannel = false;
|
const staffChannel = checkStaff(channel);
|
||||||
if (channel.type === ChannelType.GuildText) {
|
if (staffChannel) {
|
||||||
channel = channel as TextChannel;
|
channel = channel as TextChannel;
|
||||||
staffChannel = checkStaff(channel);
|
|
||||||
|
|
||||||
if (userId === null) {
|
if (userId === null) {
|
||||||
let topic: string[];
|
let topic: string[];
|
||||||
|
@ -20,14 +20,13 @@
|
|||||||
import { RegisterBehavior, Args } from '@sapphire/framework';
|
import { RegisterBehavior, Args } from '@sapphire/framework';
|
||||||
import { Subcommand } from '@sapphire/plugin-subcommands';
|
import { Subcommand } from '@sapphire/plugin-subcommands';
|
||||||
import {
|
import {
|
||||||
ChannelType,
|
|
||||||
EmbedBuilder,
|
EmbedBuilder,
|
||||||
ActionRowBuilder,
|
ActionRowBuilder,
|
||||||
ButtonBuilder,
|
ButtonBuilder,
|
||||||
ButtonInteraction,
|
ButtonInteraction,
|
||||||
ButtonStyle,
|
ButtonStyle,
|
||||||
} from 'discord.js';
|
} from 'discord.js';
|
||||||
import type { Message, GuildMember, TextChannel } from 'discord.js';
|
import type { Message, GuildMember } from 'discord.js';
|
||||||
import { isMessageInstance } from '@sapphire/discord.js-utilities';
|
import { isMessageInstance } from '@sapphire/discord.js-utilities';
|
||||||
import { addExistingUser } from '#utils/database/dbExistingUser';
|
import { addExistingUser } from '#utils/database/dbExistingUser';
|
||||||
import {
|
import {
|
||||||
@ -39,11 +38,15 @@ import {
|
|||||||
} from '#utils/database/sus';
|
} from '#utils/database/sus';
|
||||||
import { checkStaff } from '#utils/checker';
|
import { checkStaff } from '#utils/checker';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
import { createSusLogEmbed } from '#utils/embeds';
|
||||||
|
|
||||||
// TODO add a check when they join the server to give the user the sus role again
|
// TODO add a check when they join the server to give the user the sus role again
|
||||||
|
|
||||||
export class SusCommand extends Subcommand {
|
export class SusCommand extends Subcommand {
|
||||||
public constructor(context: Subcommand.LoaderContext, options: Subcommand.Options) {
|
public constructor(
|
||||||
|
context: Subcommand.LoaderContext,
|
||||||
|
options: Subcommand.Options,
|
||||||
|
) {
|
||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
name: 'sus',
|
name: 'sus',
|
||||||
@ -201,14 +204,7 @@ export class SusCommand extends Subcommand {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let staffChannel = false;
|
const staffChannel = checkStaff(interaction.channel);
|
||||||
let { channel } = interaction;
|
|
||||||
if (channel !== null) {
|
|
||||||
if (channel.type === ChannelType.GuildText) {
|
|
||||||
channel = channel as TextChannel;
|
|
||||||
staffChannel = checkStaff(channel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets the sus notes from the database
|
// Gets the sus notes from the database
|
||||||
const notes = await findNotes(user.id, true);
|
const notes = await findNotes(user.id, true);
|
||||||
@ -224,34 +220,7 @@ export class SusCommand extends Subcommand {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Creates the embed to display the sus note
|
// Creates the embed to display the sus note
|
||||||
const noteEmbed = new EmbedBuilder()
|
const noteEmbed = createSusLogEmbed(notes, user, guild);
|
||||||
.setColor('#0099ff')
|
|
||||||
.setTitle(`${notes.length} sus notes for ${user.username}`)
|
|
||||||
.setThumbnail(user.displayAvatarURL());
|
|
||||||
|
|
||||||
// Add up to 10 of the latest sus notes to the embed
|
|
||||||
for (
|
|
||||||
let i = notes.length > 10 ? notes.length - 10 : 0;
|
|
||||||
i < notes.length;
|
|
||||||
i += 1
|
|
||||||
) {
|
|
||||||
// Get mod name
|
|
||||||
let mod = notes[i].modId;
|
|
||||||
const modMember = guild.members.cache.get(mod);
|
|
||||||
if (modMember !== undefined) {
|
|
||||||
mod = modMember.displayName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add sus note to embed
|
|
||||||
noteEmbed.addFields({
|
|
||||||
name: `Sus ID: ${
|
|
||||||
notes[i].id
|
|
||||||
} | Moderator: ${mod} | Date: <t:${Math.floor(
|
|
||||||
notes[i].time.getTime() / 1000,
|
|
||||||
)}>`,
|
|
||||||
value: notes[i].note,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sends the notes to the user
|
// Sends the notes to the user
|
||||||
await interaction.reply({
|
await interaction.reply({
|
||||||
@ -554,11 +523,6 @@ export class SusCommand extends Subcommand {
|
|||||||
await user.roles.add(IDs.roles.restrictions.sus);
|
await user.roles.add(IDs.roles.restrictions.sus);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if the user is xlevra to send a very kind message
|
|
||||||
if (mod.id === '259624904746467329') {
|
|
||||||
await message.reply('Fuck you for making me add this feature 🤬');
|
|
||||||
}
|
|
||||||
|
|
||||||
await message.react('✅');
|
await message.react('✅');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,127 +0,0 @@
|
|||||||
// 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 <https://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Args, Command } from '@sapphire/framework';
|
|
||||||
import type { User, Message, Snowflake, Guild } from 'discord.js';
|
|
||||||
import { addExistingUser, updateUser } from '#utils/database/dbExistingUser';
|
|
||||||
import { addWarn } from '#utils/database/warnings';
|
|
||||||
|
|
||||||
/*
|
|
||||||
This command is not intended to be functional for now, this is purely to log
|
|
||||||
warnings onto a database, so if we were to switch purely to ARA Bot, it would
|
|
||||||
mean we would have a lot of the warns already in the database.
|
|
||||||
*/
|
|
||||||
export class WarnCommand extends Command {
|
|
||||||
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
|
||||||
super(context, {
|
|
||||||
...options,
|
|
||||||
name: 'warn',
|
|
||||||
description: 'Warns a user (only used for logging to a database for now)',
|
|
||||||
preconditions: [['CoordinatorOnly', 'ModOnly']],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
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('Warn 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 warn = await this.warn(user.id, mod.id, reason, guild);
|
|
||||||
|
|
||||||
if (!warn.success) {
|
|
||||||
await message.react('❌');
|
|
||||||
}
|
|
||||||
|
|
||||||
// await message.react('✅');
|
|
||||||
}
|
|
||||||
|
|
||||||
private async warn(
|
|
||||||
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);
|
|
||||||
|
|
||||||
// Gets guildMember
|
|
||||||
let member = guild.members.cache.get(userId);
|
|
||||||
|
|
||||||
if (member === undefined) {
|
|
||||||
member = await guild.members.fetch(userId).catch(() => undefined);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (member === undefined) {
|
|
||||||
info.message = 'User is not on this server';
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
await addExistingUser(member);
|
|
||||||
|
|
||||||
await addWarn(userId, modId, reason);
|
|
||||||
|
|
||||||
info.message = `Warned ${member}`;
|
|
||||||
info.success = true;
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
}
|
|
183
src/commands/mod/warning/deleteWarning.ts
Normal file
183
src/commands/mod/warning/deleteWarning.ts
Normal file
@ -0,0 +1,183 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2024 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
|
import { EmbedBuilder, TextChannel } from 'discord.js';
|
||||||
|
import type { Message, Guild, User } from 'discord.js';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
import { deleteWarning, fetchWarning } from '#utils/database/warnings';
|
||||||
|
import { checkStaff } from '#utils/checker';
|
||||||
|
|
||||||
|
export class DeleteWarningCommand extends Command {
|
||||||
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
name: 'deletewarning',
|
||||||
|
aliases: ['delwarn', 'removewarning'],
|
||||||
|
description: 'Deletes a warning',
|
||||||
|
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)
|
||||||
|
.addIntegerOption((option) =>
|
||||||
|
option
|
||||||
|
.setName('id')
|
||||||
|
.setDescription('ID for the warning')
|
||||||
|
.setRequired(true),
|
||||||
|
),
|
||||||
|
{
|
||||||
|
behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command run
|
||||||
|
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||||
|
// Get the arguments
|
||||||
|
const warningId = interaction.options.getInteger('id', true);
|
||||||
|
const mod = interaction.user;
|
||||||
|
const { guild } = interaction;
|
||||||
|
|
||||||
|
// Checks if all the variables are of the right type
|
||||||
|
if (guild === null) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: 'Error fetching guild!',
|
||||||
|
ephemeral: true,
|
||||||
|
fetchReply: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const staffChannel = checkStaff(interaction.channel);
|
||||||
|
|
||||||
|
await interaction.deferReply({ ephemeral: !staffChannel });
|
||||||
|
|
||||||
|
const info = await this.deleteWarning(warningId, mod, guild);
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
|
content: info.message,
|
||||||
|
embeds: info.embeds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non Application Command method for removing a warning
|
||||||
|
public async messageRun(message: Message, args: Args) {
|
||||||
|
// Get arguments
|
||||||
|
let warningId: number;
|
||||||
|
try {
|
||||||
|
warningId = await args.pick('integer');
|
||||||
|
} catch {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.react('Correct warning ID not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const mod = message.author;
|
||||||
|
|
||||||
|
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.deleteWarning(warningId, mod, guild);
|
||||||
|
|
||||||
|
await message.reply({ content: info.message, embeds: info.embeds });
|
||||||
|
if (!info.success) {
|
||||||
|
await message.react('❌');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async deleteWarning(warningId: number, mod: User, guild: Guild) {
|
||||||
|
const info = {
|
||||||
|
message: '',
|
||||||
|
embeds: [] as EmbedBuilder[],
|
||||||
|
success: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const warning = await fetchWarning(warningId);
|
||||||
|
|
||||||
|
if (warning === null) {
|
||||||
|
info.message = `Warning ID \`${warningId}\` not found!`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
await deleteWarning(warningId);
|
||||||
|
info.success = true;
|
||||||
|
|
||||||
|
const userId = warning.userId;
|
||||||
|
let user = guild.client.users.cache.get(userId);
|
||||||
|
|
||||||
|
if (user === undefined) {
|
||||||
|
user = await guild.client.users.fetch(userId);
|
||||||
|
if (user === undefined) {
|
||||||
|
info.message = `Deleted warning ID \`${warningId}\`, but the user could not be found!`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log the warnings deletion
|
||||||
|
let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as
|
||||||
|
| TextChannel
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
if (logChannel === undefined) {
|
||||||
|
logChannel = (await guild.channels.fetch(IDs.channels.logs.sus)) as
|
||||||
|
| TextChannel
|
||||||
|
| undefined;
|
||||||
|
if (logChannel === undefined) {
|
||||||
|
this.container.logger.error(
|
||||||
|
'Delete Warning Error: Could not fetch log channel',
|
||||||
|
);
|
||||||
|
info.message =
|
||||||
|
`Deleted warning for ${user} (Warning ID: ${warningId} but ` +
|
||||||
|
'could not find the log channel.';
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = new EmbedBuilder()
|
||||||
|
.setColor('#28A745')
|
||||||
|
.setAuthor({
|
||||||
|
name: `Removed warning for ${user.tag}`,
|
||||||
|
iconURL: `${user.displayAvatarURL()}`,
|
||||||
|
})
|
||||||
|
.addFields(
|
||||||
|
{ name: 'User', value: `${user}`, inline: true },
|
||||||
|
{ name: 'Moderator', value: `${mod}`, inline: true },
|
||||||
|
{ name: 'Warning ID', value: `${warningId}`, inline: true },
|
||||||
|
)
|
||||||
|
.setTimestamp()
|
||||||
|
.setFooter({ text: `ID: ${userId}` });
|
||||||
|
|
||||||
|
await logChannel.send({ embeds: [message] });
|
||||||
|
|
||||||
|
info.message = `Deleted warning for ${user}`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
226
src/commands/mod/warning/warn.ts
Normal file
226
src/commands/mod/warning/warn.ts
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2023, 2024 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {
|
||||||
|
Args,
|
||||||
|
Command,
|
||||||
|
container,
|
||||||
|
RegisterBehavior,
|
||||||
|
} from '@sapphire/framework';
|
||||||
|
import type { User, Message, Snowflake, Guild, TextChannel } from 'discord.js';
|
||||||
|
import { updateUser } from '#utils/database/dbExistingUser';
|
||||||
|
import { addWarn } from '#utils/database/warnings';
|
||||||
|
import { EmbedBuilder } from 'discord.js';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
|
||||||
|
export class WarnCommand extends Command {
|
||||||
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
name: 'warn',
|
||||||
|
description: 'Warns a user',
|
||||||
|
preconditions: [['CoordinatorOnly', '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 warn')
|
||||||
|
.setRequired(true),
|
||||||
|
)
|
||||||
|
.addStringOption((option) =>
|
||||||
|
option
|
||||||
|
.setName('reason')
|
||||||
|
.setDescription('Reason for the warning')
|
||||||
|
.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.user;
|
||||||
|
const { guild } = interaction;
|
||||||
|
|
||||||
|
// Checks if all the variables are of the right type
|
||||||
|
if (guild === null) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: 'Error fetching guild!',
|
||||||
|
ephemeral: true,
|
||||||
|
fetchReply: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await interaction.deferReply();
|
||||||
|
|
||||||
|
const info = await this.warn(user.id, mod.id, reason, guild);
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
|
content: info.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non Application Command method for warning 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('Warn 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 warn = await this.warn(user.id, mod.id, reason, guild);
|
||||||
|
|
||||||
|
if (!warn.success) {
|
||||||
|
await message.react('❌');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await message.react('✅');
|
||||||
|
}
|
||||||
|
|
||||||
|
private async warn(
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Gets User for person being restricted
|
||||||
|
let user = guild.client.users.cache.get(userId);
|
||||||
|
|
||||||
|
if (user === undefined) {
|
||||||
|
user = await guild.client.users.fetch(userId);
|
||||||
|
if (user === undefined) {
|
||||||
|
info.message = 'Error fetching user';
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await addWarn(userId, modId, reason);
|
||||||
|
|
||||||
|
info.message = `Warned ${user}`;
|
||||||
|
info.success = true;
|
||||||
|
|
||||||
|
// DM the reason
|
||||||
|
|
||||||
|
const dmEmbed = new EmbedBuilder()
|
||||||
|
.setColor('#FF6700')
|
||||||
|
.setAuthor({
|
||||||
|
name: "You've been warned!",
|
||||||
|
iconURL: `${user.displayAvatarURL()}`,
|
||||||
|
})
|
||||||
|
.addFields({ name: 'Reason', value: reason })
|
||||||
|
.setTimestamp();
|
||||||
|
|
||||||
|
await user.send({ embeds: [dmEmbed] }).catch(() => {});
|
||||||
|
|
||||||
|
// Log the ban
|
||||||
|
let logChannel = guild.channels.cache.get(IDs.channels.logs.sus) as
|
||||||
|
| TextChannel
|
||||||
|
| undefined;
|
||||||
|
|
||||||
|
if (logChannel === undefined) {
|
||||||
|
logChannel = (await guild.channels.fetch(IDs.channels.logs.sus)) as
|
||||||
|
| TextChannel
|
||||||
|
| undefined;
|
||||||
|
if (logChannel === undefined) {
|
||||||
|
container.logger.error('Warn Error: Could not fetch log channel');
|
||||||
|
info.message = `Warned ${user} but could not find the log channel. This has been logged to the database.`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const message = new EmbedBuilder()
|
||||||
|
.setColor('#FF6700')
|
||||||
|
.setAuthor({
|
||||||
|
name: `Warned ${user.tag}`,
|
||||||
|
iconURL: `${user.displayAvatarURL()}`,
|
||||||
|
})
|
||||||
|
.addFields(
|
||||||
|
{ name: 'User', value: `${user}`, inline: true },
|
||||||
|
{ name: 'Moderator', value: `${mod}`, inline: true },
|
||||||
|
{ name: 'Reason', value: reason },
|
||||||
|
)
|
||||||
|
.setTimestamp()
|
||||||
|
.setFooter({ text: `ID: ${userId}` });
|
||||||
|
|
||||||
|
await logChannel.send({ embeds: [message] });
|
||||||
|
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
161
src/commands/mod/warning/warnings.ts
Normal file
161
src/commands/mod/warning/warnings.ts
Normal file
@ -0,0 +1,161 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2024 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { Args, Command, RegisterBehavior } from '@sapphire/framework';
|
||||||
|
import { ChannelType, EmbedBuilder } from 'discord.js';
|
||||||
|
import type { Message, Guild, User } from 'discord.js';
|
||||||
|
import IDs from '#utils/ids';
|
||||||
|
import { fetchWarnings } from '#utils/database/warnings';
|
||||||
|
import { checkStaff } from '#utils/checker';
|
||||||
|
import { createWarningsEmbed } from '#utils/embeds';
|
||||||
|
|
||||||
|
export class WarningsCommand extends Command {
|
||||||
|
public constructor(context: Command.LoaderContext, options: Command.Options) {
|
||||||
|
super(context, {
|
||||||
|
...options,
|
||||||
|
name: 'warnings',
|
||||||
|
aliases: ['warninglog', 'warnlog'],
|
||||||
|
description: 'Shows all the warnings for the 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 check the warnings for')
|
||||||
|
.setRequired(true),
|
||||||
|
),
|
||||||
|
{
|
||||||
|
behaviorWhenNotIdentical: RegisterBehavior.Overwrite,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Command run
|
||||||
|
public async chatInputRun(interaction: Command.ChatInputCommandInteraction) {
|
||||||
|
// Get the arguments
|
||||||
|
const user = interaction.options.getUser('user', true);
|
||||||
|
const { guild } = interaction;
|
||||||
|
|
||||||
|
// Checks if all the variables are of the right type
|
||||||
|
if (guild === null) {
|
||||||
|
await interaction.reply({
|
||||||
|
content: 'Error fetching guild!',
|
||||||
|
ephemeral: true,
|
||||||
|
fetchReply: true,
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const staffChannel = checkStaff(interaction.channel);
|
||||||
|
|
||||||
|
await interaction.deferReply({ ephemeral: !staffChannel });
|
||||||
|
|
||||||
|
const info = await this.warnings(user, guild);
|
||||||
|
|
||||||
|
await interaction.editReply({
|
||||||
|
content: info.message,
|
||||||
|
embeds: info.embeds,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Non Application Command method for fetching warnings
|
||||||
|
public async messageRun(message: Message, args: Args) {
|
||||||
|
// Get arguments
|
||||||
|
let user: User | undefined;
|
||||||
|
try {
|
||||||
|
user = await args.pick('user');
|
||||||
|
} catch {
|
||||||
|
user = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { guild } = message;
|
||||||
|
|
||||||
|
if (guild === null) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('Guild not found! Try again or contact a developer!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user === undefined) {
|
||||||
|
const { channel } = message;
|
||||||
|
|
||||||
|
if (channel.type !== ChannelType.GuildText) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('User was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let topic: string[];
|
||||||
|
|
||||||
|
if (channel.parentId === IDs.categories.modMail) {
|
||||||
|
// Checks if the channel topic has the user's snowflake
|
||||||
|
if (channel.topic !== null) {
|
||||||
|
topic = channel.topic.split(' ');
|
||||||
|
// eslint-disable-next-line prefer-destructuring
|
||||||
|
const userId = topic[2];
|
||||||
|
|
||||||
|
user = guild.client.users.cache.get(userId);
|
||||||
|
|
||||||
|
if (user === undefined) {
|
||||||
|
user = await guild.client.users.fetch(userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user === undefined) {
|
||||||
|
await message.react('❌');
|
||||||
|
await message.reply('User was not provided!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const info = await this.warnings(user, guild);
|
||||||
|
|
||||||
|
await message.reply({ content: info.message, embeds: info.embeds });
|
||||||
|
}
|
||||||
|
|
||||||
|
private async warnings(user: User, guild: Guild) {
|
||||||
|
const info = {
|
||||||
|
message: '',
|
||||||
|
embeds: [] as EmbedBuilder[],
|
||||||
|
};
|
||||||
|
|
||||||
|
const warnings = await fetchWarnings(user.id);
|
||||||
|
|
||||||
|
if (warnings.length === 0) {
|
||||||
|
info.message = `${user} user has no warnings.`;
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Creates an embed to display the warnings
|
||||||
|
const embed = createWarningsEmbed(warnings, user, guild);
|
||||||
|
|
||||||
|
info.embeds.push(embed);
|
||||||
|
return info;
|
||||||
|
}
|
||||||
|
}
|
@ -18,15 +18,24 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Listener } from '@sapphire/framework';
|
import { Listener } from '@sapphire/framework';
|
||||||
import { ChannelType, EmbedBuilder } from 'discord.js';
|
import { ChannelType } from 'discord.js';
|
||||||
import type { GuildChannel } from 'discord.js';
|
import type { GuildChannel, EmbedBuilder } from 'discord.js';
|
||||||
import { setTimeout } from 'timers/promises';
|
import { setTimeout } from 'timers/promises';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
import { checkActive, getRestrictions } from '#utils/database/restriction';
|
import { checkActive, getRestrictions } from '#utils/database/restriction';
|
||||||
import { findNotes } from '#utils/database/sus';
|
import { findNotes } from '#utils/database/sus';
|
||||||
|
import {
|
||||||
|
createRestrictLogEmbed,
|
||||||
|
createSusLogEmbed,
|
||||||
|
createWarningsEmbed,
|
||||||
|
} from '#utils/embeds';
|
||||||
|
import { fetchWarnings } from '#utils/database/warnings';
|
||||||
|
|
||||||
export class ModMailCreateListener extends Listener {
|
export class ModMailCreateListener extends Listener {
|
||||||
public constructor(context: Listener.LoaderContext, options: Listener.Options) {
|
public constructor(
|
||||||
|
context: Listener.LoaderContext,
|
||||||
|
options: Listener.Options,
|
||||||
|
) {
|
||||||
super(context, {
|
super(context, {
|
||||||
...options,
|
...options,
|
||||||
event: 'channelCreate',
|
event: 'channelCreate',
|
||||||
@ -51,6 +60,16 @@ export class ModMailCreateListener extends Listener {
|
|||||||
// Get the user's ID
|
// Get the user's ID
|
||||||
const userId = topic[2];
|
const userId = topic[2];
|
||||||
|
|
||||||
|
// Gets user who created ModMail
|
||||||
|
let user = guild.client.users.cache.get(userId);
|
||||||
|
|
||||||
|
if (user === undefined) {
|
||||||
|
user = await guild.client.users.fetch(userId);
|
||||||
|
if (user === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the user is currently restricted on the database
|
// Check if the user is currently restricted on the database
|
||||||
if (!(await checkActive(userId))) return;
|
if (!(await checkActive(userId))) return;
|
||||||
|
|
||||||
@ -60,81 +79,21 @@ export class ModMailCreateListener extends Listener {
|
|||||||
|
|
||||||
// Creation of embeds
|
// Creation of embeds
|
||||||
// Restriction Logs
|
// Restriction Logs
|
||||||
const restrictEmbed = new EmbedBuilder()
|
const embeds: EmbedBuilder[] = [];
|
||||||
.setColor('#FF6700')
|
embeds.push(createRestrictLogEmbed(restrictions, user, guild));
|
||||||
.setTitle(`${restrictions.length} restrictions`)
|
|
||||||
.setFooter({ text: `ID: ${userId}` });
|
|
||||||
|
|
||||||
// Add up to 10 of the latest restrictions to the embed
|
// Warnings
|
||||||
for (
|
const warnings = await fetchWarnings(userId);
|
||||||
let i = restrictions.length > 10 ? restrictions.length - 10 : 0;
|
|
||||||
i < restrictions.length;
|
|
||||||
i += 1
|
|
||||||
) {
|
|
||||||
// Get mod names
|
|
||||||
let restMod = restrictions[i].modId;
|
|
||||||
const restModMember = guild.members.cache.get(restMod);
|
|
||||||
if (restModMember !== undefined) {
|
|
||||||
restMod = restModMember.displayName;
|
|
||||||
}
|
|
||||||
let endRestMod = restrictions[i].endModId;
|
|
||||||
if (endRestMod !== null) {
|
|
||||||
const endRestModMember = guild.members.cache.get(endRestMod);
|
|
||||||
if (endRestModMember !== undefined) {
|
|
||||||
endRestMod = endRestModMember.displayName;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let restTitle = `Restriction: ${i + 1} | Restricted by: ${restMod} | `;
|
embeds.push(createWarningsEmbed(warnings, user, guild));
|
||||||
|
|
||||||
if (endRestMod !== null) {
|
|
||||||
restTitle += `Unrestricted by: ${endRestMod} | `;
|
|
||||||
} else {
|
|
||||||
restTitle += 'Currently Restricted | ';
|
|
||||||
}
|
|
||||||
|
|
||||||
restTitle += `Date: <t:${Math.floor(
|
|
||||||
restrictions[i].startTime.getTime() / 1000,
|
|
||||||
)}>`;
|
|
||||||
|
|
||||||
restrictEmbed.addFields({
|
|
||||||
name: restTitle,
|
|
||||||
value: restrictions[i].reason,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sus Notes
|
// Sus Notes
|
||||||
const notes = await findNotes(userId, true);
|
const notes = await findNotes(userId, true);
|
||||||
|
|
||||||
const susEmbed = new EmbedBuilder()
|
embeds.push(createSusLogEmbed(notes, user, guild));
|
||||||
.setColor('#0099ff')
|
|
||||||
.setTitle(`${notes.length} sus notes`);
|
|
||||||
|
|
||||||
// Add up to 10 of the latest sus notes to the embed
|
|
||||||
for (
|
|
||||||
let i = notes.length > 10 ? notes.length - 10 : 0;
|
|
||||||
i < notes.length;
|
|
||||||
i += 1
|
|
||||||
) {
|
|
||||||
// Get mod name
|
|
||||||
const modGuildMember = guild.members.cache.get(notes[i].modId);
|
|
||||||
let mod = notes[i].modId;
|
|
||||||
if (modGuildMember !== undefined) {
|
|
||||||
mod = modGuildMember.displayName;
|
|
||||||
}
|
|
||||||
// Add sus note to embed
|
|
||||||
susEmbed.addFields({
|
|
||||||
name: `Sus ID: ${
|
|
||||||
notes[i].id
|
|
||||||
} | Moderator: ${mod} | Date: <t:${Math.floor(
|
|
||||||
notes[i].time.getTime() / 1000,
|
|
||||||
)}>`,
|
|
||||||
value: notes[i].note,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set a timeout for 1 second and then send the 2 embeds
|
// Set a timeout for 1 second and then send the 2 embeds
|
||||||
await setTimeout(1000);
|
await setTimeout(1000);
|
||||||
await channel.send({ embeds: [restrictEmbed, susEmbed] });
|
await channel.send({ embeds: embeds });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,8 +17,9 @@
|
|||||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { TextChannel } from 'discord.js';
|
import type { TextBasedChannel } from 'discord.js';
|
||||||
import IDs from '#utils/ids';
|
import IDs from '#utils/ids';
|
||||||
|
import { ChannelType } from 'discord.js';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the channel is in the staff category.
|
* Checks if the channel is in the staff category.
|
||||||
@ -26,7 +27,15 @@ import IDs from '#utils/ids';
|
|||||||
* @returns {boolean} true if is in staff channel
|
* @returns {boolean} true if is in staff channel
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function checkStaff(channel: TextChannel) {
|
export function checkStaff(channel: TextBasedChannel | null) {
|
||||||
|
if (channel === null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (channel.type !== ChannelType.GuildText) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (channel.parent === null) {
|
if (channel.parent === null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { container } from '@sapphire/framework';
|
import { container } from '@sapphire/framework';
|
||||||
import type { Snowflake } from 'discord.js';
|
import type { Snowflake } from 'discord.js';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
export async function restrict(
|
export async function restrict(
|
||||||
userId: Snowflake,
|
userId: Snowflake,
|
||||||
@ -71,6 +72,8 @@ export async function getRestrictions(userId: Snowflake) {
|
|||||||
return restrictions;
|
return restrictions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RestrictionLogs = Prisma.PromiseReturnType<typeof getRestrictions>;
|
||||||
|
|
||||||
export async function checkActive(userId: Snowflake) {
|
export async function checkActive(userId: Snowflake) {
|
||||||
const restriction = await container.database.restrict.findFirst({
|
const restriction = await container.database.restrict.findFirst({
|
||||||
where: {
|
where: {
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { container } from '@sapphire/framework';
|
import { container } from '@sapphire/framework';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
export async function addToDatabase(
|
export async function addToDatabase(
|
||||||
userId: string,
|
userId: string,
|
||||||
@ -39,6 +40,8 @@ export async function findNotes(userId: string, active: boolean) {
|
|||||||
return note;
|
return note;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type SusNotes = Prisma.PromiseReturnType<typeof findNotes>;
|
||||||
|
|
||||||
// Get one note from the id
|
// Get one note from the id
|
||||||
export async function getNote(noteId: number) {
|
export async function getNote(noteId: number) {
|
||||||
// Query to get the specific user's sus notes
|
// Query to get the specific user's sus notes
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { container } from '@sapphire/framework';
|
import { container } from '@sapphire/framework';
|
||||||
import type { Snowflake } from 'discord.js';
|
import type { Snowflake } from 'discord.js';
|
||||||
|
import { Prisma } from '@prisma/client';
|
||||||
|
|
||||||
export async function addWarn(
|
export async function addWarn(
|
||||||
userId: Snowflake,
|
userId: Snowflake,
|
||||||
@ -9,8 +10,13 @@ export async function addWarn(
|
|||||||
await container.database.warning.create({
|
await container.database.warning.create({
|
||||||
data: {
|
data: {
|
||||||
user: {
|
user: {
|
||||||
connect: {
|
connectOrCreate: {
|
||||||
id: userId,
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
mod: {
|
mod: {
|
||||||
@ -22,3 +28,36 @@ export async function addWarn(
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function fetchWarning(warningId: number) {
|
||||||
|
const warning = await container.database.warning.findUnique({
|
||||||
|
where: {
|
||||||
|
id: warningId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return warning;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function fetchWarnings(userId: Snowflake) {
|
||||||
|
const warnings = await container.database.warning.findMany({
|
||||||
|
where: {
|
||||||
|
userId,
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
id: 'asc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return warnings;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Warnings = Prisma.PromiseReturnType<typeof fetchWarnings>;
|
||||||
|
|
||||||
|
export async function deleteWarning(warningId: number) {
|
||||||
|
await container.database.warning.delete({
|
||||||
|
where: {
|
||||||
|
id: warningId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
@ -122,6 +122,7 @@ const devIDs = {
|
|||||||
logs: {
|
logs: {
|
||||||
restricted: '999431681217937513',
|
restricted: '999431681217937513',
|
||||||
economy: '999431681599623198',
|
economy: '999431681599623198',
|
||||||
|
sus: '999431681599623199',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
categories: {
|
categories: {
|
||||||
|
143
src/utils/embeds.ts
Normal file
143
src/utils/embeds.ts
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
/*
|
||||||
|
Animal Rights Advocates Discord Bot
|
||||||
|
Copyright (C) 2024 Anthony Berg
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
import type { Guild, User } from 'discord.js';
|
||||||
|
import { EmbedBuilder } from 'discord.js';
|
||||||
|
import type { SusNotes } from '#utils/database/sus';
|
||||||
|
import { RestrictionLogs } from '#utils/database/restriction';
|
||||||
|
import { Warnings } from '#utils/database/warnings';
|
||||||
|
|
||||||
|
export function createSusLogEmbed(notes: SusNotes, user: User, guild: Guild) {
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor('#0099ff')
|
||||||
|
.setTitle(`${notes.length} sus notes for ${user.username}`)
|
||||||
|
.setThumbnail(user.displayAvatarURL());
|
||||||
|
|
||||||
|
// Add up to 10 of the latest sus notes to the embed
|
||||||
|
for (
|
||||||
|
let i = notes.length > 10 ? notes.length - 10 : 0;
|
||||||
|
i < notes.length;
|
||||||
|
i += 1
|
||||||
|
) {
|
||||||
|
// Get mod name
|
||||||
|
let mod = notes[i].modId;
|
||||||
|
const modMember = guild.members.cache.get(mod);
|
||||||
|
if (modMember !== undefined) {
|
||||||
|
mod = modMember.displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add sus note to embed
|
||||||
|
embed.addFields({
|
||||||
|
name: `Sus ID: ${notes[i].id} | Moderator: ${mod} | Date: <t:${Math.floor(
|
||||||
|
notes[i].time.getTime() / 1000,
|
||||||
|
)}>`,
|
||||||
|
value: notes[i].note,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createRestrictLogEmbed(
|
||||||
|
restrictions: RestrictionLogs,
|
||||||
|
user: User,
|
||||||
|
guild: Guild,
|
||||||
|
) {
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor('#FF6700')
|
||||||
|
.setTitle(`${restrictions.length} restrictions for ${user.tag}`)
|
||||||
|
.setThumbnail(user.displayAvatarURL())
|
||||||
|
.setFooter({ text: `ID: ${user.id}` });
|
||||||
|
|
||||||
|
// Add up to 10 of the latest restrictions to the embed
|
||||||
|
for (
|
||||||
|
let i = restrictions.length > 10 ? restrictions.length - 10 : 0;
|
||||||
|
i < restrictions.length;
|
||||||
|
i += 1
|
||||||
|
) {
|
||||||
|
// Get mod names
|
||||||
|
let restMod = restrictions[i].modId;
|
||||||
|
const restModMember = guild.members.cache.get(restMod);
|
||||||
|
if (restModMember !== undefined) {
|
||||||
|
restMod = restModMember.displayName;
|
||||||
|
}
|
||||||
|
let endRestMod = restrictions[i].endModId;
|
||||||
|
if (endRestMod !== null) {
|
||||||
|
const endRestModMember = guild.members.cache.get(endRestMod);
|
||||||
|
if (endRestModMember !== undefined) {
|
||||||
|
endRestMod = endRestModMember.displayName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let restTitle = `Restriction: ${i + 1} | Restricted by: ${restMod} | `;
|
||||||
|
|
||||||
|
if (endRestMod !== null) {
|
||||||
|
restTitle += `Unrestricted by: ${endRestMod} | `;
|
||||||
|
} else {
|
||||||
|
restTitle += 'Currently Restricted | ';
|
||||||
|
}
|
||||||
|
|
||||||
|
restTitle += `Date: <t:${Math.floor(
|
||||||
|
restrictions[i].startTime.getTime() / 1000,
|
||||||
|
)}>`;
|
||||||
|
|
||||||
|
embed.addFields({
|
||||||
|
name: restTitle,
|
||||||
|
value: restrictions[i].reason,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createWarningsEmbed(
|
||||||
|
warnings: Warnings,
|
||||||
|
user: User,
|
||||||
|
guild: Guild,
|
||||||
|
) {
|
||||||
|
const embed = new EmbedBuilder()
|
||||||
|
.setColor('#FF6700')
|
||||||
|
.setTitle(`${warnings.length} warnings for ${user.tag}`)
|
||||||
|
.setThumbnail(user.displayAvatarURL())
|
||||||
|
.setFooter({ text: `ID: ${user.id}` });
|
||||||
|
|
||||||
|
// Add up to 10 of the latest warnings to the embed
|
||||||
|
for (
|
||||||
|
let i = warnings.length > 10 ? warnings.length - 10 : 0;
|
||||||
|
i < warnings.length;
|
||||||
|
i += 1
|
||||||
|
) {
|
||||||
|
// Get mod names
|
||||||
|
let mod = warnings[i].modId;
|
||||||
|
const modMember = guild.members.cache.get(mod);
|
||||||
|
if (modMember !== undefined) {
|
||||||
|
mod = modMember.displayName;
|
||||||
|
}
|
||||||
|
|
||||||
|
let warnTitle = `ID: ${warnings[i].id} | Moderator: ${mod} | `;
|
||||||
|
|
||||||
|
warnTitle += `Date: <t:${Math.floor(warnings[i].time.getTime() / 1000)}>`;
|
||||||
|
|
||||||
|
embed.addFields({
|
||||||
|
name: warnTitle,
|
||||||
|
value: warnings[i].note,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return embed;
|
||||||
|
}
|
@ -124,6 +124,7 @@ let IDs = {
|
|||||||
logs: {
|
logs: {
|
||||||
restricted: '920993034462715925',
|
restricted: '920993034462715925',
|
||||||
economy: '932050015034159174',
|
economy: '932050015034159174',
|
||||||
|
sus: '872884989950324826',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
categories: {
|
categories: {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user