Feature/81 slash command support (#192)

* Update discord.js

* Migrate to slash commands

* Clean up imports

* Update permissions

* Fix guild-specific commands not showing up

* Fix changes requested
This commit is contained in:
Vylpes 2022-09-18 11:57:22 +01:00 committed by GitHub
parent 0465697b87
commit ed8f5927c8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
52 changed files with 1469 additions and 1850 deletions

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 PublicEmbed from "../../helpers/embeds/PublicEmbed";
import { CommandInteraction, EmbedBuilder, PermissionsBitField, SlashCommandBuilder } from "discord.js";
import EmbedColours from "../../constants/EmbedColours";
import SettingsHelper from "../../helpers/SettingsHelper";
import { Command } from "../../type/command";
@ -7,19 +7,23 @@ export default class Entry extends Command {
constructor() {
super();
super.Category = "Moderation";
super.Roles = [
"moderator"
];
super.CommandBuilder = new SlashCommandBuilder()
.setName('entry')
.setDescription('Sends the entry embed')
.setDefaultMemberPermissions(PermissionsBitField.Flags.ModerateMembers);
}
public override async execute(context: ICommandContext) {
if (!context.message.guild) return;
public override async execute(interaction: CommandInteraction) {
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();
}
}