diff --git a/util/slash.d.ts b/util/slash.d.ts new file mode 100644 index 0000000..b418892 --- /dev/null +++ b/util/slash.d.ts @@ -0,0 +1,25 @@ +import { REST } from '@discordjs/rest'; +import { SlashCommandBuilder } from '@discordjs/builders'; +import { Client, CommandInteraction } from 'discord.js'; +export declare class SlashCommand { + response: (client: Client, interaction: CommandInteraction) => any; + command: SlashCommandBuilder; + registerMode: RegisterMode; + enabled: boolean; + readonly name: string; + readonly client: Client; + readonly rest: REST; + constructor(name: string, client: Client, command: SlashCommandBuilder, response: (client: Client, interaction: CommandInteraction) => any, registerMode?: RegisterMode); + register(forceMode?: RegisterMode): Promise; + registerGlobally(): Promise; + registerToServer(serverIDs: string[]): Promise; + setResponse(newResponse: (client: Client, interaction: CommandInteraction) => any): SlashCommand; + disable(): Promise; + setDefaultRegisterMode(mode: RegisterMode): SlashCommand; + respond(client: Client, interaction: CommandInteraction): Promise; + setCommand(newCommand: SlashCommandBuilder): SlashCommand; +} +export declare type RegisterMode = { + serverIDs: string[]; + global?: boolean; +} | "global"; diff --git a/util/slash.js b/util/slash.js new file mode 100644 index 0000000..c4e2106 --- /dev/null +++ b/util/slash.js @@ -0,0 +1,66 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SlashCommand = void 0; +const rest_1 = require("@discordjs/rest"); +const v9_1 = require("discord-api-types/v9"); +class SlashCommand { + constructor(name, client, command, response, registerMode) { + this.registerMode = 'global'; + this.enabled = false; + this.name = name; + this.command = command; + this.response = response; + this.client = client; + if (registerMode) { + this.registerMode = registerMode; + } + this.rest = new rest_1.REST({ version: '9' }).setToken(this.client.token); + } + async register(forceMode) { + let mode = typeof forceMode !== 'undefined' ? forceMode : this.registerMode; + if (mode === 'global' || mode.global) { + return this.rest.put(v9_1.Routes.applicationCommands(this.client.user.id), { body: [this.command.toJSON()] }); + } + else { + if (Array.isArray(mode.serverIDs) && mode.serverIDs.length !== 1) { + let res = []; + mode.serverIDs.forEach(id => res.push(this.rest.put(v9_1.Routes.applicationGuildCommands(this.client.user.id, id), { body: [this.command.toJSON()] }))); + return Promise.all(res); + } + else { + return this.rest.put(v9_1.Routes.applicationGuildCommands(this.client.user.id, Array.isArray(mode.serverIDs) ? mode.serverIDs[0] : mode.serverIDs), { body: [this.command.toJSON()] }); + } + } + } + ; + async registerGlobally() { + return await this.register('global'); + } + ; + async registerToServer(serverIDs) { + return await this.register({ serverIDs: serverIDs }); + } + ; + setResponse(newResponse) { + this.response = newResponse; + return this; + } + ; + async disable() { } + ; + setDefaultRegisterMode(mode) { + this.registerMode = mode; + return this; + } + ; + async respond(client, interaction) { + return this.response(client, interaction); + } + ; + setCommand(newCommand) { + this.command = newCommand; + return this; + } +} +exports.SlashCommand = SlashCommand; +; diff --git a/util/slashmanager.d.ts b/util/slashmanager.d.ts new file mode 100644 index 0000000..a273946 --- /dev/null +++ b/util/slashmanager.d.ts @@ -0,0 +1,27 @@ +import Discord = require("discord.js"); +import { RegisterMode, SlashCommand } from "./slash"; +export declare class SlashManager { + client: Discord.Client; + commands: SlashCommand[]; + testServerId: string; + beforeHandle: (client: Discord.Client, interaction: Discord.CommandInteraction) => any; + afterHandle: (client: Discord.Client, interaction: Discord.CommandInteraction, success: boolean) => any; + private initialized; + private rest; + constructor(client: Discord.Client, commands?: SlashCommand[], testServer?: string); + register(commands?: CommandLookup): Promise; + devRegister(commands?: CommandLookup): Promise; + add(command: SlashCommand, register?: boolean | RegisterMode): SlashManager; + remove(commands: CommandLookup): SlashManager; + getCommand(command: SlashCommand | string): number; + getCommands(commands: SlashCommand[] | string[]): number[]; + init(): SlashManager; + disableHandling(): SlashManager; + setBeforeHandle(execute: (client: Discord.Client, interaction: Discord.CommandInteraction) => any): SlashManager; + setAfterHandle(execute: (client: Discord.Client, interaction: Discord.CommandInteraction, success: Boolean) => any): SlashManager; + setTestServer(id: string): SlashManager; + private handle; + importCommands(dir?: string): SlashManager; +} +declare type CommandLookup = SlashCommand[] | SlashCommand | string | string[]; +export {}; diff --git a/util/slashmanager.js b/util/slashmanager.js new file mode 100644 index 0000000..b685582 --- /dev/null +++ b/util/slashmanager.js @@ -0,0 +1,136 @@ +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +exports.SlashManager = void 0; +const fs = require("fs"); +const rest_1 = require("@discordjs/rest"); +const v9_1 = require("discord-api-types/v9"); +class SlashManager { + constructor(client, commands, testServer) { + this.beforeHandle = () => { }; + this.afterHandle = () => { }; + this.initialized = false; + this.client = client; + this.commands = commands || []; + if (this.testServerId) { + this.testServerId = testServer; + } + this.rest = new rest_1.REST({ version: '9' }).setToken(this.client.token); + } + async register(commands) { + let trp = commands || this.commands; + let tr = Array.isArray(trp) ? this.getCommands(trp) : this.getCommand(trp); + return Array.isArray(tr) + ? this.rest.put(v9_1.Routes.applicationCommands(this.client.user.id), { body: [(() => { let t = []; tr.forEach(trt => t.push(this.commands[trt].command.toJSON())); return t; })()] }) + : this.commands[tr].registerGlobally(); + } + ; + async devRegister(commands) { + if (!this.testServerId) { + throw new Error("You tried to register commands to your test server, but don't have a test server ID set. Try running SlashManager#setTestServer() first!"); + } + let trp = commands || this.commands; + let tr = Array.isArray(trp) ? this.getCommands(trp) : this.getCommand(trp); + return Array.isArray(tr) + ? this.rest.put(v9_1.Routes.applicationGuildCommands(this.client.user.id, this.testServerId), { body: (() => { let t = []; tr.forEach(trt => t.push(this.commands[trt].command.toJSON())); return t; })() }) + : this.commands[tr].registerToServer([this.testServerId]); + } + ; + add(command, register) { + this.commands.push(command); + if (register) { + command.register(typeof register === 'boolean' ? 'global' : register); + } + return this; + } + ; + remove(commands) { + let trp = commands || this.commands; + let tr = Array.isArray(trp) ? this.getCommands(trp) : this.getCommand(trp); + if (Array.isArray(tr)) { + tr.forEach(t => { this.commands.splice(t, 1); }); + } + else { + this.commands.splice(tr, 1); + } + return this; + } + ; + getCommand(command) { + let res; + if (typeof command === "string") { + this.commands.forEach(cmd => { if (cmd.name === command) { + res = cmd; + } }); + if (!res) { + throw new Error(`Name '${command}' doesn't match any commands in your SlashManager.`); + } + } + else { + if (!this.commands.includes(command)) { + throw new Error(`The command you provided (with name '${command.name}') doesn't exist`); + } + res = command; + } + return this.commands.indexOf(res); + } + ; + getCommands(commands) { + let res = []; + commands.forEach((cmd) => res.push(this.getCommand(cmd))); + return res; + } + ; + init() { + if (this.initialized) { + this.disableHandling(); + } + this.client.on("interactionCreate", (interaction) => this.handle(interaction)); + return this; + } + ; + disableHandling() { + this.client.removeListener('interactionCreate', this.handle); + return this; + } + ; + setBeforeHandle(execute) { + this.beforeHandle = execute; + this.init(); + return this; + } + ; + setAfterHandle(execute) { + this.afterHandle = execute; + this.init(); + return this; + } + ; + setTestServer(id) { + this.testServerId = id; + return this; + } + ; + async handle(interaction) { + this.beforeHandle(this.client, interaction); + let success = true; + try { + await this.commands[this.getCommand(interaction.commandName)].respond(this.client, interaction).catch((e) => { console.log(e); success = false; }); + } + catch (e) { + console.log(e); + success = false; + } + this.afterHandle(this.client, interaction, success); + } + ; + importCommands(dir) { + dir = dir || './slash'; + const commands = fs.readdirSync(dir).filter(file => file.endsWith('.js')); + for (const command of commands) { + this.add(require(`../${dir}/${command}`)(this.client)); + } + return this; + } + ; +} +exports.SlashManager = SlashManager; diff --git a/util/ts/slash.ts b/util/ts/slash.ts new file mode 100644 index 0000000..476b425 --- /dev/null +++ b/util/ts/slash.ts @@ -0,0 +1,79 @@ +import {REST} from '@discordjs/rest'; +import {Routes} from 'discord-api-types/v9'; +import {SlashCommandBuilder} from '@discordjs/builders'; +import {Client, CommandInteraction} from 'discord.js'; + +export class SlashCommand { + + response: (client: Client, interaction: CommandInteraction) => any; + command: SlashCommandBuilder; + registerMode: RegisterMode = 'global'; + enabled: boolean = false; + + readonly name: string; + readonly client: Client; + readonly rest: REST; + + + + constructor(name: string, client: Client, command: SlashCommandBuilder, response: (client: Client, interaction: CommandInteraction) => any, registerMode?: RegisterMode) { + this.name = name; + this.command = command; + this.response = response; + this.client = client; + if (registerMode) {this.registerMode = registerMode;} + + this.rest = new REST({version: '9'}).setToken(this.client.token); + } + + + + public async register(forceMode?: RegisterMode): Promise { + let mode = typeof forceMode !== 'undefined' ? forceMode : this.registerMode; + if (mode === 'global' || mode.global) { + return this.rest.put(Routes.applicationCommands(this.client.user.id), {body: [this.command.toJSON()]}); + } else { + if (Array.isArray(mode.serverIDs) && mode.serverIDs.length !== 1) { + let res = []; + mode.serverIDs.forEach(id => res.push(this.rest.put(Routes.applicationGuildCommands(this.client.user.id, id), {body: [this.command.toJSON()]}))); + return Promise.all(res); + } + else { + return this.rest.put(Routes.applicationGuildCommands(this.client.user.id, Array.isArray(mode.serverIDs) ? mode.serverIDs[0] : mode.serverIDs), {body: [this.command.toJSON()]}); + } + } + }; + + public async registerGlobally() { + return await this.register('global'); + }; + + public async registerToServer(serverIDs: string[]) { + return await this.register({serverIDs: serverIDs}); + }; + + public setResponse(newResponse: (client: Client, interaction: CommandInteraction) => any): SlashCommand { + this.response = newResponse; + return this; + }; + + public async disable() {}; + + public setDefaultRegisterMode(mode: RegisterMode): SlashCommand { + this.registerMode = mode; + return this; + }; + + public async respond(client: Client, interaction: CommandInteraction): Promise { + return this.response(client, interaction); + }; + + public setCommand(newCommand: SlashCommandBuilder): SlashCommand { + this.command = newCommand; + return this; + } + +}; + + +export type RegisterMode = {serverIDs: string[], global?: boolean} | "global"; \ No newline at end of file diff --git a/util/ts/slashmanager.ts b/util/ts/slashmanager.ts new file mode 100644 index 0000000..36ef77d --- /dev/null +++ b/util/ts/slashmanager.ts @@ -0,0 +1,130 @@ +import Discord = require("discord.js"); +import fs = require('fs'); +import {REST} from '@discordjs/rest'; +import {Routes} from 'discord-api-types/v9'; + +import {RegisterMode, SlashCommand} from "./slash"; + +export class SlashManager { + + client: Discord.Client; + commands: SlashCommand[]; + testServerId: string; + beforeHandle: (client: Discord.Client, interaction: Discord.CommandInteraction) => any = () => {}; + afterHandle: (client: Discord.Client, interaction: Discord.CommandInteraction, success: boolean) => any = () => {}; + + private initialized: boolean = false; + private rest: REST; + + constructor(client: Discord.Client, commands?: SlashCommand[], testServer?: string) { + this.client = client; + this.commands = commands || []; + if (this.testServerId) {this.testServerId = testServer;} + this.rest = new REST({version: '9'}).setToken(this.client.token); + } + + + + public async register(commands?: CommandLookup): Promise { + let trp = commands || this.commands; + let tr = Array.isArray(trp) ? this.getCommands(trp) : this.getCommand(trp); + return Array.isArray(tr) + ? this.rest.put(Routes.applicationCommands(this.client.user.id), {body: [(() => {let t = []; tr.forEach(trt => t.push(this.commands[trt].command.toJSON())); return t;})()]}) + : this.commands[tr].registerGlobally(); + }; + + public async devRegister(commands?: CommandLookup): Promise { + if (!this.testServerId) {throw new Error("You tried to register commands to your test server, but don't have a test server ID set. Try running SlashManager#setTestServer() first!");} + let trp = commands || this.commands; + let tr = Array.isArray(trp) ? this.getCommands(trp) : this.getCommand(trp); + return Array.isArray(tr) + ? this.rest.put(Routes.applicationGuildCommands(this.client.user.id, this.testServerId), {body: (() => {let t = []; tr.forEach(trt => t.push(this.commands[trt].command.toJSON())); return t;})()}) + : this.commands[tr].registerToServer([this.testServerId]); + }; + + public add(command: SlashCommand, register?: boolean | RegisterMode): SlashManager { + this.commands.push(command); + if (register) {command.register(typeof register === 'boolean' ? 'global' : register);} + return this; + }; + + public remove(commands: CommandLookup): SlashManager { + let trp = commands || this.commands; + let tr = Array.isArray(trp) ? this.getCommands(trp) : this.getCommand(trp); + if (Array.isArray(tr)) {tr.forEach(t => {this.commands.splice(t, 1);});} + else {this.commands.splice(tr, 1);} + return this; + }; + + public getCommand(command: SlashCommand | string): number { + let res: SlashCommand; + if (typeof command === "string") { + this.commands.forEach(cmd => {if (cmd.name === command) {res = cmd;}}); + if (!res) {throw new Error(`Name '${command}' doesn't match any commands in your SlashManager.`);} + } else { + if (!this.commands.includes(command)) {throw new Error(`The command you provided (with name '${command.name}') doesn't exist`);} + res = command; + } + return this.commands.indexOf(res); + }; + + public getCommands(commands: SlashCommand[] | string[]): number[] { + let res = []; + commands.forEach((cmd: SlashCommand | string) => res.push(this.getCommand(cmd))); + return res; + }; + + public init(): SlashManager { + if (this.initialized) {this.disableHandling();} + this.client.on("interactionCreate", (interaction: Discord.CommandInteraction) => this.handle(interaction)); + return this; + }; + + public disableHandling(): SlashManager { + this.client.removeListener('interactionCreate', this.handle); + return this; + }; + + public setBeforeHandle(execute: (client: Discord.Client, interaction: Discord.CommandInteraction) => any): SlashManager { + this.beforeHandle = execute; + this.init(); + return this; + }; + + public setAfterHandle(execute: (client: Discord.Client, interaction: Discord.CommandInteraction, success: Boolean) => any): SlashManager { + this.afterHandle = execute; + this.init(); + return this; + }; + + public setTestServer(id: string): SlashManager { + this.testServerId = id; + return this; + }; + + + private async handle(interaction: Discord.CommandInteraction): Promise { + this.beforeHandle(this.client, interaction); + let success = true; + try { + await this.commands[this.getCommand(interaction.commandName)].respond(this.client, interaction).catch((e) => {console.log(e); success = false;}); + } + catch (e) {console.log(e); success = false;} + this.afterHandle(this.client, interaction, success); + }; + + + + public importCommands(dir?: string): SlashManager { + dir = dir || './slash'; + const commands = fs.readdirSync(dir).filter(file => file.endsWith('.js')); + for (const command of commands) { + this.add(require(`../${dir}/${command}`)(this.client)); + } + return this; + }; + +} + + +type CommandLookup = SlashCommand[] | SlashCommand | string | string[]; \ No newline at end of file