Feature/81 slash command support #192

Merged
Vylpes merged 6 commits from feature/81-slash-command-support into develop 2022-09-18 11:57:22 +01:00
52 changed files with 1469 additions and 1850 deletions

View file

@ -9,9 +9,8 @@
BOT_TOKEN= BOT_TOKEN=
BOT_VER=3.1 BOT_VER=3.1
BOT_AUTHOR=Vylpes BOT_AUTHOR=Vylpes
BOT_DATE=06 Sep 2022
BOT_OWNERID=147392775707426816 BOT_OWNERID=147392775707426816
BOT_PREFIX=d! BOT_CLIENTID=682942374040961060
ABOUT_FUNDING=https://ko-fi.com/vylpes ABOUT_FUNDING=https://ko-fi.com/vylpes
ABOUT_REPO=https://github.com/vylpes/vylbot-app ABOUT_REPO=https://github.com/vylpes/vylbot-app

View file

@ -9,9 +9,8 @@
BOT_TOKEN= BOT_TOKEN=
BOT_VER=3.1 BOT_VER=3.1
BOT_AUTHOR=Vylpes BOT_AUTHOR=Vylpes
BOT_DATE=06 Sep 2022
BOT_OWNERID=147392775707426816 BOT_OWNERID=147392775707426816
BOT_PREFIX=v! BOT_CLIENTID=680083120896081954
ABOUT_FUNDING=https://ko-fi.com/vylpes ABOUT_FUNDING=https://ko-fi.com/vylpes
ABOUT_REPO=https://github.com/vylpes/vylbot-app ABOUT_REPO=https://github.com/vylpes/vylbot-app

View file

@ -9,9 +9,8 @@
BOT_TOKEN= BOT_TOKEN=
BOT_VER=3.1 BOT_VER=3.1
BOT_AUTHOR=Vylpes BOT_AUTHOR=Vylpes
BOT_DATE=06 Sep 2022
BOT_OWNERID=147392775707426816 BOT_OWNERID=147392775707426816
BOT_PREFIX=s! BOT_CLIENTID=1016767908740857949
ABOUT_FUNDING=https://ko-fi.com/vylpes ABOUT_FUNDING=https://ko-fi.com/vylpes
ABOUT_REPO=https://github.com/vylpes/vylbot-app ABOUT_REPO=https://github.com/vylpes/vylbot-app

View file

@ -24,9 +24,10 @@
"homepage": "https://github.com/Vylpes/vylbot-app", "homepage": "https://github.com/Vylpes/vylbot-app",
"funding": "https://ko-fi.com/vylpes", "funding": "https://ko-fi.com/vylpes",
"dependencies": { "dependencies": {
"@discordjs/rest": "^1.1.0",
"@types/jest": "^27.0.3", "@types/jest": "^27.0.3",
"@types/uuid": "^8.3.4", "@types/uuid": "^8.3.4",
"discord.js": "^13.6.0", "discord.js": "^14.3.0",
"dotenv": "^10.0.0", "dotenv": "^10.0.0",
"emoji-regex": "^9.2.0", "emoji-regex": "^9.2.0",
"jest": "^27.4.5", "jest": "^27.4.5",

View file

@ -1,7 +1,6 @@
import { Client } from "discord.js"; import { Client } from "discord.js";
import * as dotenv from "dotenv"; import * as dotenv from "dotenv";
import { createConnection } from "typeorm"; import { createConnection } from "typeorm";
import DefaultValues from "../constants/DefaultValues";
import ICommandItem from "../contracts/ICommandItem"; import ICommandItem from "../contracts/ICommandItem";
import IEventItem from "../contracts/IEventItem"; import IEventItem from "../contracts/IEventItem";
import { Command } from "../type/command"; import { Command } from "../type/command";
@ -47,14 +46,13 @@ export class CoreClient extends Client {
return; return;
}); });
super.on("messageCreate", (message) => { super.on("interactionCreate", this._events.onInteractionCreate);
this._events.onMessageCreate(message, CoreClient._commandItems)
});
super.on("ready", this._events.onReady); super.on("ready", this._events.onReady);
super.login(process.env.BOT_TOKEN); await super.login(process.env.BOT_TOKEN);
this._util.loadEvents(this, CoreClient._eventItems); this._util.loadEvents(this, CoreClient._eventItems);
this._util.loadSlashCommands(this);
} }
public static RegisterCommand(name: string, command: Command, serverId?: string) { public static RegisterCommand(name: string, command: Command, serverId?: string) {

View file

@ -1,33 +1,40 @@
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
import { Message } from "discord.js"; import { Interaction } from "discord.js";
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
import ICommandItem from "../contracts/ICommandItem"; import ICommandItem from "../contracts/ICommandItem";
import SettingsHelper from "../helpers/SettingsHelper"; import SettingsHelper from "../helpers/SettingsHelper";
import { Util } from "./util"; import { CoreClient } from "./client";
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
export class Events { export class Events {
private _util: Util; public async onInteractionCreate(interaction: Interaction) {
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
if (!interaction.isChatInputCommand()) return;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
if (!interaction.guildId) return;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
constructor() { const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId);
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
this._util = new Util(); const disabledCommands = disabledCommandsString?.split(",");
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
}
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
// Emit when a message is sent const disabledCommandsMessage = await SettingsHelper.GetSetting("commands.disabled.message", interaction.guildId);
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
// Used to check for commands
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
public async onMessageCreate(message: Message, commands: ICommandItem[]) {
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
if (!message.guild) return;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
if (message.author.bot) return;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
const prefix = await SettingsHelper.GetSetting("bot.prefix", message.guild.id); if (disabledCommands?.find(x => x == interaction.commandName)) {
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
await interaction.reply(disabledCommandsMessage || "This command is disabled.");
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
if (!prefix) return; return;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
if (message.content.substring(0, prefix.length).toLowerCase() == prefix.toLowerCase()) {
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
const args = message.content.substring(prefix.length).split(" ");
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
const name = args.shift();
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
if (!name) return;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
await this._util.loadCommand(name, args, message, commands);
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
} }
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
const item = CoreClient.commandItems.find(x => x.Name == interaction.commandName && !x.ServerId);
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
const itemForServer = CoreClient.commandItems.find(x => x.Name == interaction.commandName && x.ServerId == interaction.guildId);
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
let itemToUse: ICommandItem;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
if (!itemForServer) {
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
if (!item) {
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
await interaction.reply('Command not found');
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
return;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
}
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
itemToUse = item;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
} else {
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
itemToUse = itemForServer;
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
}
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
itemToUse.Command.execute(interaction);
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
} }
// Emit when bot is logged in and ready to use // Emit when bot is logged in and ready to use

VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore
VylpesTester commented 2022-09-17 19:21:27 +01:00 (Migrated from github.com)
Review

Process.env isn't there anymore

Process.env isn't there anymore

View file

@ -1,83 +1,49 @@
// Required Components import { Client, REST, Routes, SlashCommandBuilder } from "discord.js";
import { Client, Message } from "discord.js";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandItem from "../contracts/ICommandItem";
import IEventItem from "../contracts/IEventItem"; import IEventItem from "../contracts/IEventItem";
import SettingsHelper from "../helpers/SettingsHelper"; import { CoreClient } from "./client";
import StringTools from "../helpers/StringTools";
import { CommandResponse } from "../constants/CommandResponse";
import ErrorMessages from "../constants/ErrorMessages";
// Util Class
export class Util { export class Util {
public async loadCommand(name: string, args: string[], message: Message, commands: ICommandItem[]) { public loadSlashCommands(client: Client) {
if (!message.member) return; const registeredCommands = CoreClient.commandItems;
if (!message.guild) return;
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", message.guild?.id); const globalCommands = registeredCommands.filter(x => !x.ServerId);
const disabledCommands = disabledCommandsString?.split(","); const guildCommands = registeredCommands.filter(x => x.ServerId);
if (disabledCommands?.find(x => x == name)) { const globalCommandData: SlashCommandBuilder[] = globalCommands
message.reply(process.env.COMMANDS_DISABLED_MESSAGE || "This command is disabled."); .filter(x => x.Command.CommandBuilder)
return; .flatMap(x => x.Command.CommandBuilder);
}
const item = commands.find(x => x.Name == name && !x.ServerId); const guildIds: string[] = [];
const itemForServer = commands.find(x => x.Name == name && x.ServerId == message.guild?.id);
let itemToUse: ICommandItem; for (let command of guildCommands) {
if (!guildIds.find(x => x == command.ServerId)) {
if (!itemForServer) { guildIds.push(command.ServerId!);
if (!item) {
message.reply('Command not found');
return;
} }
itemToUse = item;
} else {
itemToUse = itemForServer;
} }
const requiredRoles = itemToUse.Command.Roles; const rest = new REST({ version: '10' }).setToken(process.env.BOT_TOKEN!);
if (message.author.id != process.env.BOT_OWNERID && message.author.id != message.guild.ownerId) { rest.put(
for (const i in requiredRoles) { Routes.applicationCommands(process.env.BOT_CLIENTID!),
if (message.guild) { {
const setting = await SettingsHelper.GetSetting(`role.${requiredRoles[i]}`, message.guild?.id); body: globalCommandData
}
);
if (!setting) { for (let guild of guildIds) {
message.reply("Unable to verify if you have this role, please contact your bot administrator"); const guildCommandData = guildCommands.filter(x => x.ServerId == guild)
return; .filter(x => x.Command.CommandBuilder)
} .flatMap(x => x.Command.CommandBuilder);
if (!message.member.roles.cache.find(role => role.name == setting)) { if (!client.guilds.cache.has(guild)) continue;
message.reply(`You require the \`${StringTools.Capitalise(setting)}\` role to run this command`);
return; rest.put(
} Routes.applicationGuildCommands(process.env.BOT_CLIENTID!, guild),
{
body: guildCommandData
} }
} )
} }
const context: ICommandContext = {
name: name,
args: args,
message: message
};
const precheckResponse = itemToUse.Command.precheck(context);
const precheckAsyncResponse = await itemToUse.Command.precheckAsync(context);
if (precheckResponse != CommandResponse.Ok) {
message.reply(ErrorMessages.GetErrorMessage(precheckResponse));
return;
}
if (precheckAsyncResponse != CommandResponse.Ok) {
message.reply(ErrorMessages.GetErrorMessage(precheckAsyncResponse));
return;
}
itemToUse.Command.execute(context);
} }
// Load the events // Load the events

View file

@ -0,0 +1,58 @@
import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { Command } from "../../../type/command";
import { default as eLobby } from "../../../entity/501231711271780357/Lobby";
export default class AddRole extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('addlobby')
.setDescription('Add lobby channel')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addChannelOption(option =>
option
.setName('channel')
.setDescription('The channel')
.setRequired(true))
.addRoleOption(option =>
option
.setName('role')
.setDescription('The role to ping on request')
.setRequired(true))
.addNumberOption(option =>
option
.setName('cooldown')
.setDescription('The cooldown in minutes')
.setRequired(true))
.addStringOption(option =>
option
.setName('name')
.setDescription('The game name')
.setRequired(true));
}
public override async execute(interaction: CommandInteraction) {
const channel = interaction.options.get('channel');
const role = interaction.options.get('role');
const cooldown = interaction.options.get('cooldown');
const gameName = interaction.options.get('name');
if (!channel || !channel.channel || !role || !role.role || !cooldown || !cooldown.value || !gameName || !gameName.value) {
await interaction.reply('Fields are required.');
return;
}
const lobby = await eLobby.FetchOneByChannelId(channel.channel.id);
if (lobby) {
await interaction.reply('This channel has already been setup.');
return;
}
const entity = new eLobby(channel.channel.id, role.role.id, cooldown.value as number, gameName.value as string);
await entity.Save(eLobby, entity);
await interaction.reply(`Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown.value} minutes \` and will ping \`${role.name}\` on use`);
}
}

View file

@ -0,0 +1,41 @@
import { CommandInteraction, SlashCommandBuilder } from "discord.js";
import { Command } from "../../../type/command";
import { default as eLobby } from "../../../entity/501231711271780357/Lobby";
export default class Lobby extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('lobby')
.setDescription('Attempt to organise a lobby');
}
public override async execute(interaction: CommandInteraction) {
if (!interaction.channelId) return;
const lobby = await eLobby.FetchOneByChannelId(interaction.channelId);
if (!lobby) {
await interaction.reply('This channel is disabled from using the lobby command.');
return;
}
const timeNow = Date.now();
const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms
const timeAgo = timeNow - timeLength;
// If it was less than x minutes ago
if (lobby.LastUsed.getTime() > timeAgo) {
const timeLeft = Math.ceil((timeLength - (timeNow - lobby.LastUsed.getTime())) / 1000 / 60);
await interaction.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`);
return;
}
lobby.MarkAsUsed();
await lobby.Save(eLobby, lobby);
await interaction.reply(`${interaction.user} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`);
}
}

View file

@ -0,0 +1,40 @@
import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { Command } from "../../../type/command";
import { default as eLobby } from "../../../entity/501231711271780357/Lobby";
import BaseEntity from "../../../contracts/BaseEntity";
export default class RemoveLobby extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('removelobby')
.setDescription('Remove a lobby channel')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addChannelOption(option =>
option
.setName('channel')
.setDescription('The channel')
.setRequired(true));
}
public override async execute(interaction: CommandInteraction) {
const channel = interaction.options.get('channel');
if (!channel || !channel.channel) {
await interaction.reply('Channel is required.');
return;
}
const entity = await eLobby.FetchOneByChannelId(channel.channel.id);
if (!entity) {
await interaction.reply('Channel not found.');
return;
}
await BaseEntity.Remove<eLobby>(eLobby, entity);
await interaction.reply(`Removed <#${channel.channel.id}> from the list of lobby channels`);
}
}

View file

@ -1,5 +1,5 @@
import { ICommandContext } from "../../contracts/ICommandContext"; import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import PublicEmbed from "../../helpers/embeds/PublicEmbed"; import EmbedColours from "../../constants/EmbedColours";
import SettingsHelper from "../../helpers/SettingsHelper"; import SettingsHelper from "../../helpers/SettingsHelper";
import { Command } from "../../type/command"; import { Command } from "../../type/command";
@ -7,19 +7,23 @@ export default class Entry extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName('entry')
"moderator" .setDescription('Sends the entry embed')
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers);
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild) return; if (!interaction.guildId) return;
if (!interaction.channel) return;
const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", context.message.guild.id) || "rules"; const rulesChannelId = await SettingsHelper.GetSetting("channels.rules", interaction.guildId) || "rules";
const embedInfo = new PublicEmbed(context, "", `Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`); const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Welcome")
.setDescription(`Welcome to the server! Please make sure to read the rules in the <#${rulesChannelId}> channel and type the code found there in here to proceed to the main part of the server.`);
await embedInfo.SendToCurrentChannel(); await interaction.channel.send({ embeds: [ embed ]});
} }
} }

View file

@ -1,159 +0,0 @@
import { TextChannel } from "discord.js";
import { ICommandContext } from "../../contracts/ICommandContext";
import { Command } from "../../type/command";
import { default as eLobby } from "../../entity/501231711271780357/Lobby";
import SettingsHelper from "../../helpers/SettingsHelper";
import PublicEmbed from "../../helpers/embeds/PublicEmbed";
import { readFileSync } from "fs";
import ErrorEmbed from "../../helpers/embeds/ErrorEmbed";
import BaseEntity from "../../contracts/BaseEntity";
export default class Lobby extends Command {
constructor() {
super();
super.Category = "General";
}
public override async execute(context: ICommandContext) {
if (!context.message.guild) return;
switch (context.args[0]) {
case "config":
await this.UseConfig(context);
break;
default:
await this.UseDefault(context);
}
}
// =======
// Default
// =======
private async UseDefault(context: ICommandContext) {
const channel = context.message.channel as TextChannel;
const channelId = channel.id;
const lobby = await eLobby.FetchOneByChannelId(channelId);
if (!lobby) {
this.SendDisabled(context);
return;
}
const timeNow = Date.now();
const timeLength = lobby.Cooldown * 60 * 1000; // x minutes in ms
const timeAgo = timeNow - timeLength;
// If it was less than x minutes ago
if (lobby.LastUsed.getTime() > timeAgo) {
this.SendOnCooldown(context, timeLength, new Date(timeNow), lobby.LastUsed);
return;
}
await this.RequestLobby(context, lobby);
}
private async RequestLobby(context: ICommandContext, lobby: eLobby) {
lobby.MarkAsUsed();
await lobby.Save(eLobby, lobby);
context.message.channel.send(`${context.message.author} would like to organise a lobby of **${lobby.Name}**! <@&${lobby.RoleId}>`);
}
private SendOnCooldown(context: ICommandContext, timeLength: number, timeNow: Date, timeUsed: Date) {
const timeLeft = Math.ceil((timeLength - (timeNow.getTime() - timeUsed.getTime())) / 1000 / 60);
context.message.reply(`Requesting a lobby for this game is on cooldown! Please try again in **${timeLeft} minutes**.`);
}
private SendDisabled(context: ICommandContext) {
context.message.reply("This channel hasn't been setup for lobbies.");
}
// ======
// Config
// ======
private async UseConfig(context: ICommandContext) {
const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id);
if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) {
const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command");
await errorEmbed.SendToCurrentChannel();
return;
}
switch (context.args[1]) {
case "add":
await this.AddLobbyConfig(context);
break;
case "remove":
await this.RemoveLobbyConfig(context);
break;
case "help":
default:
await this.SendConfigHelp(context);
}
}
private async SendConfigHelp(context: ICommandContext) {
const helpText = readFileSync(`${process.cwd()}/data/usage/lobby.txt`).toString();
const embed = new PublicEmbed(context, "Configure Lobby Command", helpText);
await embed.SendToCurrentChannel();
}
private async AddLobbyConfig(context: ICommandContext) {
const channel = context.message.guild!.channels.cache.find(x => x.id == context.args[2]);
const role = context.message.guild!.roles.cache.find(x => x.id == context.args[3]);
const cooldown = Number(context.args[4]) || 30;
const gameName = context.args.splice(5).join(" ");
if (!channel) {
const errorEmbed = new ErrorEmbed(context, "The channel id you provided is invalid or channel does not exist.");
errorEmbed.SendToCurrentChannel();
return;
}
if (!role) {
const errorEmbed = new ErrorEmbed(context, "The role id you provided is invalid or role does not exist.");
errorEmbed.SendToCurrentChannel();
return;
}
const lobby = await eLobby.FetchOneByChannelId(channel.id);
if (lobby) {
const errorEmbed = new ErrorEmbed(context, "This channel has already been setup.");
errorEmbed.SendToCurrentChannel();
return;
}
const entity = new eLobby(channel.id, role.id, cooldown, gameName);
await entity.Save(eLobby, entity);
const embed = new PublicEmbed(context, "", `Added \`${channel.name}\` as a new lobby channel with a cooldown of \`${cooldown} minutes\` and will ping \`${role.name}\` on use`);
await embed.SendToCurrentChannel();
}
private async RemoveLobbyConfig(context: ICommandContext) {
const entity = await eLobby.FetchOneByChannelId(context.args[2]);
if (!entity) {
const errorEmbed = new ErrorEmbed(context, "The channel id you provided has not been setup as a lobby, unable to remove.");
await errorEmbed.SendToCurrentChannel();
return;
}
await BaseEntity.Remove<eLobby>(eLobby, entity);
const embed = new PublicEmbed(context, "", `Removed <#${context.args[2]}> from the list of lobby channels`);
await embed.SendToCurrentChannel();
}
}

View file

@ -0,0 +1,54 @@
import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { Command } from "../../type/command";
import { default as eRole } from "../../entity/Role";
import Server from "../../entity/Server";
export default class ConfigRole extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('configrole')
.setDescription('Toggle your roles')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ManageRoles)
.addRoleOption(option =>
option
.setName('role')
.setDescription('The role name')
.setRequired(true));
}
public override async execute(interaction: CommandInteraction) {
if (!interaction.guildId || !interaction.guild) return;
if (!interaction.member) return;
const role = interaction.options.get('role');
if (!role || !role.role) {
await interaction.reply('Fields are required.');
return;
}
const existingRole = await eRole.FetchOneByRoleId(role.role.id);
if (existingRole) {
await eRole.Remove(eRole, existingRole);
await interaction.reply('Removed role from configuration.');
} else {
const server = await Server.FetchOneById(Server, interaction.guildId);
if (!server) {
await interaction.reply('This server has not been setup.');
return;
}
const newRole = new eRole(role.role.id);
newRole.SetServer(server);
await newRole.Save(eRole, newRole);
await interaction.reply('Added role to configuration.');
}
}
}

109
src/commands/Role/role.ts Normal file
View file

@ -0,0 +1,109 @@
import { CommandInteraction, EmbedBuilder, GuildMemberRoleManager, SlashCommandBuilder } from "discord.js";
import { Command } from "../../type/command";
import { default as eRole } from "../../entity/Role";
import EmbedColours from "../../constants/EmbedColours";
export default class Role extends Command {
constructor() {
super();
super.CommandBuilder = new SlashCommandBuilder()
.setName('role')
.setDescription('Toggle your roles')
.addSubcommand(subcommand =>
subcommand
.setName('toggle')
.setDescription('Toggle your role')
.addRoleOption(option =>
option
.setName('role')
.setDescription('The role name')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('list')
.setDescription('List togglable roles'));
}
public override async execute(interaction: CommandInteraction) {
if (!interaction.isChatInputCommand()) return;
switch (interaction.options.getSubcommand()) {
case 'toggle':
await this.ToggleRole(interaction);
break;
case 'list':
await this.SendRolesList(interaction);
break;
default:
await interaction.reply('Subcommand not found.');
}
}
private async SendRolesList(interaction: CommandInteraction) {
const roles = await this.GetRolesList(interaction);
const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Roles")
.setDescription(`Roles: ${roles.length}\n\n${roles.join("\n")}`);
await interaction.reply({ embeds: [ embed ]});
}
private async ToggleRole(interaction: CommandInteraction) {
if (!interaction.guild) return;
if (!interaction.member) return;
const roles = await this.GetRolesList(interaction);
const requestedRole = interaction.options.get('role');
if (!requestedRole || !requestedRole.role) {
await interaction.reply('Fields are required.');
return;
}
if (!roles.includes(requestedRole.role.name)) {
await interaction.reply('This role isn\'t marked as assignable.');
return;
}
const roleManager = interaction.member.roles as GuildMemberRoleManager;
const userRole = roleManager.cache.find(x => x.name == requestedRole.role!.name);
const assignRole = interaction.guild.roles.cache.find(x => x.id == requestedRole.role!.id);
if (!assignRole) return;
if (!assignRole.editable) {
await interaction.reply('Insufficient permissions. Please contact a moderator.');
return;
}
if (!userRole) {
await roleManager.add(assignRole);
await interaction.reply(`Gave role: \`${assignRole.name}\``);
} else {
await roleManager.remove(assignRole);
await interaction.reply(`Removed role: \`${assignRole.name}\``);
}
}
private async GetRolesList(interaction: CommandInteraction): Promise<string[]> {
if (!interaction.guildId || !interaction.guild) return [];
const rolesArray = await eRole.FetchAllByServerId(interaction.guildId);
const roles: string[] = [];
for (let i = 0; i < rolesArray.length; i++) {
const serverRole = interaction.guild.roles.cache.find(x => x.id == rolesArray[i].RoleId);
if (serverRole) {
roles.push(serverRole.name);
}
}
return roles;
}
}

View file

@ -1,42 +1,56 @@
import { MessageActionRow, MessageButton } from "discord.js"; import { ActionRowBuilder, ButtonBuilder, ButtonStyle, CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
import { MessageButtonStyles } from "discord.js/typings/enums"; import EmbedColours from "../constants/EmbedColours";
import { ICommandContext } from "../contracts/ICommandContext";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class About extends Command { export default class About extends Command {
constructor() { constructor() {
super(); super();
super.Category = "General";
super.CommandBuilder = new SlashCommandBuilder()
.setName('about')
.setDescription('About VylBot');
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
const fundingLink = process.env.ABOUT_FUNDING; const fundingLink = process.env.ABOUT_FUNDING;
const repoLink = process.env.ABOUT_REPO; const repoLink = process.env.ABOUT_REPO;
const embed = new PublicEmbed(context, "About", "") const embed = new EmbedBuilder()
.addField("Version", process.env.BOT_VER!, true) .setColor(EmbedColours.Ok)
.addField("Author", process.env.BOT_AUTHOR!, true) .setTitle("About")
.addField("Date", process.env.BOT_DATE!, true); .setDescription("Discord Bot made by Vylpes");
const row = new MessageActionRow(); embed.addFields([
{
name: "Version",
value: process.env.BOT_VER!,
inline: true,
},
{
name: "Author",
value: process.env.BOT_AUTHOR!,
inline: true,
},
])
const row = new ActionRowBuilder<ButtonBuilder>();
if (repoLink) { if (repoLink) {
row.addComponents( row.addComponents(
new MessageButton() new ButtonBuilder()
.setURL(repoLink) .setURL(repoLink)
.setLabel("Repo") .setLabel("Repo")
.setStyle(MessageButtonStyles.LINK)); .setStyle(ButtonStyle.Link));
} }
if (fundingLink) { if (fundingLink) {
row.addComponents( row.addComponents(
new MessageButton() new ButtonBuilder()
.setURL(fundingLink) .setURL(fundingLink)
.setLabel("Funding") .setLabel("Funding")
.setStyle(MessageButtonStyles.LINK)); .setStyle(ButtonStyle.Link));
} }
await embed.SendToCurrentChannel({ components: [row] }); await interaction.reply({ embeds: [ embed ]});
} }
} }

View file

@ -1,144 +1,212 @@
import { ICommandContext } from "../contracts/ICommandContext";
import Audit from "../entity/Audit"; import Audit from "../entity/Audit";
import AuditTools from "../helpers/AuditTools"; import AuditTools from "../helpers/AuditTools";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
import SettingsHelper from "../helpers/SettingsHelper"; import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import { AuditType } from "../constants/AuditType";
import EmbedColours from "../constants/EmbedColours";
export default class Audits extends Command { export default class Audits extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("audits")
"moderator" .setDescription("View audits of a particular user in the server")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addSubcommand(subcommand =>
subcommand
.setName('user')
.setDescription('View all audits done against a user')
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('view')
.setDescription('View a particular audit')
.addStringOption(option =>
option
.setName('auditid')
.setDescription('The audit id in caps')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('clear')
.setDescription('Clears an audit from a user')
.addStringOption(option =>
option
.setName('auditid')
.setDescription('The audit id in caps')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('add')
.setDescription('Manually add an audit')
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('type')
.setDescription('The type of audit')
.setRequired(true)
.addChoices(
{ name: 'General', value: AuditType.General.toString() },
{ name: 'Warn', value: AuditType.Warn.toString() },
{ name: 'Mute', value: AuditType.Mute.toString() },
{ name: 'Kick', value: AuditType.Kick.toString() },
{ name: 'Ban', value: AuditType.Ban.toString() },
)
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason')));
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild) return; if (!interaction.isChatInputCommand()) return;
switch (context.args[0]) { switch (interaction.options.getSubcommand()) {
case "user": case "user":
await this.SendAuditForUser(context); await this.SendAuditForUser(interaction);
break; break;
case "view": case "view":
await this.SendAudit(context); await this.SendAudit(interaction);
break; break;
case "clear": case "clear":
await this.ClearAudit(context); await this.ClearAudit(interaction);
break; break;
case "add": case "add":
await this.AddAudit(context); await this.AddAudit(interaction);
break; break;
default: default:
await this.SendUsage(context); await interaction.reply("Subcommand doesn't exist.");
} }
} }
private async SendUsage(context: ICommandContext) { private async SendAuditForUser(interaction: CommandInteraction) {
const prefix = await SettingsHelper.GetServerPrefix(context.message.guild!.id); if (!interaction.guildId) return;
const description = [ const user = interaction.options.getUser('target');
`\`${prefix}audits user <id>\` - Send the audits for this user`,
`\`${prefix}audits view <id>\` - Send information about an audit`,
`\`${prefix}audits clear <id>\` - Clears an audit for a user by audit id`,
`\`${prefix}audits add <userid> <type> [reason]\` - Manually add an audit for a user`,
]
const publicEmbed = new PublicEmbed(context, "Usage", description.join("\n")); if (!user) {
await publicEmbed.SendToCurrentChannel(); await interaction.reply("User not found.");
} return;
}
private async SendAuditForUser(context: ICommandContext) { const audits = await Audit.FetchAuditsByUserId(user.id, interaction.guildId);
const userId = context.args[1];
const audits = await Audit.FetchAuditsByUserId(userId, context.message.guild!.id);
if (!audits || audits.length == 0) { if (!audits || audits.length == 0) {
const publicEmbed = new PublicEmbed(context, "", "There are no audits logged for this user."); await interaction.reply("There are no audits for this user.");
await publicEmbed.SendToCurrentChannel();
return; return;
} }
const publicEmbed = new PublicEmbed(context, "Audit Log", ""); const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Audits")
.setDescription(`Audits: ${audits.length}`);
for (let audit of audits) { for (let audit of audits) {
publicEmbed.addField(`${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`, audit.WhenCreated.toString()); embed.addFields([
{
name: `${audit.AuditId} // ${AuditTools.TypeToFriendlyText(audit.AuditType)}`,
value: audit.WhenCreated.toString(),
}
]);
} }
await publicEmbed.SendToCurrentChannel(); await interaction.reply({ embeds: [ embed ]});
} }
private async SendAudit(context: ICommandContext) { private async SendAudit(interaction: CommandInteraction) {
const auditId = context.args[1]; if (!interaction.guildId) return;
if (!auditId) { const auditId = interaction.options.get('auditid');
await this.SendUsage(context);
if (!auditId || !auditId.value) {
await interaction.reply("AuditId not found.");
return; return;
} }
const audit = await Audit.FetchAuditByAuditId(auditId.toUpperCase(), context.message.guild!.id); const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId);
if (!audit) { if (!audit) {
const errorEmbed = new ErrorEmbed(context, "This audit can not be found."); await interaction.reply("Audit not found.");
await errorEmbed.SendToCurrentChannel();
return; return;
} }
const publicEmbed = new PublicEmbed(context, `Audit ${audit.AuditId.toUpperCase()}`, ""); const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Audit")
.setDescription(audit.AuditId.toUpperCase())
.addFields([
{
name: "Reason",
value: audit.Reason || "*none*",
inline: true,
},
{
name: "Type",
value: AuditTools.TypeToFriendlyText(audit.AuditType),
inline: true,
},
{
name: "Moderator",
value: `<@${audit.ModeratorId}>`,
inline: true,
},
]);
publicEmbed.addField("Reason", audit.Reason || "*none*", true); await interaction.reply({ embeds: [ embed ]});
publicEmbed.addField("Type", AuditTools.TypeToFriendlyText(audit.AuditType), true);
publicEmbed.addField("Moderator", `<@${audit.ModeratorId}>`, true);
await publicEmbed.SendToCurrentChannel();
} }
private async ClearAudit(context: ICommandContext) { private async ClearAudit(interaction: CommandInteraction) {
const auditId = context.args[1]; if (!interaction.guildId) return;
if (!auditId) { const auditId = interaction.options.get('auditid');
await this.SendUsage(context);
if (!auditId || !auditId.value) {
await interaction.reply("AuditId not found.");
return; return;
} }
const audit = await Audit.FetchAuditByAuditId(auditId.toUpperCase(), context.message.guild!.id); const audit = await Audit.FetchAuditByAuditId(auditId.value.toString().toUpperCase(), interaction.guildId);
if (!audit) { if (!audit) {
const errorEmbed = new ErrorEmbed(context, "This audit can not be found."); await interaction.reply("Audit not found.");
await errorEmbed.SendToCurrentChannel();
return; return;
} }
await Audit.Remove(Audit, audit); await Audit.Remove(Audit, audit);
const publicEmbed = new PublicEmbed(context, "", "Audit cleared"); await interaction.reply("Audit cleared.");
await publicEmbed.SendToCurrentChannel();
} }
private async AddAudit(context: ICommandContext) { private async AddAudit(interaction: CommandInteraction) {
const userId = context.args[1]; if (!interaction.guildId) return;
const typeString = context.args[2];
const reason = context.args.splice(3)
.join(" ");
if (!userId || !typeString) { const user = interaction.options.getUser('target');
await this.SendUsage(context); const auditType = interaction.options.get('type');
const reasonInput = interaction.options.get('reason');
if (!user || !auditType || !auditType.value) {
await interaction.reply("Invalid input.");
return; return;
} }
const type = AuditTools.StringToType(typeString); const type = auditType.value as AuditType;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "";
const audit = new Audit(userId, type, reason, context.message.author.id, context.message.guild!.id); const audit = new Audit(user.id, type, reason, interaction.user.id, interaction.guildId);
await audit.Save(Audit, audit); await audit.Save(Audit, audit);
const publicEmbed = new PublicEmbed(context, "", `Created new audit with ID \`${audit.AuditId}\``); await interaction.reply(`Created new audit with ID \`${audit.AuditId}\``);
await publicEmbed.SendToCurrentChannel();
} }
} }

View file

@ -1,91 +1,79 @@
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import ErrorMessages from "../constants/ErrorMessages";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import Audit from "../entity/Audit"; import Audit from "../entity/Audit";
import { AuditType } from "../constants/AuditType"; import { AuditType } from "../constants/AuditType";
import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import EmbedColours from "../constants/EmbedColours";
import SettingsHelper from "../helpers/SettingsHelper";
export default class Ban extends Command { export default class Ban extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("ban")
"moderator" .setDescription("Ban a member from the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.BanMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const targetUser = context.message.mentions.users.first(); if (!interaction.isChatInputCommand()) return;
if (!interaction.guildId) return;
if (!interaction.guild) return;
if (!targetUser) { const targetUser = interaction.options.get('target');
const embed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await embed.SendToCurrentChannel();
return { if (!targetUser || !targetUser.user || !targetUser.member) {
commandContext: context, await interaction.reply("User not found.");
embeds: [embed], return;
};
} }
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); const member = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!targetMember) { const logEmbed = new EmbedBuilder()
const embed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await embed.SendToCurrentChannel(); .setTitle("Member Banned")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.addFields([
{
name: "Moderator",
value: `<@${interaction.user.id}>`,
},
{
name: "Reason",
value: reason,
},
]);
return { if (!member.bannable) {
commandContext: context, await interaction.reply('Insufficient permissions. Please contact a moderator.');
embeds: [embed], return;
};
} }
const reasonArgs = context.args; await member.ban();
reasonArgs.splice(0, 1) await interaction.reply(`\`${targetUser.user.tag}\` has been banned.`);
const reason = reasonArgs.join(" "); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
if (!context.message.guild?.available) { if (!channelName) return;
return {
commandContext: context, const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
embeds: [],
}; if (channel) {
await channel.send({ embeds: [ logEmbed ]});
} }
if (!targetMember.bannable) { const audit = new Audit(targetUser.user.id, AuditType.Ban, reason, interaction.user.id, interaction.guildId);
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await audit.Save(Audit, audit);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed],
};
}
const logEmbed = new LogEmbed(context, "Member Banned");
logEmbed.AddUser("User", targetUser, true);
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been banned`);
await targetMember.ban({ reason: `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}` });
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
if (context.message.guild) {
const audit = new Audit(targetUser.id, AuditType.Ban, reason, context.message.author.id, context.message.guild.id);
await audit.Save(Audit, audit);
}
return {
commandContext: context,
embeds: [logEmbed, publicEmbed],
};
} }
} }

View file

@ -1,17 +1,20 @@
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
import { ICommandContext } from "../contracts/ICommandContext";
import randomBunny from "random-bunny"; import randomBunny from "random-bunny";
import { CommandInteraction, EmbedBuilder, SlashCommandBuilder } from "discord.js";
import EmbedColours from "../constants/EmbedColours";
export default class Bunny extends Command { export default class Bunny extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Fun"; super.CommandBuilder = new SlashCommandBuilder()
.setName("bunny")
.setDescription("Get a random picture of a rabbit.");
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
if (!interaction.isChatInputCommand()) return;
const subreddits = [ const subreddits = [
'rabbits', 'rabbits',
'bunnieswithhats', 'bunnieswithhats',
@ -26,15 +29,17 @@ export default class Bunny extends Command {
const result = await randomBunny(selectedSubreddit, 'hot'); const result = await randomBunny(selectedSubreddit, 'hot');
if (result.IsSuccess) { if (result.IsSuccess) {
const embed = new PublicEmbed(context, result.Result!.Title, "") const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle(result.Result!.Title)
.setDescription(result.Result!.Permalink)
.setImage(result.Result!.Url) .setImage(result.Result!.Url)
.setURL(`https://reddit.com${result.Result!.Permalink}`) .setURL(`https://reddit.com${result.Result!.Permalink}`)
.setFooter({ text: `r/${selectedSubreddit} · ${result.Result!.Ups} upvotes` }); .setFooter({ text: `r/${selectedSubreddit} · ${result.Result!.Ups} upvotes`});
await embed.SendToCurrentChannel(); await interaction.reply({ embeds: [ embed ]});
} else { } else {
const errorEmbed = new ErrorEmbed(context, "There was an error using this command."); await interaction.reply("There was an error running this command.");
await errorEmbed.SendToCurrentChannel();
} }
} }
} }

View file

@ -1,50 +1,43 @@
import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import { CommandInteraction, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import { TextChannel } from "discord.js";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
export default class Clear extends Command { export default class Clear extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("clear")
"moderator" .setDescription("Clears the channel of messages")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ManageMessages)
.addNumberOption(option =>
option
.setName('count')
.setDescription('The amount to delete')
.setRequired(true)
.setMinValue(1)
.setMaxValue(100));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
if (context.args.length == 0) { if (!interaction.isChatInputCommand()) return;
const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); if (!interaction.channel) return;
await errorEmbed.SendToCurrentChannel();
return { const totalToClear = interaction.options.getNumber('count');
commandContext: context,
embeds: [errorEmbed]
};
}
const totalToClear = Number.parseInt(context.args[0]);
if (!totalToClear || totalToClear <= 0 || totalToClear > 100) { if (!totalToClear || totalToClear <= 0 || totalToClear > 100) {
const errorEmbed = new ErrorEmbed(context, "Please specify an amount between 1 and 100"); await interaction.reply('Please specify an amount between 1 and 100.');
await errorEmbed.SendToCurrentChannel(); return;
return {
commandContext: context,
embeds: [errorEmbed]
};
} }
await (context.message.channel as TextChannel).bulkDelete(totalToClear); const channel = interaction.channel as TextChannel;
const embed = new PublicEmbed(context, "", `${totalToClear} message(s) were removed`); if (!channel.manageable) {
await embed.SendToCurrentChannel(); await interaction.reply('Insufficient permissions. Please contact a moderator.');
return;
}
return { await channel.bulkDelete(totalToClear);
commandContext: context,
embeds: [embed] await interaction.reply(`${totalToClear} message(s) were removed.`);
};
} }
} }

View file

@ -1,7 +1,4 @@
import { CommandResponse } from "../constants/CommandResponse"; import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { ICommandContext } from "../contracts/ICommandContext";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import SettingsHelper from "../helpers/SettingsHelper"; import SettingsHelper from "../helpers/SettingsHelper";
import StringTools from "../helpers/StringTools"; import StringTools from "../helpers/StringTools";
import { Command } from "../type/command"; import { Command } from "../type/command";
@ -10,85 +7,58 @@ export default class Code extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName('code')
"moderator" .setDescription('Manage the verification code of the server')
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addSubcommand(subcommand =>
subcommand
.setName('randomise')
.setDescription('Regenerates the verification code for this server'))
.addSubcommand(subcommand =>
subcommand
.setName('embed')
.setDescription('Sends the embed with the current code to the current channel'));
} }
public override async precheckAsync(context: ICommandContext): Promise<CommandResponse> { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild){ if (!interaction.isChatInputCommand()) return;
return CommandResponse.NotInServer;
}
const isEnabled = await SettingsHelper.GetSetting("verification.enabled", context.message.guild?.id); switch (interaction.options.getSubcommand()) {
if (!isEnabled) {
return CommandResponse.FeatureDisabled;
}
if (isEnabled.toLocaleLowerCase() != 'true') {
return CommandResponse.FeatureDisabled;
}
return CommandResponse.Ok;
}
public override async execute(context: ICommandContext) {
const action = context.args[0];
switch (action) {
case "randomise": case "randomise":
await this.Randomise(context); await this.Randomise(interaction);
break; break;
case "embed": case "embed":
await this.SendEmbed(context); await this.SendEmbed(interaction);
break; break;
default:
await this.SendUsage(context);
} }
} }
private async SendUsage(context: ICommandContext) { private async Randomise(interaction: CommandInteraction) {
const description = [ if (!interaction.guildId) return;
"USAGE: <randomise|embed>",
"",
"randomise: Sets the server's entry code to a random code",
"embed: Sends an embed with the server's entry code"
].join("\n");
const embed = new PublicEmbed(context, "", description);
await embed.SendToCurrentChannel();
}
private async Randomise(context: ICommandContext) {
if (!context.message.guild) {
return;
}
const randomCode = StringTools.RandomString(5); const randomCode = StringTools.RandomString(5);
await SettingsHelper.SetSetting("verification.code", context.message.guild.id, randomCode); await SettingsHelper.SetSetting("verification.code", interaction.guildId, randomCode);
const embed = new PublicEmbed(context, "Code", `Entry code has been set to \`${randomCode}\``); await interaction.reply(`Entry code has been set to \`${randomCode}\``);
await embed.SendToCurrentChannel();
} }
private async SendEmbed(context: ICommandContext) { private async SendEmbed(interaction: CommandInteraction) {
if (!context.message.guild) { if (!interaction.guildId) return;
return; if (!interaction.channel) return;
}
const code = await SettingsHelper.GetSetting("verification.code", context.message.guild.id); const code = await SettingsHelper.GetSetting("verification.code", interaction.guildId);
if (!code || code == "") { if (!code || code == "") {
const errorEmbed = new ErrorEmbed(context, "There is no code for this server setup."); await interaction.reply("There is no code for this server setup.");
errorEmbed.SendToCurrentChannel();
return; return;
} }
const embed = new PublicEmbed(context, "Entry Code", code!); const embed = new EmbedBuilder()
await embed.SendToCurrentChannel(); .setTitle("Entry Code")
.setDescription(code);
await interaction.channel.send({ embeds: [ embed ]});
} }
} }

View file

@ -1,126 +1,186 @@
import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { readFileSync } from "fs"; import { readFileSync } from "fs";
import { CommandResponse } from "../constants/CommandResponse";
import DefaultValues from "../constants/DefaultValues"; import DefaultValues from "../constants/DefaultValues";
import { ICommandContext } from "../contracts/ICommandContext"; import EmbedColours from "../constants/EmbedColours";
import Server from "../entity/Server"; import Server from "../entity/Server";
import Setting from "../entity/Setting"; import Setting from "../entity/Setting";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Config extends Command { export default class Config extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Administration";
super.Roles = [ super.CommandBuilder = new SlashCommandBuilder()
"administrator" .setName('config')
] .setDescription('Configure the current server')
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
.addSubcommand(subcommand =>
subcommand
.setName('reset')
.setDescription('Reset a setting to the default')
.addStringOption(option =>
option
.setName('key')
.setDescription('The key')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('get')
.setDescription('Gets a setting for the server')
.addStringOption(option =>
option
.setName('key')
.setDescription('The key')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('set')
.setDescription('Sets a setting to a specified value')
.addStringOption(option =>
option
.setName('key')
.setDescription('The key')
.setRequired(true))
.addStringOption(option =>
option
.setName('value')
.setDescription('The value')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('list')
.setDescription('Lists all settings'))
} }
public override async precheckAsync(context: ICommandContext): Promise<CommandResponse> { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild) { if (!interaction.isChatInputCommand()) return;
return CommandResponse.ServerNotSetup; if (!interaction.guildId) return;
}
const server = await Server.FetchOneById<Server>(Server, context.message.guild?.id, [ const server = await Server.FetchOneById<Server>(Server, interaction.guildId, [
"Settings", "Settings",
]); ]);
if (!server) { if (!server) {
return CommandResponse.ServerNotSetup; await interaction.reply('Server not setup. Please use the setup command,');
}
return CommandResponse.Ok;
}
public override async execute(context: ICommandContext) {
if (!context.message.guild) {
return; return;
} }
const server = await Server.FetchOneById<Server>(Server, context.message.guild?.id, [ switch (interaction.options.getSubcommand()) {
"Settings", case 'list':
]); await this.SendHelpText(interaction);
break;
if (!server) { case 'reset':
return; await this.ResetValue(interaction);
} break;
case 'get':
const key = context.args[0]; await this.GetValue(interaction);
const action = context.args[1]; break;
const value = context.args.splice(2).join(" "); case 'set':
await this.SetValue(interaction);
if (!key) { break;
this.SendHelpText(context); default:
} else if (!action) { await interaction.reply('Subcommand not found.');
this.GetValue(context, server, key);
} else {
switch(action) {
case 'reset':
this.ResetValue(context, server, key);
break;
case 'set':
if (!value) {
const errorEmbed = new ErrorEmbed(context, "Value is required when setting");
errorEmbed.SendToCurrentChannel();
return;
}
this.SetValue(context, server, key, value);
break;
default:
const errorEmbed = new ErrorEmbed(context, "Action must be either set or reset");
errorEmbed.SendToCurrentChannel();
return;
}
} }
} }
private async SendHelpText(context: ICommandContext) { private async SendHelpText(interaction: CommandInteraction) {
const description = readFileSync(`${process.cwd()}/data/usage/config.txt`).toString(); const description = readFileSync(`${process.cwd()}/data/usage/config.txt`).toString();
const embed = new PublicEmbed(context, "Config", description); const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle("Config")
.setDescription(description);
await embed.SendToCurrentChannel(); await interaction.reply({ embeds: [ embed ]});
} }
private async GetValue(context: ICommandContext, server: Server, key: string) { private async GetValue(interaction: CommandInteraction) {
const setting = server.Settings.filter(x => x.Key == key)[0]; if (!interaction.guildId) return;
const key = interaction.options.get('key');
if (!key || !key.value) {
await interaction.reply('Fields are required.');
return;
}
const server = await Server.FetchOneById<Server>(Server, interaction.guildId, [
"Settings",
]);
if (!server) {
await interaction.reply('Server not found.');
return;
}
const setting = server.Settings.filter(x => x.Key == key.value)[0];
if (setting) { if (setting) {
const embed = new PublicEmbed(context, "", `${key}: ${setting.Value}`); await interaction.reply(`\`${key}\`: \`${setting.Value}\``);
await embed.SendToCurrentChannel();
} else { } else {
const embed = new PublicEmbed(context, "", `${key}: ${DefaultValues.GetValue(key)} <DEFAULT>`); await interaction.reply(`\`${key}\`: \`${DefaultValues.GetValue(key.value.toString())}\` <DEFAULT>`);
await embed.SendToCurrentChannel();
} }
} }
private async ResetValue(context: ICommandContext, server: Server, key: string) { private async ResetValue(interaction: CommandInteraction) {
const setting = server.Settings.filter(x => x.Key == key)[0]; if (!interaction.guildId) return;
const key = interaction.options.get('key');
if (!key || !key.value) {
await interaction.reply('Fields are required.');
return;
}
const server = await Server.FetchOneById<Server>(Server, interaction.guildId, [
"Settings",
]);
if (!server) {
await interaction.reply('Server not found.');
return;
}
const setting = server.Settings.filter(x => x.Key == key.value)[0];
if (!setting) { if (!setting) {
const embed = new PublicEmbed(context, "", "Setting has been reset"); await interaction.reply('Setting not found.');
await embed.SendToCurrentChannel();
return; return;
} }
await Setting.Remove(Setting, setting); await Setting.Remove(Setting, setting);
const embed = new PublicEmbed(context, "", "Setting has been reset"); await interaction.reply('The setting has been reset to the default.');
await embed.SendToCurrentChannel();
} }
private async SetValue(context: ICommandContext, server: Server, key: string, value: string) { private async SetValue(interaction: CommandInteraction) {
const setting = server.Settings.filter(x => x.Key == key)[0]; if (!interaction.guildId) return;
const key = interaction.options.get('key');
const value = interaction.options.get('value');
if (!key || !key.value || !value || !value.value) {
await interaction.reply('Fields are required.');
return;
}
const server = await Server.FetchOneById<Server>(Server, interaction.guildId, [
"Settings",
]);
if (!server) {
await interaction.reply('Server not found.');
return;
}
const setting = server.Settings.filter(x => x.Key == key.value)[0];
if (setting) { if (setting) {
setting.UpdateBasicDetails(key, value); setting.UpdateBasicDetails(key.value.toString(), value.value.toString());
await setting.Save(Setting, setting); await setting.Save(Setting, setting);
} else { } else {
const newSetting = new Setting(key, value); const newSetting = new Setting(key.value.toString(), value.value.toString());
await newSetting.Save(Setting, newSetting); await newSetting.Save(Setting, newSetting);
@ -129,7 +189,6 @@ export default class Config extends Command {
await server.Save(Server, server); await server.Save(Server, server);
} }
const embed = new PublicEmbed(context, "", "Setting has been set"); await interaction.reply('Setting has been set.');
await embed.SendToCurrentChannel();
} }
} }

View file

@ -1,5 +1,4 @@
import { ICommandContext } from "../contracts/ICommandContext"; import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import SettingsHelper from "../helpers/SettingsHelper"; import SettingsHelper from "../helpers/SettingsHelper";
import { Command } from "../type/command"; import { Command } from "../type/command";
@ -7,87 +6,86 @@ export default class Disable extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName('disable')
"moderator" .setDescription('Disables a command')
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator)
.addSubcommand(subcommand =>
subcommand
.setName('add')
.setDescription('Disables a command for the server')
.addStringOption(option =>
option
.setName('name')
.setDescription('The name of the command')
.setRequired(true)))
.addSubcommand(subcommand =>
subcommand
.setName('remove')
.setDescription('Enables a command for the server')
.addStringOption(option =>
option
.setName('name')
.setDescription('The name of the command')
.setRequired(true)));
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
const action = context.args[0]; if (!interaction.isChatInputCommand()) return;
switch (action) { switch (interaction.options.getSubcommand()) {
case "add": case "add":
await this.Add(context); await this.Add(interaction);
break; break;
case "remove": case "remove":
await this.Remove(context); await this.Remove(interaction);
break; break;
default: default:
await this.SendUsage(context); await interaction.reply('Subcommand not found.');
} }
} }
private async SendUsage(context: ICommandContext) { private async Add(interaction: CommandInteraction) {
const description = [ if (!interaction.guildId) return;
"USAGE: <add|remove> <name>",
"",
"add: Adds the command name to the server's disabled command string",
"remove: Removes the command name from the server's disabled command string",
"name: The name of the command to enable/disable"
].join("\n");
const embed = new PublicEmbed(context, "", description); const commandName = interaction.options.get('name');
await embed.SendToCurrentChannel();
}
private async Add(context: ICommandContext) { if (!commandName || !commandName.value) {
if (!context.message.guild) { await interaction.reply('Fields are required.');
return; return;
} }
const commandName = context.args[1]; const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId);
if (!commandName) {
this.SendUsage(context);
return;
}
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id);
const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : [];
disabledCommands?.push(commandName); disabledCommands?.push(commandName.value.toString());
await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(","));
const embed = new PublicEmbed(context, "", `Disabled command: ${commandName}`); await interaction.reply(`Disabled command ${commandName.value}`);
await embed.SendToCurrentChannel();
} }
private async Remove(context: ICommandContext) { private async Remove(interaction: CommandInteraction) {
if (!context.message.guild) { if (!interaction.guildId) return;
const commandName = interaction.options.get('name');
if (!commandName || !commandName.value) {
await interaction.reply('Fields are required.');
return; return;
} }
const commandName = context.args[1]; const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", interaction.guildId);
if (!commandName) {
this.SendUsage(context);
return;
}
const disabledCommandsString = await SettingsHelper.GetSetting("commands.disabled", context.message.guild.id);
const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : []; const disabledCommands = disabledCommandsString != "" ? disabledCommandsString?.split(",") : [];
const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName); const disabledCommandsInstance = disabledCommands?.findIndex(x => x == commandName.value!.toString());
if (disabledCommandsInstance! > -1) { if (disabledCommandsInstance! > -1) {
disabledCommands?.splice(disabledCommandsInstance!, 1); disabledCommands?.splice(disabledCommandsInstance!, 1);
} }
await SettingsHelper.SetSetting("commands.disabled", context.message.guild.id, disabledCommands!.join(",")); await SettingsHelper.SetSetting("commands.disabled", interaction.guildId, disabledCommands!.join(","));
const embed = new PublicEmbed(context, "", `Enabled command: ${commandName}`); await interaction.reply(`Enabled command ${commandName.value}`);
await embed.SendToCurrentChannel();
} }
} }

View file

@ -1,68 +0,0 @@
import { existsSync, readdirSync } from "fs";
import { CoreClient } from "../client/client";
import { ICommandContext } from "../contracts/ICommandContext";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import StringTools from "../helpers/StringTools";
import { Command } from "../type/command";
export interface ICommandData {
Exists: boolean;
Name?: string;
Category?: string;
Roles?: string[];
}
export default class Help extends Command {
constructor() {
super();
super.Category = "General";
}
public override async execute(context: ICommandContext) {
if (context.args.length == 0) {
await this.SendAll(context);
} else {
await this.SendSingle(context);
}
}
public async SendAll(context: ICommandContext) {
const allCommands = CoreClient.commandItems
.filter(x => !x.ServerId || x.ServerId == context.message.guild?.id);
const cateogries = [...new Set(allCommands.map(x => x.Command.Category))];
const embed = new PublicEmbed(context, "Commands", "");
cateogries.forEach(category => {
let filtered = allCommands.filter(x => x.Command.Category == category);
embed.addField(StringTools.Capitalise(category || "Uncategorised"), StringTools.CapitaliseArray(filtered.flatMap(x => x.Name)).join(", "));
});
await embed.SendToCurrentChannel();
}
public async SendSingle(context: ICommandContext) {
const command = CoreClient.commandItems.find(x => x.Name == context.args[0] && !x.ServerId);
const exclusiveCommand = CoreClient.commandItems.find(x => x.Name == context.args[0] && x.ServerId == context.message.guild?.id);
if (exclusiveCommand) {
const embed = new PublicEmbed(context, StringTools.Capitalise(exclusiveCommand.Name), "");
embed.addField("Category", StringTools.Capitalise(exclusiveCommand.Command.Category || "Uncategorised"));
embed.addField("Required Roles", StringTools.Capitalise(exclusiveCommand.Command.Roles.join(", ")) || "Everyone");
await embed.SendToCurrentChannel();
} else if (command) {
const embed = new PublicEmbed(context, StringTools.Capitalise(command.Name), "");
embed.addField("Category", StringTools.Capitalise(command.Command.Category || "Uncategorised"));
embed.addField("Required Roles", StringTools.Capitalise(command.Command.Roles.join(", ")) || "Everyone");
await embed.SendToCurrentChannel();
} else {
const errorEmbed = new ErrorEmbed(context, "Command does not exist");
await errorEmbed.SendToCurrentChannel();
}
}
}

View file

@ -1,37 +1,34 @@
import { ICommandContext } from "../contracts/ICommandContext"; import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import IgnoredChannel from "../entity/IgnoredChannel"; import IgnoredChannel from "../entity/IgnoredChannel";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Ignore extends Command { export default class Ignore extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName('ignore')
"moderator" .setDescription('Ignore events in this channel')
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild) return; if (!interaction.guildId) return;
const isChannelIgnored = await IgnoredChannel.IsChannelIgnored(context.message.channel.id); const isChannelIgnored = await IgnoredChannel.IsChannelIgnored(interaction.guildId);
if (isChannelIgnored) { if (isChannelIgnored) {
const entity = await IgnoredChannel.FetchOneById(IgnoredChannel, context.message.channel.id); const entity = await IgnoredChannel.FetchOneById(IgnoredChannel, interaction.guildId);
await IgnoredChannel.Remove(IgnoredChannel, entity); await IgnoredChannel.Remove(IgnoredChannel, entity);
const embed = new PublicEmbed(context, "Success", "This channel will start being logged again."); await interaction.reply('This channel will start being logged again.');
await embed.SendToCurrentChannel();
} else { } else {
const entity = new IgnoredChannel(context.message.channel.id); const entity = new IgnoredChannel(interaction.guildId);
await entity.Save(IgnoredChannel, entity); await entity.Save(IgnoredChannel, entity);
const embed = new PublicEmbed(context, "Success", "This channel will now be ignored from logging."); await interaction.reply('This channel will now be ignored from logging.');
await embed.SendToCurrentChannel();
} }
} }
} }

View file

@ -1,91 +1,79 @@
import { AuditType } from "../constants/AuditType";
import ErrorMessages from "../constants/ErrorMessages";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import Audit from "../entity/Audit";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
import Audit from "../entity/Audit";
import { AuditType } from "../constants/AuditType";
import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import EmbedColours from "../constants/EmbedColours";
import SettingsHelper from "../helpers/SettingsHelper";
export default class Kick extends Command { export default class Kick extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("kick")
"moderator" .setDescription("Kick a member from the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.KickMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const targetUser = context.message.mentions.users.first(); if (!interaction.isChatInputCommand()) return;
if (!interaction.guildId) return;
if (!interaction.guild) return;
if (!targetUser) { const targetUser = interaction.options.get('target');
const embed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await embed.SendToCurrentChannel();
return { if (!targetUser || !targetUser.user || !targetUser.member) {
commandContext: context, await interaction.reply("User not found.");
embeds: [embed] return;
};
} }
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); const member = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!targetMember) { const logEmbed = new EmbedBuilder()
const embed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await embed.SendToCurrentChannel(); .setTitle("Member Kicked")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.addFields([
{
name: "Moderator",
value: `<@${interaction.user.id}>`,
},
{
name: "Reason",
value: reason,
},
]);
return { if (!member.kickable) {
commandContext: context, await interaction.reply('Insufficient permissions. Please contact a moderator.');
embeds: [embed] return;
};
} }
const reasonArgs = context.args; await member.kick();
reasonArgs.splice(0, 1) await interaction.reply(`\`${targetUser.user.tag}\` has been kicked.`);
const reason = reasonArgs.join(" "); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
if (!context.message.guild?.available) { if (!channelName) return;
return {
commandContext: context, const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
embeds: []
}; if (channel) {
await channel.send({ embeds: [ logEmbed ]});
} }
if (!targetMember.kickable) { const audit = new Audit(targetUser.user.id, AuditType.Kick, reason, interaction.user.id, interaction.guildId);
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await audit.Save(Audit, audit);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed]
};
}
const logEmbed = new LogEmbed(context, "Member Kicked");
logEmbed.AddUser("User", targetUser, true);
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been kicked`);
await targetMember.kick(`Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`);
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
if (context.message.guild) {
const audit = new Audit(targetUser.id, AuditType.Kick, reason, context.message.author.id, context.message.guild.id);
await audit.Save(Audit, audit);
}
return {
commandContext: context,
embeds: [logEmbed, publicEmbed]
};
} }
} }

View file

@ -1,104 +1,83 @@
import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import { AuditType } from "../constants/AuditType"; import { AuditType } from "../constants/AuditType";
import ErrorMessages from "../constants/ErrorMessages"; import EmbedColours from "../constants/EmbedColours";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import Audit from "../entity/Audit"; import Audit from "../entity/Audit";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import SettingsHelper from "../helpers/SettingsHelper";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Mute extends Command { export default class Mute extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("mute")
"moderator" .setDescription("Mute a member in the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const targetUser = context.message.mentions.users.first(); if (!interaction.guild || !interaction.guildId) return;
if (!targetUser) { const targetUser = interaction.options.get('target');
const embed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await embed.SendToCurrentChannel();
return { if (!targetUser || !targetUser.user || !targetUser.member) {
commandContext: context, await interaction.reply('Fields are required.');
embeds: [embed] return;
};
} }
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); const targetMember = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!targetMember) { const logEmbed = new EmbedBuilder()
const embed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await embed.SendToCurrentChannel(); .setTitle("Member Muted")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.addFields([
{
name: "Moderator",
value: `<@${interaction.user.id}>`,
},
{
name: "Reason",
value: reason,
},
]);
return { const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED);
commandContext: context,
embeds: [embed]
};
}
const reasonArgs = context.args; if (!mutedRole) {
reasonArgs.splice(0, 1); await interaction.reply('Muted role not found.');
return;
const reason = reasonArgs.join(" ");
if (!context.message.guild?.available) {
return {
commandContext: context,
embeds: []
};
} }
if (!targetMember.manageable) { if (!targetMember.manageable) {
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await interaction.reply('Insufficient permissions. Please contact a moderator.');
await embed.SendToCurrentChannel(); return;
return {
commandContext: context,
embeds: [embed]
};
} }
const logEmbed = new LogEmbed(context, "Member Muted"); await targetMember.roles.add(mutedRole);
logEmbed.AddUser("User", targetUser, true)
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been muted`); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
publicEmbed.AddReason(reason);
const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); if (!channelName) return;
if (!mutedRole) { const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound);
await embed.SendToCurrentChannel();
return { if (channel) {
commandContext: context, await channel.send({ embeds: [ logEmbed ]});
embeds: [embed]
};
} }
await targetMember.roles.add(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`); const audit = new Audit(targetUser.user.id, AuditType.Mute, reason, interaction.user.id, interaction.guildId);
await audit.Save(Audit, audit);
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
if (context.message.guild) {
const audit = new Audit(targetUser.id, AuditType.Mute, reason, context.message.author.id, context.message.guild.id);
await audit.Save(Audit, audit);
}
return {
commandContext: context,
embeds: [logEmbed, publicEmbed]
};
} }
} }

View file

@ -1,67 +0,0 @@
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command";
export default class Poll extends Command {
constructor() {
super();
super.Category = "General";
}
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> {
const argsJoined = context.args.join(" ");
const argsSplit = argsJoined.split(";");
if (argsSplit.length < 3 || argsSplit.length > 10) {
const errorEmbed = new ErrorEmbed(context, "Usage: <title>;<option 1>;<option 2>... (separate options with semicolons), maximum of 9 options");
await errorEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [errorEmbed]
};
}
const title = argsSplit[0];
const arrayOfNumbers = [
':one:',
':two:',
':three:',
':four:',
':five:',
':six:',
':seven:',
':eight:',
':nine:'
];
const reactionEmojis = ["1⃣", "2⃣", "3⃣", "4⃣", "5⃣", "6⃣", "7⃣", "8⃣", "9⃣"];
const description = arrayOfNumbers.splice(0, argsSplit.length - 1);
description.forEach((value, index) => {
description[index] = `${value} ${argsSplit[index + 1]}`;
});
const embed = new PublicEmbed(context, title, description.join("\n"));
const message = await context.message.channel.send({ embeds: [ embed ]});
description.forEach(async (value, index) => {
await message.react(reactionEmojis[index]);
});
if (context.message.deletable) {
await context.message.delete();
}
return {
commandContext: context,
embeds: [embed]
};
}
}

View file

@ -1,232 +0,0 @@
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Role as DiscordRole } from "discord.js";
import { Command } from "../type/command";
import { ICommandContext } from "../contracts/ICommandContext";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import SettingsHelper from "../helpers/SettingsHelper";
import { readFileSync } from "fs";
import { default as eRole } from "../entity/Role";
import Server from "../entity/Server";
export default class Role extends Command {
constructor() {
super();
super.Category = "General";
}
public override async execute(context: ICommandContext) {
if (!context.message.guild) return;
switch (context.args[0]) {
case "config":
await this.UseConfig(context);
break;
default:
await this.UseDefault(context);
}
}
// =======
// Default
// =======
private async UseDefault(context: ICommandContext) {
if (context.args.length == 0) {
await this.SendRolesList(context, context.message.guild!.id);
} else {
await this.ToggleRole(context);
}
}
public async GetRolesList(context: ICommandContext): Promise<string[]> {
const rolesArray = await eRole.FetchAllByServerId(context.message.guild!.id);
const stringArray: string[] = [];
for (let i = 0; i < rolesArray.length; i++) {
const serverRole = context.message.guild!.roles.cache.find(x => x.id == rolesArray[i].RoleId);
if (serverRole) {
stringArray.push(serverRole.name);
}
}
return stringArray;
}
public async SendRolesList(context: ICommandContext, serverId: string): Promise<ICommandReturnContext> {
const roles = await this.GetRolesList(context);
const botPrefix = await SettingsHelper.GetServerPrefix(serverId);
const description = roles.length == 0 ? "*no roles*" : `Do ${botPrefix}role <role> to get the role!\n\n${roles.join('\n')}`;
const embed = new PublicEmbed(context, "Roles", description);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed]
};
}
public async ToggleRole(context: ICommandContext): Promise<ICommandReturnContext> {
const roles = await this.GetRolesList(context);
const requestedRole = context.args.join(" ");
if (!roles.includes(requestedRole)) {
const errorEmbed = new ErrorEmbed(context, "This role isn't marked as assignable, to see a list of assignable roles, run this command without any parameters");
await errorEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [errorEmbed]
};
}
const assignRole = context.message.guild?.roles.cache.find(x => x.name == requestedRole);
if (!assignRole) {
const errorEmbed = new ErrorEmbed(context, "The current server doesn't have this role. Please contact the server's moderators");
await errorEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [errorEmbed]
};
}
const role = context.message.member?.roles.cache.find(x => x.name == requestedRole)
if (!role) {
await this.AddRole(context, assignRole);
} else {
await this.RemoveRole(context, assignRole);
}
return {
commandContext: context,
embeds: []
};
}
public async AddRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> {
await context.message.member?.roles.add(role, "Toggled with role command");
const embed = new PublicEmbed(context, "", `Gave role: \`${role.name}\``);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed]
};
}
public async RemoveRole(context: ICommandContext, role: DiscordRole): Promise<ICommandReturnContext> {
await context.message.member?.roles.remove(role, "Toggled with role command");
const embed = new PublicEmbed(context, "", `Removed role: \`${role.name}\``);
await embed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [embed]
};
}
// ======
// Config
// ======
private async UseConfig(context: ICommandContext) {
const moderatorRole = await SettingsHelper.GetSetting("role.moderator", context.message.guild!.id);
if (!context.message.member?.roles.cache.find(x => x.name == moderatorRole)) {
const errorEmbed = new ErrorEmbed(context, "Sorry, you must be a moderator to be able to configure this command");
await errorEmbed.SendToCurrentChannel();
return;
}
switch (context.args[1]) {
case "add":
await this.AddRoleConfig(context);
break;
case "remove":
await this.RemoveRoleConfig(context);
break;
default:
await this.SendConfigHelp(context);
}
}
private async SendConfigHelp(context: ICommandContext) {
const helpText = readFileSync(`${process.cwd()}/data/usage/role.txt`).toString();
const embed = new PublicEmbed(context, "Configure Role Command", helpText);
await embed.SendToCurrentChannel();
}
private async AddRoleConfig(context: ICommandContext) {
const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]);
if (!role) {
this.SendConfigHelp(context);
return;
}
const existingRole = await eRole.FetchOneByRoleId(role.id);
if (existingRole) {
const errorEmbed = new ErrorEmbed(context, "This role has already been setup");
await errorEmbed.SendToCurrentChannel();
return;
}
const server = await Server.FetchOneById(Server, context.message.guild!.id, [
"Roles",
]);
if (!server) {
const errorEmbed = new ErrorEmbed(context, "Server not setup, please request the server owner runs the setup command.");
await errorEmbed.SendToCurrentChannel();
return;
}
const roleSetting = new eRole(role.id);
await roleSetting.Save(eRole, roleSetting);
server.AddRoleToServer(roleSetting);
await server.Save(Server, server);
const embed = new PublicEmbed(context, "", `Added \`${role.name}\` as a new assignable role`);
await embed.SendToCurrentChannel();
}
private async RemoveRoleConfig(context: ICommandContext) {
const role = context.message.guild!.roles.cache.find(x => x.id == context.args[2]);
if (!role) {
this.SendConfigHelp(context);
return;
}
const existingRole = await eRole.FetchOneByRoleId(role.id);
if (!existingRole) {
const errorEmbed = new ErrorEmbed(context, "Unable to find this role");
errorEmbed.SendToCurrentChannel();
return;
}
await eRole.Remove(eRole, existingRole);
const embed = new PublicEmbed(context, "", `Removed \`${role.name}\` from the list of assignable roles`);
await embed.SendToCurrentChannel();
}
}

View file

@ -1,7 +1,6 @@
import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import { existsSync, readFileSync } from "fs"; import { existsSync, readFileSync } from "fs";
import { ICommandContext } from "../contracts/ICommandContext"; import EmbedColours from "../constants/EmbedColours";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
interface IRules { interface IRules {
@ -15,38 +14,48 @@ export default class Rules extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Admin"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("rules")
"administrator" .setDescription("Send the rules embeds for this server")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
if (!existsSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`)) { if (!interaction.guildId) return;
const errorEmbed = new ErrorEmbed(context, "Rules file doesn't exist");
await errorEmbed.SendToCurrentChannel();
if (!existsSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`)) {
await interaction.reply('Rules file doesn\'t exist.');
return; return;
} }
const rulesFile = readFileSync(`${process.cwd()}/data/rules/${context.message.guild?.id}.json`).toString(); const rulesFile = readFileSync(`${process.cwd()}/data/rules/${interaction.guildId}.json`).toString();
const rules = JSON.parse(rulesFile) as IRules[]; const rules = JSON.parse(rulesFile) as IRules[];
const embeds: PublicEmbed[] = []; const embeds: EmbedBuilder[] = [];
rules.forEach(rule => { rules.forEach(rule => {
const embed = new PublicEmbed(context, rule.title || "", rule.description?.join("\n") || ""); const embed = new EmbedBuilder()
.setColor(EmbedColours.Ok)
.setTitle(rule.title || "Rules")
.setDescription(rule.description ? rule.description.join("\n") : "*none*");
embed.setImage(rule.image || ""); if (rule.image) {
embed.setFooter({ text: rule.footer || "" }); embed.setImage(rule.image);
}
if (rule.footer) {
embed.setFooter({ text: rule.footer });
}
embeds.push(embed); embeds.push(embed);
}); });
for (let i = 0; i < embeds.length; i++) { const channel = interaction.channel;
const embed = embeds[i];
await embed.SendToCurrentChannel(); if (!channel) {
return;
} }
await channel.send({ embeds: embeds });
} }
} }

View file

@ -1,26 +0,0 @@
import { ICommandContext } from "../contracts/ICommandContext";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import { Command } from "../type/command";
export default class Say extends Command {
constructor() {
super();
super.Category = "Misc";
super.Roles = [
"moderator"
];
}
public override async execute(context: ICommandContext) {
const input = context.args.join(" ");
if (input.length == 0) {
const errorEmbed = new ErrorEmbed(context, "You must supply a message.");
await errorEmbed.SendToCurrentChannel();
return;
}
context.message.channel.send(input);
}
}

View file

@ -1,37 +1,31 @@
import { ICommandContext } from "../contracts/ICommandContext"; import { CommandInteraction, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import Server from "../entity/Server"; import Server from "../entity/Server";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Setup extends Command { export default class Setup extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Administration";
super.Roles = [ super.CommandBuilder = new SlashCommandBuilder()
"moderator" .setName('setup')
] .setDescription('Makes the server ready to be configured')
.setDefaultMemberPermissions(PermissionsBitField.Flags.Administrator);
} }
public override async execute(context: ICommandContext) { public override async execute(interaction: CommandInteraction) {
if (!context.message.guild) { if (!interaction.guildId) return;
return;
}
const server = await Server.FetchOneById(Server, context.message.guild?.id); const server = await Server.FetchOneById(Server, interaction.guildId);
if (server) { if (server) {
const embed = new ErrorEmbed(context, "This server has already been setup, please configure using the config command"); await interaction.reply('This server has already been setup, please configure using the config command.');
await embed.SendToCurrentChannel();
return; return;
} }
const newServer = new Server(context.message.guild?.id); const newServer = new Server(interaction.guildId);
await newServer.Save(Server, newServer); await newServer.Save(Server, newServer);
const embed = new PublicEmbed(context, "Success", "Please configure using the config command"); await interaction.reply('Success, please configure using the configure command.');
await embed.SendToCurrentChannel();
} }
} }

View file

@ -1,96 +1,78 @@
import ErrorMessages from "../constants/ErrorMessages"; import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import { ICommandContext } from "../contracts/ICommandContext"; import EmbedColours from "../constants/EmbedColours";
import ICommandReturnContext from "../contracts/ICommandReturnContext"; import SettingsHelper from "../helpers/SettingsHelper";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Unmute extends Command { export default class Unmute extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("unmute")
"moderator" .setDescription("Unmute a member in the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const targetUser = context.message.mentions.users.first(); if (!interaction.guild || !interaction.guildId) return;
if (!targetUser) { const targetUser = interaction.options.get('target');
const embed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await embed.SendToCurrentChannel();
return { if (!targetUser || !targetUser.user || !targetUser.member) {
commandContext: context, await interaction.reply('Fields are required.');
embeds: [embed] return;
};
} }
const targetMember = context.message.guild?.members.cache.find(x => x.user.id == targetUser.id); const targetMember = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!targetMember) { const logEmbed = new EmbedBuilder()
const embed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await embed.SendToCurrentChannel(); .setTitle("Member Unmuted")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.addFields([
{
name: "Moderator",
value: `<@${interaction.user.id}>`,
},
{
name: "Reason",
value: reason,
},
]);
return { const mutedRole = interaction.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED);
commandContext: context,
embeds: [embed]
};
}
const reasonArgs = context.args; if (!mutedRole) {
reasonArgs.splice(0, 1); await interaction.reply('Muted role not found.');
return;
const reason = reasonArgs.join(" ");
if (!context.message.guild?.available) {
return {
commandContext: context,
embeds: []
};
} }
if (!targetMember.manageable) { if (!targetMember.manageable) {
const embed = new ErrorEmbed(context, ErrorMessages.InsufficientBotPermissions); await interaction.reply('Insufficient permissions. Please contact a moderator.');
await embed.SendToCurrentChannel(); return;
return {
commandContext: context,
embeds: [embed]
};
} }
const logEmbed = new LogEmbed(context, "Member Unmuted"); await targetMember.roles.remove(mutedRole);
logEmbed.AddUser("User", targetUser, true)
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${targetUser} has been unmuted`); const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
publicEmbed.AddReason(reason);
const mutedRole = context.message.guild.roles.cache.find(role => role.name == process.env.ROLES_MUTED); if (!channelName) return;
if (!mutedRole) { const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
const embed = new ErrorEmbed(context, ErrorMessages.RoleNotFound);
await embed.SendToCurrentChannel();
return { if (channel) {
commandContext: context, await channel.send({ embeds: [ logEmbed ]});
embeds: [embed]
};
} }
await targetMember.roles.remove(mutedRole, `Moderator: ${context.message.author.tag}, Reason: ${reason || "*none*"}`);
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
return {
commandContext: context,
embeds: [logEmbed, publicEmbed]
};
} }
} }

View file

@ -1,79 +1,69 @@
import { CommandInteraction, EmbedBuilder, GuildMember, PermissionsBitField, SlashCommandBuilder, TextChannel } from "discord.js";
import { AuditType } from "../constants/AuditType"; import { AuditType } from "../constants/AuditType";
import { ICommandContext } from "../contracts/ICommandContext"; import EmbedColours from "../constants/EmbedColours";
import ICommandReturnContext from "../contracts/ICommandReturnContext";
import Audit from "../entity/Audit"; import Audit from "../entity/Audit";
import ErrorEmbed from "../helpers/embeds/ErrorEmbed"; import SettingsHelper from "../helpers/SettingsHelper";
import LogEmbed from "../helpers/embeds/LogEmbed";
import PublicEmbed from "../helpers/embeds/PublicEmbed";
import { Command } from "../type/command"; import { Command } from "../type/command";
export default class Warn extends Command { export default class Warn extends Command {
constructor() { constructor() {
super(); super();
super.Category = "Moderation"; super.CommandBuilder = new SlashCommandBuilder()
super.Roles = [ .setName("warn")
"moderator" .setDescription("Warns a member in the server with an optional reason")
]; .setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers)
.addUserOption(option =>
option
.setName('target')
.setDescription('The user')
.setRequired(true))
.addStringOption(option =>
option
.setName('reason')
.setDescription('The reason'));
} }
public override async execute(context: ICommandContext): Promise<ICommandReturnContext> { public override async execute(interaction: CommandInteraction) {
const user = context.message.mentions.users.first(); if (!interaction.guild || !interaction.guildId) return;
if (!user) { const targetUser = interaction.options.get('target');
const errorEmbed = new ErrorEmbed(context, "User does not exist"); const reasonInput = interaction.options.get('reason');
await errorEmbed.SendToCurrentChannel();
return { if (!targetUser || !targetUser.user || !targetUser.member) {
commandContext: context, await interaction.reply('Fields are required.');
embeds: [errorEmbed] return;
};
} }
const member = context.message.guild?.members.cache.find(x => x.user.id == user.id); const targetMember = targetUser.member as GuildMember;
const reason = reasonInput && reasonInput.value ? reasonInput.value.toString() : "*none*";
if (!member) { const logEmbed = new EmbedBuilder()
const errorEmbed = new ErrorEmbed(context, "User is not in this server"); .setColor(EmbedColours.Ok)
await errorEmbed.SendToCurrentChannel(); .setTitle("Member Warned")
.setDescription(`<@${targetUser.user.id}> \`${targetUser.user.tag}\``)
.addFields([
{
name: "Moderator",
value: `<@${interaction.user.id}>`,
},
{
name: "Reason",
value: reason,
},
]);
return { const channelName = await SettingsHelper.GetSetting('channels.logs.mod', interaction.guildId);
commandContext: context,
embeds: [errorEmbed] if (!channelName) return;
};
const channel = interaction.guild.channels.cache.find(x => x.name == channelName) as TextChannel;
if (channel) {
await channel.send({ embeds: [ logEmbed ]});
} }
const reasonArgs = context.args; const audit = new Audit(targetUser.user.id, AuditType.Warn, reason, interaction.user.id, interaction.guildId);
reasonArgs.splice(0, 1); await audit.Save(Audit, audit);
const reason = reasonArgs.join(" ");
if (!context.message.guild?.available) {
return {
commandContext: context,
embeds: []
};
}
const logEmbed = new LogEmbed(context, "Member Warned");
logEmbed.AddUser("User", user, true);
logEmbed.AddUser("Moderator", context.message.author);
logEmbed.AddReason(reason);
const publicEmbed = new PublicEmbed(context, "", `${user} has been warned`);
publicEmbed.AddReason(reason);
await logEmbed.SendToModLogsChannel();
await publicEmbed.SendToCurrentChannel();
if (context.message.guild) {
const audit = new Audit(user.id, AuditType.Warn, reason, context.message.author.id, context.message.guild.id);
await audit.Save(Audit, audit);
}
return {
commandContext: context,
embeds: [logEmbed, publicEmbed]
};
} }
} }

View file

@ -1,7 +0,0 @@
export enum CommandResponse {
Ok,
Unauthorised,
ServerNotSetup,
NotInServer,
FeatureDisabled,
}

View file

@ -0,0 +1,3 @@
export default class EmbedColours {
public static readonly Ok = 0x3050ba;
}

View file

@ -1,27 +0,0 @@
import { CommandResponse } from "./CommandResponse";
export default class ErrorMessages {
public static readonly InsufficientBotPermissions = "Unable to do this action, am I missing permissions?";
public static readonly ChannelNotFound = "Unable to find channel";
public static readonly RoleNotFound = "Unable to find role";
public static readonly UserUnauthorised = "You are not authorised to use this command";
public static readonly ServerNotSetup = "This server hasn't been setup yet, please run the setup command";
public static readonly NotInServer = "This command requires to be ran inside of a server";
public static readonly FeatureDisabled = "This feature is currently disabled by a server moderator";
public static GetErrorMessage(response: CommandResponse): string {
switch (response) {
case CommandResponse.Unauthorised:
return this.UserUnauthorised;
case CommandResponse.ServerNotSetup:
return this.ServerNotSetup;
case CommandResponse.NotInServer:
return this.NotInServer;
case CommandResponse.FeatureDisabled:
return this.FeatureDisabled;
default:
return "";
}
}
}

View file

@ -1,7 +0,0 @@
import { Message } from "discord.js";
export interface ICommandContext {
name: string;
args: string[];
message: Message;
}

View file

@ -1,7 +0,0 @@
import { MessageEmbed } from "discord.js";
import { ICommandContext } from "./ICommandContext";
export default interface ICommandReturnContext {
commandContext: ICommandContext,
embeds: MessageEmbed[]
}

View file

@ -1,6 +0,0 @@
import { MessageEmbed } from "discord.js";
import { ICommandContext } from "./ICommandContext";
export default interface ICommandReturnContext {
embeds: MessageEmbed[]
}

View file

@ -1,4 +1,4 @@
import { Column, Entity, getConnection, ManyToOne } from "typeorm"; import { Column, Entity, getConnection } from "typeorm";
import { AuditType } from "../constants/AuditType"; import { AuditType } from "../constants/AuditType";
import BaseEntity from "../contracts/BaseEntity"; import BaseEntity from "../contracts/BaseEntity";
import StringTools from "../helpers/StringTools"; import StringTools from "../helpers/StringTools";

View file

@ -1,4 +1,4 @@
import { Column, Entity, EntityTarget, getConnection, ManyToOne } from "typeorm"; import { Column, Entity, getConnection, ManyToOne } from "typeorm";
import BaseEntity from "../contracts/BaseEntity" import BaseEntity from "../contracts/BaseEntity"
import Server from "./Server"; import Server from "./Server";
@ -16,6 +16,10 @@ export default class Role extends BaseEntity {
@ManyToOne(() => Server, x => x.Roles) @ManyToOne(() => Server, x => x.Roles)
Server: Server; Server: Server;
public SetServer(server: Server) {
this.Server = server;
}
public static async FetchOneByRoleId(roleId: string, relations?: string[]): Promise<Role | undefined> { public static async FetchOneByRoleId(roleId: string, relations?: string[]): Promise<Role | undefined> {
const connection = getConnection(); const connection = getConnection();

View file

@ -1,8 +1,8 @@
import { Event } from "../type/event"; import { Event } from "../type/event";
import { GuildMember } from "discord.js"; import { EmbedBuilder, GuildMember, TextChannel } from "discord.js";
import EventEmbed from "../helpers/embeds/EventEmbed";
import GuildMemberUpdate from "./MemberEvents/GuildMemberUpdate"; import GuildMemberUpdate from "./MemberEvents/GuildMemberUpdate";
import SettingsHelper from "../helpers/SettingsHelper"; import SettingsHelper from "../helpers/SettingsHelper";
import EmbedColours from "../constants/EmbedColours";
export default class MemberEvents extends Event { export default class MemberEvents extends Event {
constructor() { constructor() {
@ -15,15 +15,29 @@ export default class MemberEvents extends Event {
const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id); const enabled = await SettingsHelper.GetSetting("event.member.add.enabled", member.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return; if (!enabled || enabled.toLowerCase() != "true") return;
const embed = new EventEmbed(member.client, member.guild, "Member Joined"); const embed = new EmbedBuilder()
embed.AddUser("User", member.user, true); .setColor(EmbedColours.Ok)
embed.addField("Created", member.user.createdAt.toISOString()); .setTitle('Member Joined')
embed.setFooter({ text: `Id: ${member.user.id}` }); .setDescription(`${member.user} \`${member.user.tag}\``)
.setFooter({ text: `Id: ${member.user.id}` })
.addFields([
{
name: 'Created',
value: member.user.createdAt.toISOString(),
}
]);
const channel = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id); const channelSetting = await SettingsHelper.GetSetting("event.member.add.channel", member.guild.id);
if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return;
await embed.SendToChannel(channel); if (!channelSetting) return;
const channel = member.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [embed ]});
} }
public override async guildMemberRemove(member: GuildMember) { public override async guildMemberRemove(member: GuildMember) {
@ -32,15 +46,29 @@ export default class MemberEvents extends Event {
const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id); const enabled = await SettingsHelper.GetSetting("event.member.remove.enabled", member.guild.id);
if (!enabled || enabled.toLowerCase() != "true") return; if (!enabled || enabled.toLowerCase() != "true") return;
const embed = new EventEmbed(member.client, member.guild, "Member Left"); const embed = new EmbedBuilder()
embed.AddUser("User", member.user, true); .setColor(EmbedColours.Ok)
embed.addField("Joined", member.joinedAt?.toISOString() || "n/a"); .setTitle('Member Left')
embed.setFooter({ text: `Id: ${member.user.id}` }); .setDescription(`${member.user} \`${member.user.tag}\``)
.setFooter({ text: `Id: ${member.user.id}` })
.addFields([
{
name: 'Joined',
value: member.joinedAt ? member.joinedAt.toISOString() : "*none*",
}
]);
const channel = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id); const channelSetting = await SettingsHelper.GetSetting("event.member.remove.channel", member.guild.id);
if (!channel || !member.guild.channels.cache.find(x => x.name == channel)) return;
await embed.SendToChannel(channel); if (!channelSetting) return;
const channel = member.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [embed ]});
} }
public override async guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) { public override async guildMemberUpdate(oldMember: GuildMember, newMember: GuildMember) {

View file

@ -1,5 +1,5 @@
import { GuildMember } from "discord.js"; import { EmbedBuilder, GuildMember, TextChannel } from "discord.js";
import EventEmbed from "../../helpers/embeds/EventEmbed"; import EmbedColours from "../../constants/EmbedColours";
import SettingsHelper from "../../helpers/SettingsHelper"; import SettingsHelper from "../../helpers/SettingsHelper";
export default class GuildMemberUpdate { export default class GuildMemberUpdate {
@ -18,15 +18,32 @@ export default class GuildMemberUpdate {
const oldNickname = this.oldMember.nickname || "*none*"; const oldNickname = this.oldMember.nickname || "*none*";
const newNickname = this.newMember.nickname || "*none*"; const newNickname = this.newMember.nickname || "*none*";
const embed = new EventEmbed(this.oldMember.client, this.newMember.guild, "Nickname Changed"); const embed = new EmbedBuilder()
embed.AddUser("User", this.newMember.user, true); .setColor(EmbedColours.Ok)
embed.addField("Before", oldNickname, true); .setTitle('Nickname Changed')
embed.addField("After", newNickname, true); .setDescription(`${this.newMember.user} \`${this.newMember.user.tag}\``)
embed.setFooter({ text: `Id: ${this.newMember.user.id}` }); .setFooter({ text: `Id: ${this.newMember.user.id}` })
.addFields([
{
name: 'Before',
value: oldNickname,
},
{
name: 'After',
value: newNickname,
},
]);
const channel = await SettingsHelper.GetSetting("event.member.update.channel", this.newMember.guild.id); const channelSetting = await SettingsHelper.GetSetting("event.member.update.channel", this.newMember.guild.id);
if (!channel || channel.toLowerCase() != "true") return;
await embed.SendToChannel(channel); if (!channelSetting) return;
const channel = this.newMember.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [embed ]});
} }
} }

View file

@ -1,9 +1,9 @@
import { Event } from "../type/event"; import { Event } from "../type/event";
import { Message } from "discord.js"; import { EmbedBuilder, Message, TextChannel } from "discord.js";
import EventEmbed from "../helpers/embeds/EventEmbed";
import SettingsHelper from "../helpers/SettingsHelper"; import SettingsHelper from "../helpers/SettingsHelper";
import OnMessage from "./MessageEvents/OnMessage"; import OnMessage from "./MessageEvents/OnMessage";
import IgnoredChannel from "../entity/IgnoredChannel"; import IgnoredChannel from "../entity/IgnoredChannel";
import EmbedColours from "../constants/EmbedColours";
export default class MessageEvents extends Event { export default class MessageEvents extends Event {
constructor() { constructor() {
@ -20,19 +20,42 @@ export default class MessageEvents extends Event {
const ignored = await IgnoredChannel.IsChannelIgnored(message.channel.id); const ignored = await IgnoredChannel.IsChannelIgnored(message.channel.id);
if (ignored) return; if (ignored) return;
const embed = new EventEmbed(message.client, message.guild, "Message Deleted"); const embed = new EmbedBuilder()
embed.AddUser("User", message.author, true); .setColor(EmbedColours.Ok)
embed.addField("Channel", message.channel.toString(), true); .setTitle("Message Deleted")
embed.addField("Content", `\`\`\`${message.content || "*none*"}\`\`\``); .setDescription(`${message.author} \`${message.author.tag}\``)
.addFields([
{
name: "Channel",
value: message.channel.toString(),
inline: true,
},
{
name: "Content",
value: `\`\`\`${message.content || "*none*"}\`\`\``,
}
]);
if (message.attachments.size > 0) { if (message.attachments.size > 0) {
embed.addField("Attachments", `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``); embed.addFields([
{
name: "Attachments",
value: `\`\`\`${message.attachments.map(x => x.url).join("\n")}\`\`\``
}
]);
} }
const channel = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id); const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", message.guild.id);
if (!channel || !message.guild.channels.cache.find(x => x.name == channel)) return;
await embed.SendToChannel(channel); if (!channelSetting) return;
const channel = message.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [ embed ]});
} }
public override async messageUpdate(oldMessage: Message, newMessage: Message) { public override async messageUpdate(oldMessage: Message, newMessage: Message) {
@ -46,16 +69,37 @@ export default class MessageEvents extends Event {
const ignored = await IgnoredChannel.IsChannelIgnored(newMessage.channel.id); const ignored = await IgnoredChannel.IsChannelIgnored(newMessage.channel.id);
if (ignored) return; if (ignored) return;
const embed = new EventEmbed(newMessage.client, newMessage.guild, "Message Edited"); const embed = new EmbedBuilder()
embed.AddUser("User", newMessage.author, true); .setColor(EmbedColours.Ok)
embed.addField("Channel", newMessage.channel.toString(), true); .setTitle("Message Deleted")
embed.addField("Before", `\`\`\`${oldMessage.content || "*none*"}\`\`\``); .setDescription(`${newMessage.author} \`${newMessage.author.tag}\``)
embed.addField("After", `\`\`\`${newMessage.content || "*none*"}\`\`\``); .addFields([
{
name: "Channel",
value: newMessage.channel.toString(),
inline: true,
},
{
name: "Before",
value: `\`\`\`${oldMessage.content || "*none*"}\`\`\``,
},
{
name: "After",
value: `\`\`\`${newMessage.content || "*none*"}\`\`\``,
}
]);
const channel = await SettingsHelper.GetSetting("event.message.update.channel", newMessage.guild.id); const channelSetting = await SettingsHelper.GetSetting("event.message.delete.channel", newMessage.guild.id);
if (!channel || !newMessage.guild.channels.cache.find(x => x.name == channel)) return;
await embed.SendToChannel(channel); if (!channelSetting) return;
const channel = newMessage.guild.channels.cache.find(x => x.name == channelSetting);
if (!channel) return;
const guildChannel = channel as TextChannel;
await guildChannel.send({ embeds: [ embed ]});
} }
public override async messageCreate(message: Message) { public override async messageCreate(message: Message) {

View file

@ -1,28 +0,0 @@
import { MessageEmbed, Permissions, TextChannel } from "discord.js";
import { ICommandContext } from "../../contracts/ICommandContext";
export default class ErrorEmbed extends MessageEmbed {
public context: ICommandContext;
constructor(context: ICommandContext, message: string) {
super();
super.setColor(0xd52803);
super.setDescription(message);
this.context = context;
}
public async SendToCurrentChannel() {
const channel = this.context.message.channel as TextChannel;
const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! });
if (!botMember) return;
const permissions = channel.permissionsFor(botMember);
if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return;
this.context.message.channel.send({ embeds: [ this ]});
}
}

View file

@ -1,83 +0,0 @@
import { MessageEmbed, TextChannel, User, Guild, Client, Permissions } from "discord.js";
import { ICommandContext } from "../../contracts/ICommandContext";
import SettingsHelper from "../SettingsHelper";
export default class EventEmbed extends MessageEmbed {
public guild: Guild;
private client: Client;
constructor(client: Client, guild: Guild, title: string) {
super();
super.setColor(0x3050ba);
super.setTitle(title);
this.guild = guild;
this.client = client;
}
// Detail methods
public AddUser(title: string, user: User, setThumbnail: boolean = false) {
this.addField(title, `${user} \`${user.tag}\``, true);
if (setThumbnail) {
this.setThumbnail(user.displayAvatarURL());
}
}
public AddReason(message: string) {
this.addField("Reason", message || "*none*");
}
// Send methods
public async SendToChannel(name: string) {
const channel = this.guild.channels.cache
.find(channel => channel.name == name) as TextChannel;
if (!channel) {
console.error(`Unable to find channel ${name}`);
return;
}
const botMember = await this.guild.members.fetch({ user: this.client.user! });
if (!botMember) return;
const permissions = channel.permissionsFor(botMember);
if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return;
channel.send({embeds: [ this ]});
}
public async SendToMessageLogsChannel() {
const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.guild.id);
if (!channelName) {
return;
}
this.SendToChannel(channelName);
}
public async SendToMemberLogsChannel() {
const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.guild.id);
if (!channelName) {
return;
}
this.SendToChannel(channelName);
}
public async SendToModLogsChannel() {
const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.guild.id);
if (!channelName) {
return;
}
this.SendToChannel(channelName);
}
}

View file

@ -1,88 +0,0 @@
import { MessageEmbed, Permissions, TextChannel, User } from "discord.js";
import ErrorMessages from "../../constants/ErrorMessages";
import { ICommandContext } from "../../contracts/ICommandContext";
import SettingsHelper from "../SettingsHelper";
import ErrorEmbed from "./ErrorEmbed";
export default class LogEmbed extends MessageEmbed {
public context: ICommandContext;
constructor(context: ICommandContext, title: string) {
super();
super.setColor(0x3050ba);
super.setTitle(title);
this.context = context;
}
// Detail methods
public AddUser(title: string, user: User, setThumbnail: boolean = false) {
this.addField(title, `${user} \`${user.tag}\``, true);
if (setThumbnail) {
this.setThumbnail(user.displayAvatarURL());
}
}
public AddReason(message: string) {
this.addField("Reason", message || "*none*");
}
// Send methods
public async SendToCurrentChannel() {
const channel = this.context.message.channel as TextChannel;
const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! });
if (!botMember) return;
const permissions = channel.permissionsFor(botMember);
if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return;
this.context.message.channel.send({ embeds: [ this ]});
}
public SendToChannel(name: string) {
const channel = this.context.message.guild?.channels.cache
.find(channel => channel.name == name) as TextChannel;
if (!channel) {
const errorEmbed = new ErrorEmbed(this.context, ErrorMessages.ChannelNotFound);
errorEmbed.SendToCurrentChannel();
return;
}
channel.send({ embeds: [ this ]});
}
public async SendToMessageLogsChannel() {
const channelName = await SettingsHelper.GetSetting("channels.logs.message", this.context.message.guild?.id!);
if (!channelName) {
return;
}
this.SendToChannel(channelName);
}
public async SendToMemberLogsChannel() {
const channelName = await SettingsHelper.GetSetting("channels.logs.member", this.context.message.guild?.id!);
if (!channelName) {
return;
}
this.SendToChannel(channelName);
}
public async SendToModLogsChannel() {
const channelName = await SettingsHelper.GetSetting("channels.logs.mod", this.context.message.guild?.id!);
if (!channelName) {
return;
}
this.SendToChannel(channelName);
}
}

View file

@ -1,35 +0,0 @@
import { MessageEmbed, MessageOptions, Permissions, TextChannel } from "discord.js";
import { ICommandContext } from "../../contracts/ICommandContext";
export default class PublicEmbed extends MessageEmbed {
public context: ICommandContext;
constructor(context: ICommandContext, title: string, description: string) {
super();
super.setColor(0x3050ba);
super.setTitle(title);
super.setDescription(description);
this.context = context;
}
// Detail methods
public AddReason(message: string) {
this.addField("Reason", message || "*none*");
}
// Send methods
public async SendToCurrentChannel(options?: MessageOptions) {
const channel = this.context.message.channel as TextChannel;
const botMember = await this.context.message.guild?.members.fetch({ user: this.context.message.client.user! });
if (!botMember) return;
const permissions = channel.permissionsFor(botMember);
if (!permissions.has(Permissions.FLAGS.SEND_MESSAGES)) return;
this.context.message.channel.send({ embeds: [ this ], ...options});
}
}

View file

@ -8,21 +8,21 @@ import Clear from "./commands/clear";
import Code from "./commands/code"; import Code from "./commands/code";
import Config from "./commands/config"; import Config from "./commands/config";
import Disable from "./commands/disable"; import Disable from "./commands/disable";
import Help from "./commands/help";
import Ignore from "./commands/ignore"; import Ignore from "./commands/ignore";
import Kick from "./commands/kick"; import Kick from "./commands/kick";
import Mute from "./commands/mute"; import Mute from "./commands/mute";
import Poll from "./commands/poll"; import Role from "./commands/Role/role";
import Role from "./commands/role"; import ConfigRole from "./commands/Role/config";
import Rules from "./commands/rules"; import Rules from "./commands/rules";
import Say from "./commands/say";
import Setup from "./commands/setup"; import Setup from "./commands/setup";
import Unmute from "./commands/unmute"; import Unmute from "./commands/unmute";
import Warn from "./commands/warn"; import Warn from "./commands/warn";
// Command Imports: MankBot // Command Imports: MankBot
import Entry from "./commands/501231711271780357/entry"; import Entry from "./commands/501231711271780357/entry";
import Lobby from "./commands/501231711271780357/lobby"; import Lobby from "./commands/501231711271780357/Lobby/lobby";
import AddLobby from "./commands/501231711271780357/Lobby/add";
import RemoveLobby from "./commands/501231711271780357/Lobby/remove";
// Event Imports // Event Imports
import MemberEvents from "./events/MemberEvents"; import MemberEvents from "./events/MemberEvents";
@ -38,25 +38,28 @@ export default class Registry {
CoreClient.RegisterCommand("code", new Code()); CoreClient.RegisterCommand("code", new Code());
CoreClient.RegisterCommand("config", new Config()); CoreClient.RegisterCommand("config", new Config());
CoreClient.RegisterCommand("disable", new Disable()); CoreClient.RegisterCommand("disable", new Disable());
CoreClient.RegisterCommand("help", new Help());
CoreClient.RegisterCommand("ignore", new Ignore()); CoreClient.RegisterCommand("ignore", new Ignore());
CoreClient.RegisterCommand("kick", new Kick()); CoreClient.RegisterCommand("kick", new Kick());
CoreClient.RegisterCommand("mute", new Mute()); CoreClient.RegisterCommand("mute", new Mute());
CoreClient.RegisterCommand("poll", new Poll());
CoreClient.RegisterCommand("role", new Role());
CoreClient.RegisterCommand("rules", new Rules()); CoreClient.RegisterCommand("rules", new Rules());
CoreClient.RegisterCommand("unmute", new Unmute()); CoreClient.RegisterCommand("unmute", new Unmute());
CoreClient.RegisterCommand("warn", new Warn()); CoreClient.RegisterCommand("warn", new Warn());
CoreClient.RegisterCommand("setup", new Setup()); CoreClient.RegisterCommand("setup", new Setup());
CoreClient.RegisterCommand("say", new Say());
CoreClient.RegisterCommand("audits", new Audits()); CoreClient.RegisterCommand("audits", new Audits());
CoreClient.RegisterCommand("role", new Role());
CoreClient.RegisterCommand("configrole", new ConfigRole());
// Exclusive Commands: MankBot // Exclusive Commands: MankBot
CoreClient.RegisterCommand("lobby", new Lobby(), "501231711271780357"); CoreClient.RegisterCommand("lobby", new Lobby(), "501231711271780357");
CoreClient.RegisterCommand("lobbyAdd", new AddLobby(), "501231711271780357");
CoreClient.RegisterCommand("lobbyRemove", new RemoveLobby(), "501231711271780357");
CoreClient.RegisterCommand("entry", new Entry(), "501231711271780357"); CoreClient.RegisterCommand("entry", new Entry(), "501231711271780357");
// Add Exclusive Commands to Test Server // Add Exclusive Commands to Test Server
CoreClient.RegisterCommand("lobby", new Lobby(), "442730357897429002"); CoreClient.RegisterCommand("lobby", new Lobby(), "442730357897429002");
CoreClient.RegisterCommand("addlobby", new AddLobby(), "442730357897429002");
CoreClient.RegisterCommand("removelobby", new RemoveLobby(), "442730357897429002");
CoreClient.RegisterCommand("entry", new Entry(), "442730357897429002"); CoreClient.RegisterCommand("entry", new Entry(), "442730357897429002");
} }

View file

@ -1,23 +1,9 @@
import { CommandResponse } from "../constants/CommandResponse"; import { CommandInteraction } from "discord.js";
import { ICommandContext } from "../contracts/ICommandContext";
export class Command { export class Command {
public Roles: string[]; public CommandBuilder: any;
public Category?: string;
constructor() { public execute(interaction: CommandInteraction) {
this.Roles = [];
}
public precheck(context: ICommandContext): CommandResponse {
return CommandResponse.Ok;
}
public async precheckAsync(context: ICommandContext): Promise<CommandResponse> {
return CommandResponse.Ok;
}
public execute(context: ICommandContext) {
} }
} }

View file

@ -1,7 +1,7 @@
import { CoreClient } from "./client/client"; import { CoreClient } from "./client/client";
import * as dotenv from "dotenv"; import * as dotenv from "dotenv";
import registry from "./registry"; import registry from "./registry";
import { Intents } from "discord.js"; import { IntentsBitField } from "discord.js";
dotenv.config(); dotenv.config();
@ -9,8 +9,8 @@ const requiredConfigs: string[] = [
"BOT_TOKEN", "BOT_TOKEN",
"BOT_VER", "BOT_VER",
"BOT_AUTHOR", "BOT_AUTHOR",
"BOT_DATE",
"BOT_OWNERID", "BOT_OWNERID",
"BOT_CLIENTID",
]; ];
requiredConfigs.forEach(config => { requiredConfigs.forEach(config => {
@ -20,9 +20,10 @@ requiredConfigs.forEach(config => {
}); });
const client = new CoreClient([ const client = new CoreClient([
Intents.FLAGS.GUILDS, IntentsBitField.Flags.Guilds,
Intents.FLAGS.GUILD_MESSAGES, IntentsBitField.Flags.GuildMessages,
Intents.FLAGS.GUILD_MEMBERS, IntentsBitField.Flags.GuildMembers,
IntentsBitField.Flags.MessageContent,
]); ]);
registry.RegisterCommands(); registry.RegisterCommands();

248
yarn.lock
View file

@ -286,21 +286,34 @@
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==
"@discordjs/builders@^0.11.0": "@discordjs/builders@^1.2.0":
version "0.11.0" version "1.2.0"
resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-0.11.0.tgz#4102abe3e0cd093501f3f71931df43eb92f5b0cc" resolved "https://registry.yarnpkg.com/@discordjs/builders/-/builders-1.2.0.tgz#4f5059e258c30a26931ae384985ef8c6b371ba05"
integrity sha512-ZTB8yJdJKrKlq44dpWkNUrAtEJEq0gqpb7ASdv4vmq6/mZal5kOv312hQ56I/vxwMre+VIkoHquNUAfnTbiYtg== integrity sha512-ARy4BUTMU+S0ZI6605NDqfWO+qZqV2d/xfY32z3hVSsd9IaAKJBZ1ILTZLy87oIjW8+gUpQmk9Kt0ZP9bmmd8Q==
dependencies: dependencies:
"@sindresorhus/is" "^4.2.0" "@sapphire/shapeshift" "^3.5.1"
discord-api-types "^0.26.0" discord-api-types "^0.37.3"
ts-mixer "^6.0.0" fast-deep-equal "^3.1.3"
tslib "^2.3.1" ts-mixer "^6.0.1"
zod "^3.11.6" tslib "^2.4.0"
"@discordjs/collection@^0.4.0": "@discordjs/collection@^1.0.1", "@discordjs/collection@^1.1.0":
version "0.4.0" version "1.1.0"
resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-0.4.0.tgz#b6488286a1cc7b41b644d7e6086f25a1c1e6f837" resolved "https://registry.yarnpkg.com/@discordjs/collection/-/collection-1.1.0.tgz#5f9f926404fd48ccde86a0d2268f202cbec77833"
integrity sha512-zmjq+l/rV35kE6zRrwe8BHqV78JvIh2ybJeZavBi5NySjWXqN3hmmAKg7kYMMXSeiWtSsMoZ/+MQi0DiQWy2lw== integrity sha512-PQ2Bv6pnT7aGPCKWbvvNRww5tYCGpggIQVgpuF9TdDPeR6n6vQYxezXiLVOS9z2B62Dp4c+qepQ15SgJbLYtCQ==
"@discordjs/rest@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@discordjs/rest/-/rest-1.1.0.tgz#5c283571a22b911ca334316245487af40baffe8b"
integrity sha512-yCrthRTQeUyNThQEpCk7bvQJlwQmz6kU0tf3dcWBv2WX3Bncl41x7Wc+v5b5OsIxfNYq38PvVtWircu9jtYZug==
dependencies:
"@discordjs/collection" "^1.0.1"
"@sapphire/async-queue" "^1.5.0"
"@sapphire/snowflake" "^3.2.2"
discord-api-types "^0.37.3"
file-type "^17.1.6"
tslib "^2.4.0"
undici "^5.9.1"
"@istanbuljs/load-nyc-config@^1.0.0": "@istanbuljs/load-nyc-config@^1.0.0":
version "1.1.0" version "1.1.0"
@ -487,12 +500,25 @@
"@types/yargs" "^16.0.0" "@types/yargs" "^16.0.0"
chalk "^4.0.0" chalk "^4.0.0"
"@sapphire/async-queue@^1.1.9": "@sapphire/async-queue@^1.5.0":
version "1.3.1" version "1.5.0"
resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.3.1.tgz#9d861e626dbffae02d808e13f823d4510e450a78" resolved "https://registry.yarnpkg.com/@sapphire/async-queue/-/async-queue-1.5.0.tgz#2f255a3f186635c4fb5a2381e375d3dfbc5312d8"
integrity sha512-FFTlPOWZX1kDj9xCAsRzH5xEJfawg1lNoYAA+ecOWJMHOfiZYb1uXOI3ne9U4UILSEPwfE68p3T9wUHwIQfR0g== integrity sha512-JkLdIsP8fPAdh9ZZjrbHWR/+mZj0wvKS5ICibcLrRI1j84UmLMshx5n9QmL8b95d4onJ2xxiyugTgSAX7AalmA==
"@sindresorhus/is@^4.0.0", "@sindresorhus/is@^4.2.0": "@sapphire/shapeshift@^3.5.1":
version "3.6.0"
resolved "https://registry.yarnpkg.com/@sapphire/shapeshift/-/shapeshift-3.6.0.tgz#988ff6576162a581a29bc26deb5492f7d1bf419f"
integrity sha512-tu2WLRdo5wotHRvsCkspg3qMiP6ETC3Q1dns1Q5V6zKUki+1itq6AbhMwohF9ZcLoYqg+Y8LkgRRtVxxTQVTBQ==
dependencies:
fast-deep-equal "^3.1.3"
lodash.uniqwith "^4.5.0"
"@sapphire/snowflake@^3.2.2":
version "3.2.2"
resolved "https://registry.yarnpkg.com/@sapphire/snowflake/-/snowflake-3.2.2.tgz#faacdc1b5f7c43145a71eddba917de2b707ef780"
integrity sha512-ula2O0kpSZtX9rKXNeQMrHwNd7E4jPDJYUXmEGTFdMRfyfMw+FPyh04oKMjAiDuOi64bYgVkOV3MjK+loImFhQ==
"@sindresorhus/is@^4.0.0":
version "4.6.0" version "4.6.0"
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f" resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-4.6.0.tgz#3c7c9c46e678feefe7a2e5bb609d3dbd665ffb3f"
integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw== integrity sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==
@ -523,6 +549,11 @@
dependencies: dependencies:
defer-to-connect "^2.0.0" defer-to-connect "^2.0.0"
"@tokenizer/token@^0.3.0":
version "0.3.0"
resolved "https://registry.yarnpkg.com/@tokenizer/token/-/token-0.3.0.tgz#fe98a93fe789247e998c75e74e9c7c63217aa276"
integrity sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==
"@tootallnate/once@1": "@tootallnate/once@1":
version "1.1.2" version "1.1.2"
resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82" resolved "https://registry.yarnpkg.com/@tootallnate/once/-/once-1.1.2.tgz#ccb91445360179a04e7fe6aff78c00ffc1eeaf82"
@ -622,14 +653,6 @@
dependencies: dependencies:
"@types/node" "*" "@types/node" "*"
"@types/node-fetch@^2.5.12":
version "2.6.1"
resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975"
integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA==
dependencies:
"@types/node" "*"
form-data "^3.0.0"
"@types/node@*": "@types/node@*":
version "18.0.0" version "18.0.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a" resolved "https://registry.yarnpkg.com/@types/node/-/node-18.0.0.tgz#67c7b724e1bcdd7a8821ce0d5ee184d3b4dd525a"
@ -662,7 +685,7 @@
resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc" resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-8.3.4.tgz#bd86a43617df0594787d38b735f55c805becf1bc"
integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw== integrity sha512-c/I8ZRb51j+pYGAu5CrFMRxqZ2ke4y2grEBO5AUjgSkSk+qT2Ea+OdWElz/OiMf5MNpn2b17kuVBwZLQJXzihw==
"@types/ws@^8.2.2": "@types/ws@^8.5.3":
version "8.5.3" version "8.5.3"
resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-8.5.3.tgz#7d25a1ffbecd3c4f2d35068d0b283c037003274d"
integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w== integrity sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==
@ -1181,25 +1204,27 @@ diff-sequences@^27.4.0:
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5" resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-27.4.0.tgz#d783920ad8d06ec718a060d00196dfef25b132a5"
integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww== integrity sha512-YqiQzkrsmHMH5uuh8OdQFU9/ZpADnwzml8z0O5HvRNda+5UZsaX/xN+AAxfR2hWq1Y7HZnAzO9J5lJXOuDz2Ww==
discord-api-types@^0.26.0: discord-api-types@^0.37.3:
version "0.26.1" version "0.37.9"
resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.26.1.tgz#726f766ddc37d60da95740991d22cb6ef2ed787b" resolved "https://registry.yarnpkg.com/discord-api-types/-/discord-api-types-0.37.9.tgz#3ceba0f047c5239967c50b180fe9d72c55c82464"
integrity sha512-T5PdMQ+Y1MEECYMV5wmyi9VEYPagEDEi4S0amgsszpWY0VB9JJ/hEvM6BgLhbdnKky4gfmZEXtEEtojN8ZKJQQ== integrity sha512-mAJv7EcDDo6YztR3XSIED2IAHJcAlzWFD31Q2cHLURMKPb0CW0TM/r32HhAHO9uHw74N3cviOzJIZfZVB/e/5A==
discord.js@^13.6.0: discord.js@^14.3.0:
version "13.6.0" version "14.3.0"
resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-13.6.0.tgz#d8a8a591dbf25cbcf9c783d5ddf22c4694860475" resolved "https://registry.yarnpkg.com/discord.js/-/discord.js-14.3.0.tgz#9e43df7e4d2d14b11f3de751236e983159c3d3d0"
integrity sha512-tXNR8zgsEPxPBvGk3AQjJ9ljIIC6/LOPjzKwpwz8Y1Q2X66Vi3ZqFgRHYwnHKC0jC0F+l4LzxlhmOJsBZDNg9g== integrity sha512-CpIwoAAuELiHSgVKRMzsCADS6ZlJwAZ9RlvcJYdEgS00aW36dSvXyBgE+S3pigkc7G+jU6BEalMUWIJFveqrBQ==
dependencies: dependencies:
"@discordjs/builders" "^0.11.0" "@discordjs/builders" "^1.2.0"
"@discordjs/collection" "^0.4.0" "@discordjs/collection" "^1.1.0"
"@sapphire/async-queue" "^1.1.9" "@discordjs/rest" "^1.1.0"
"@types/node-fetch" "^2.5.12" "@sapphire/snowflake" "^3.2.2"
"@types/ws" "^8.2.2" "@types/ws" "^8.5.3"
discord-api-types "^0.26.0" discord-api-types "^0.37.3"
form-data "^4.0.0" fast-deep-equal "^3.1.3"
node-fetch "^2.6.1" lodash.snakecase "^4.1.1"
ws "^8.4.0" tslib "^2.4.0"
undici "^5.9.1"
ws "^8.8.1"
domexception@^2.0.1: domexception@^2.0.1:
version "2.0.1" version "2.0.1"
@ -1319,6 +1344,11 @@ expect@^27.4.2:
jest-message-util "^27.4.2" jest-message-util "^27.4.2"
jest-regex-util "^27.4.0" jest-regex-util "^27.4.0"
fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0: fast-json-stable-stringify@2.x, fast-json-stable-stringify@^2.0.0:
version "2.1.0" version "2.1.0"
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
@ -1336,6 +1366,15 @@ fb-watchman@^2.0.0:
dependencies: dependencies:
bser "2.1.1" bser "2.1.1"
file-type@^17.1.6:
version "17.1.6"
resolved "https://registry.yarnpkg.com/file-type/-/file-type-17.1.6.tgz#18669e0577a4849ef6e73a41f8bdf1ab5ae21023"
integrity sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==
dependencies:
readable-web-to-node-stream "^3.0.2"
strtok3 "^7.0.0-alpha.9"
token-types "^5.0.0-alpha.2"
fill-range@^7.0.1: fill-range@^7.0.1:
version "7.0.1" version "7.0.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
@ -1360,15 +1399,6 @@ form-data@^3.0.0:
combined-stream "^1.0.8" combined-stream "^1.0.8"
mime-types "^2.1.12" mime-types "^2.1.12"
form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"
fs.realpath@^1.0.0: fs.realpath@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
@ -1559,7 +1589,7 @@ inflight@^1.0.4:
once "^1.3.0" once "^1.3.0"
wrappy "1" wrappy "1"
inherits@2, inherits@^2.0.1, inherits@~2.0.3: inherits@2, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.3:
version "2.0.4" version "2.0.4"
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
@ -2201,6 +2231,16 @@ lodash.memoize@4.x:
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe" resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4= integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
lodash.snakecase@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz#39d714a35357147837aefd64b5dcbb16becd8f8d"
integrity sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==
lodash.uniqwith@^4.5.0:
version "4.5.0"
resolved "https://registry.yarnpkg.com/lodash.uniqwith/-/lodash.uniqwith-4.5.0.tgz#7a0cbf65f43b5928625a9d4d0dc54b18cadc7ef3"
integrity sha512-7lYL8bLopMoy4CTICbxygAUq6CdRJ36vFc80DucPueUee+d5NBRxz3FdT9Pes/HEx5mPoT9jwnsEJWz1N7uq7Q==
lodash@^4.7.0: lodash@^4.7.0:
version "4.17.21" version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
@ -2323,13 +2363,6 @@ natural-compare@^1.4.0:
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc= integrity sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=
node-fetch@^2.6.1:
version "2.6.7"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
dependencies:
whatwg-url "^5.0.0"
node-int64@^0.4.0: node-int64@^0.4.0:
version "0.4.0" version "0.4.0"
resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b" resolved "https://registry.yarnpkg.com/node-int64/-/node-int64-0.4.0.tgz#87a9065cdb355d3182d8f94ce11188b825c68a3b"
@ -2454,6 +2487,11 @@ path-parse@^1.0.6:
resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735"
integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==
peek-readable@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/peek-readable/-/peek-readable-5.0.0.tgz#7ead2aff25dc40458c60347ea76cfdfd63efdfec"
integrity sha512-YtCKvLUOvwtMGmrniQPdO7MwPjgkFBtFIrmfSbYmYuq3tKDV/mcfAhBth1+C3ru7uXIZasc/pHnb+YDYNkkj4A==
picocolors@^1.0.0: picocolors@^1.0.0:
version "1.0.0" version "1.0.0"
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
@ -2553,6 +2591,22 @@ readable-stream@2.3.7:
string_decoder "~1.1.1" string_decoder "~1.1.1"
util-deprecate "~1.0.1" util-deprecate "~1.0.1"
readable-stream@^3.6.0:
version "3.6.0"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198"
integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
readable-web-to-node-stream@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz#5d52bb5df7b54861fd48d015e93a2cb87b3ee0bb"
integrity sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==
dependencies:
readable-stream "^3.6.0"
reflect-metadata@^0.1.13: reflect-metadata@^0.1.13:
version "0.1.13" version "0.1.13"
resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08"
@ -2612,7 +2666,7 @@ safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1:
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==
safe-buffer@^5.0.1: safe-buffer@^5.0.1, safe-buffer@~5.2.0:
version "5.2.1" version "5.2.1"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
@ -2738,6 +2792,13 @@ string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
is-fullwidth-code-point "^3.0.0" is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1" strip-ansi "^6.0.1"
string_decoder@^1.1.1:
version "1.3.0"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==
dependencies:
safe-buffer "~5.2.0"
string_decoder@~1.1.1: string_decoder@~1.1.1:
version "1.1.1" version "1.1.1"
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
@ -2762,6 +2823,14 @@ strip-final-newline@^2.0.0:
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad" resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA== integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
strtok3@^7.0.0-alpha.9:
version "7.0.0"
resolved "https://registry.yarnpkg.com/strtok3/-/strtok3-7.0.0.tgz#868c428b4ade64a8fd8fee7364256001c1a4cbe5"
integrity sha512-pQ+V+nYQdC5H3Q7qBZAz/MO6lwGhoC2gOAjuouGf/VO0m7vQRh8QNMl2Uf6SwAtzZ9bOw3UIeBukEGNJl5dtXQ==
dependencies:
"@tokenizer/token" "^0.3.0"
peek-readable "^5.0.0"
supports-color@^5.3.0: supports-color@^5.3.0:
version "5.5.0" version "5.5.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@ -2849,6 +2918,14 @@ to-regex-range@^5.0.1:
dependencies: dependencies:
is-number "^7.0.0" is-number "^7.0.0"
token-types@^5.0.0-alpha.2:
version "5.0.1"
resolved "https://registry.yarnpkg.com/token-types/-/token-types-5.0.1.tgz#aa9d9e6b23c420a675e55413b180635b86a093b4"
integrity sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==
dependencies:
"@tokenizer/token" "^0.3.0"
ieee754 "^1.2.1"
tough-cookie@^4.0.0: tough-cookie@^4.0.0:
version "4.0.0" version "4.0.0"
resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-4.0.0.tgz#d822234eeca882f991f0f908824ad2622ddbece4"
@ -2865,11 +2942,6 @@ tr46@^2.1.0:
dependencies: dependencies:
punycode "^2.1.1" punycode "^2.1.1"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
ts-essentials@^7.0.3: ts-essentials@^7.0.3:
version "7.0.3" version "7.0.3"
resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" resolved "https://registry.yarnpkg.com/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38"
@ -2889,16 +2961,21 @@ ts-jest@^27.1.2:
semver "7.x" semver "7.x"
yargs-parser "20.x" yargs-parser "20.x"
ts-mixer@^6.0.0: ts-mixer@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.1.tgz#7c2627fb98047eb5f3c7f2fee39d1521d18fe87a" resolved "https://registry.yarnpkg.com/ts-mixer/-/ts-mixer-6.0.1.tgz#7c2627fb98047eb5f3c7f2fee39d1521d18fe87a"
integrity sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg== integrity sha512-hvE+ZYXuINrx6Ei6D6hz+PTim0Uf++dYbK9FFifLNwQj+RwKquhQpn868yZsCtJYiclZF1u8l6WZxxKi+vv7Rg==
tslib@^2.1.0, tslib@^2.3.1: tslib@^2.1.0:
version "2.3.1" version "2.3.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
tslib@^2.4.0:
version "2.4.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3"
integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==
type-check@~0.3.2: type-check@~0.3.2:
version "0.3.2" version "0.3.2"
resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72"
@ -2951,12 +3028,17 @@ typescript@^4.5.2:
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998" resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.2.tgz#8ac1fba9f52256fdb06fb89e4122fa6a346c2998"
integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw== integrity sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==
undici@^5.9.1:
version "5.10.0"
resolved "https://registry.yarnpkg.com/undici/-/undici-5.10.0.tgz#dd9391087a90ccfbd007568db458674232ebf014"
integrity sha512-c8HsD3IbwmjjbLvoZuRI26TZic+TSEe8FPMLLOkN1AfYRhdjnKBU6yL+IwcSCbdZiX4e5t0lfMDLDCqj4Sq70g==
universalify@^0.1.2: universalify@^0.1.2:
version "0.1.2" version "0.1.2"
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==
util-deprecate@~1.0.1: util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2" version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
@ -2996,11 +3078,6 @@ walker@^1.0.7:
dependencies: dependencies:
makeerror "1.0.12" makeerror "1.0.12"
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
webidl-conversions@^5.0.0: webidl-conversions@^5.0.0:
version "5.0.0" version "5.0.0"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff" resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-5.0.0.tgz#ae59c8a00b121543a2acc65c0434f57b0fc11aff"
@ -3023,14 +3100,6 @@ whatwg-mimetype@^2.3.0:
resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf" resolved "https://registry.yarnpkg.com/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz#3d4b1e0312d2079879f826aff18dbeeca5960fbf"
integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g== integrity sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
whatwg-url@^8.0.0, whatwg-url@^8.5.0: whatwg-url@^8.0.0, whatwg-url@^8.5.0:
version "8.7.0" version "8.7.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77" resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-8.7.0.tgz#656a78e510ff8f3937bc0bcbe9f5c0ac35941b77"
@ -3081,10 +3150,10 @@ ws@^7.4.6:
resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b" resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.6.tgz#e59fc509fb15ddfb65487ee9765c5a51dec5fe7b"
integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA== integrity sha512-6GLgCqo2cy2A2rjCNFlxQS6ZljG/coZfZXclldI8FB/1G3CCI36Zd8xy2HrFVACi8tfk5XrgLQEk+P0Tnz9UcA==
ws@^8.4.0: ws@^8.8.1:
version "8.5.0" version "8.8.1"
resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0"
integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA==
xml-name-validator@^3.0.0: xml-name-validator@^3.0.0:
version "3.0.0" version "3.0.0"
@ -3167,8 +3236,3 @@ zen-observable@0.8.15:
version "0.8.15" version "0.8.15"
resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15" resolved "https://registry.yarnpkg.com/zen-observable/-/zen-observable-0.8.15.tgz#96415c512d8e3ffd920afd3889604e30b9eaac15"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ== integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
zod@^3.11.6:
version "3.14.4"
resolved "https://registry.yarnpkg.com/zod/-/zod-3.14.4.tgz#e678fe9e5469f4663165a5c35c8f3dc66334a5d6"
integrity sha512-U9BFLb2GO34Sfo9IUYp0w3wJLlmcyGoMd75qU9yf+DrdGA4kEx6e+l9KOkAlyUO0PSQzZCa3TR4qVlcmwqSDuw==