Tried experimental porting of command types from CrossExchange
This commit is contained in:
parent
df3e4e8e6e
commit
0d0d134415
|
@ -1,4 +1,4 @@
|
|||
import Command from "../../core/command";
|
||||
import Command, {TYPES} from "../../core/command";
|
||||
import {toTitleCase} from "../../core/lib";
|
||||
import {loadableCommands, categories} from "../../core/command";
|
||||
import {getPermissionName} from "../../core/permissions";
|
||||
|
@ -68,16 +68,16 @@ export default new Command({
|
|||
if (permLevel === -1) permLevel = command.permission;
|
||||
|
||||
switch (type) {
|
||||
case Command.TYPES.SUBCOMMAND:
|
||||
case TYPES.SUBCOMMAND:
|
||||
header += ` ${command.originalCommandName}`;
|
||||
break;
|
||||
case Command.TYPES.USER:
|
||||
case TYPES.USER:
|
||||
header += " <user>";
|
||||
break;
|
||||
case Command.TYPES.NUMBER:
|
||||
case TYPES.NUMBER:
|
||||
header += " <number>";
|
||||
break;
|
||||
case Command.TYPES.ANY:
|
||||
case TYPES.ANY:
|
||||
header += " <any>";
|
||||
break;
|
||||
default:
|
||||
|
@ -85,7 +85,7 @@ export default new Command({
|
|||
break;
|
||||
}
|
||||
|
||||
if (type === Command.TYPES.NONE) {
|
||||
if (type === TYPES.NONE) {
|
||||
invalid = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
import {parseVars} from "./lib";
|
||||
import {Collection} from "discord.js";
|
||||
import {Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember} from "discord.js";
|
||||
import {Collection, Client, Message, TextChannel, DMChannel, NewsChannel, Guild, User, GuildMember} from "discord.js";
|
||||
import {getPrefix} from "../core/structures";
|
||||
import glob from "glob";
|
||||
|
||||
|
@ -22,17 +21,27 @@ interface CommandOptions {
|
|||
aliases?: string[];
|
||||
run?: (($: CommandMenu) => Promise<any>) | string;
|
||||
subcommands?: {[key: string]: Command};
|
||||
channel?: Command;
|
||||
role?: Command;
|
||||
emote?: Command;
|
||||
message?: Command;
|
||||
user?: Command;
|
||||
id?: "channel" | "role" | "emote" | "message" | "user";
|
||||
number?: Command;
|
||||
any?: Command;
|
||||
}
|
||||
|
||||
export enum TYPES {
|
||||
SUBCOMMAND,
|
||||
USER,
|
||||
NUMBER,
|
||||
ANY,
|
||||
NONE
|
||||
SUBCOMMAND, // Any specifically-defined keywords / string literals.
|
||||
CHANNEL, // <#...>
|
||||
ROLE, // <@&...>
|
||||
EMOTE, // <::ID> (The previous two values, animated and emote name respectively, do not matter at all for finding the emote.)
|
||||
MESSAGE, // Available by using the built-in "Copy Message Link" or "Copy ID" buttons. https://discordapp.com/channels/<Guild ID>/<Channel ID>/<Message ID> or <Channel ID>-<Message ID> (automatically searches all guilds for the channel ID).
|
||||
USER, // <@...> and <@!...>
|
||||
ID, // Any number with 17-19 digits. Only used as a redirect to another subcommand type.
|
||||
NUMBER, // Any valid number via the Number() function, except for NaN and Infinity (because those can really mess with the program).
|
||||
ANY, // Generic argument case.
|
||||
NONE // No subcommands exist.
|
||||
}
|
||||
|
||||
export default class Command {
|
||||
|
@ -44,10 +53,14 @@ export default class Command {
|
|||
public originalCommandName: string | null; // If the command is an alias, what's the original name?
|
||||
public run: (($: CommandMenu) => Promise<any>) | string;
|
||||
public readonly subcommands: Collection<string, Command>; // This is the final data structure you'll actually use to work with the commands the aliases point to.
|
||||
public channel: Command | null;
|
||||
public role: Command | null;
|
||||
public emote: Command | null;
|
||||
public message: Command | null;
|
||||
public user: Command | null;
|
||||
public id: Command | null;
|
||||
public number: Command | null;
|
||||
public any: Command | null;
|
||||
public static readonly TYPES = TYPES;
|
||||
|
||||
constructor(options?: CommandOptions) {
|
||||
this.description = options?.description || "No description.";
|
||||
|
@ -58,10 +71,35 @@ export default class Command {
|
|||
this.originalCommandName = null;
|
||||
this.run = options?.run || "No action was set on this command!";
|
||||
this.subcommands = new Collection(); // Populate this collection after setting subcommands.
|
||||
this.channel = options?.channel || null;
|
||||
this.role = options?.role || null;
|
||||
this.emote = options?.emote || null;
|
||||
this.message = options?.message || null;
|
||||
this.user = options?.user || null;
|
||||
this.number = options?.number || null;
|
||||
this.any = options?.any || null;
|
||||
|
||||
switch (options?.id) {
|
||||
case "channel":
|
||||
this.id = this.channel;
|
||||
break;
|
||||
case "role":
|
||||
this.id = this.role;
|
||||
break;
|
||||
case "emote":
|
||||
this.id = this.emote;
|
||||
break;
|
||||
case "message":
|
||||
this.id = this.message;
|
||||
break;
|
||||
case "user":
|
||||
this.id = this.user;
|
||||
break;
|
||||
default:
|
||||
this.id = null;
|
||||
break;
|
||||
}
|
||||
|
||||
if (options?.subcommands) {
|
||||
const baseSubcommands = Object.keys(options.subcommands);
|
||||
|
||||
|
@ -89,20 +127,28 @@ export default class Command {
|
|||
}
|
||||
}
|
||||
|
||||
if (this.user && this.user.aliases.length > 0)
|
||||
console.warn(
|
||||
`There are aliases defined for a "user"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`
|
||||
);
|
||||
// Because command aliases don't actually do anything except for subcommands, let the user know that this won't do anything.
|
||||
warnCommandAliases(this.channel, "channel");
|
||||
warnCommandAliases(this.role, "role");
|
||||
warnCommandAliases(this.emote, "emote");
|
||||
warnCommandAliases(this.message, "message");
|
||||
warnCommandAliases(this.user, "user");
|
||||
warnCommandAliases(this.number, "number");
|
||||
warnCommandAliases(this.any, "any");
|
||||
|
||||
if (this.number && this.number.aliases.length > 0)
|
||||
console.warn(
|
||||
`There are aliases defined for a "number"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`
|
||||
);
|
||||
|
||||
if (this.any && this.any.aliases.length > 0)
|
||||
console.warn(
|
||||
`There are aliases defined for an "any"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`
|
||||
);
|
||||
// Warn on unused endpoints too.
|
||||
if (
|
||||
this.endpoint &&
|
||||
(this.subcommands.size > 0 ||
|
||||
this.channel ||
|
||||
this.role ||
|
||||
this.emote ||
|
||||
this.message ||
|
||||
this.user ||
|
||||
this.number ||
|
||||
this.any)
|
||||
)
|
||||
console.warn(`An endpoint cannot have subcommands!`);
|
||||
}
|
||||
|
||||
public execute($: CommandMenu) {
|
||||
|
@ -121,41 +167,61 @@ export default class Command {
|
|||
}
|
||||
|
||||
public resolve(param: string): TYPES {
|
||||
if (this.id && /^\d{17,19}$/.test(param)) {
|
||||
}
|
||||
|
||||
if (this.subcommands.has(param)) return TYPES.SUBCOMMAND;
|
||||
// Any Discord ID format will automatically format to a user ID.
|
||||
else if (this.user && /\d{17,19}/.test(param)) return TYPES.USER;
|
||||
else if (this.channel && /^<#\d{17,19}>$/.test(param)) return TYPES.CHANNEL;
|
||||
else if (this.role && /^<@&\d{17,19}>$/.test(param)) return TYPES.ROLE;
|
||||
else if (this.emote && /^<a?:.*?:\d{17,19}>$/.test(param)) return TYPES.EMOTE;
|
||||
else if (this.message && /(\d{17,19}\/\d{17,19}\/\d{17,19}$)|(^\d{17,19}-\d{17,19}$)/.test(param))
|
||||
return TYPES.MESSAGE;
|
||||
else if (this.user && /^<@!?\d{17,19}>$/.test(param)) return TYPES.USER;
|
||||
// Disallow infinity and allow for 0.
|
||||
else if (this.number && (Number(param) || param === "0") && !param.includes("Infinity")) return TYPES.NUMBER;
|
||||
else if (this.number && !Number.isNaN(Number(param)) && param !== "Infinity" && param !== "-Infinity")
|
||||
return TYPES.NUMBER;
|
||||
else if (this.any) return TYPES.ANY;
|
||||
else return TYPES.NONE;
|
||||
}
|
||||
|
||||
public get(param: string): Command {
|
||||
const type = this.resolve(param);
|
||||
let command: Command;
|
||||
|
||||
switch (type) {
|
||||
// You can also optionally send in a pre-calculated value if you already called Command.resolve so you don't call it again.
|
||||
public get(param: string, type?: TYPES): Command {
|
||||
// This expression only runs once, don't worry.
|
||||
switch (type ?? this.resolve(param)) {
|
||||
case TYPES.SUBCOMMAND:
|
||||
command = this.subcommands.get(param) as Command;
|
||||
break;
|
||||
return checkResolvedCommand(this.subcommands.get(param));
|
||||
case TYPES.CHANNEL:
|
||||
return checkResolvedCommand(this.channel);
|
||||
case TYPES.ROLE:
|
||||
return checkResolvedCommand(this.role);
|
||||
case TYPES.EMOTE:
|
||||
return checkResolvedCommand(this.emote);
|
||||
case TYPES.MESSAGE:
|
||||
return checkResolvedCommand(this.message);
|
||||
case TYPES.USER:
|
||||
command = this.user as Command;
|
||||
break;
|
||||
return checkResolvedCommand(this.user);
|
||||
case TYPES.NUMBER:
|
||||
command = this.number as Command;
|
||||
break;
|
||||
return checkResolvedCommand(this.number);
|
||||
case TYPES.ANY:
|
||||
command = this.any as Command;
|
||||
break;
|
||||
return checkResolvedCommand(this.any);
|
||||
default:
|
||||
command = this;
|
||||
break;
|
||||
return this;
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
}
|
||||
|
||||
function warnCommandAliases(command: Command | null, type: string) {
|
||||
if (command && command.aliases.length > 0)
|
||||
console.warn(
|
||||
`There are aliases defined for an "${type}"-type subcommand, but those aliases won't be used. (Look at the next "Loading Command" line to see which command is affected.)`
|
||||
);
|
||||
}
|
||||
|
||||
function checkResolvedCommand(command: Command | null | undefined): Command {
|
||||
if (!command) throw new Error("FATAL: Command type mismatch while calling Command.get!");
|
||||
return command;
|
||||
}
|
||||
|
||||
// Internally, it'll keep its original capitalization. It's up to you to convert it to title case when you make a help command.
|
||||
export const categories = new Collection<string, string[]>();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {client} from "../index";
|
||||
import Command, {loadableCommands} from "../core/command";
|
||||
import Command, {loadableCommands, TYPES} from "../core/command";
|
||||
import {hasPermission, getPermissionLevel, getPermissionName} from "../core/permissions";
|
||||
import {Permissions} from "discord.js";
|
||||
import {getPrefix} from "../core/structures";
|
||||
|
@ -102,18 +102,70 @@ client.on("message", async (message) => {
|
|||
}
|
||||
|
||||
const type = command.resolve(param);
|
||||
command = command.get(param);
|
||||
command = command.get(param, type);
|
||||
permLevel = command.permission ?? permLevel;
|
||||
|
||||
if (type === Command.TYPES.USER) {
|
||||
const id = param.match(/\d+/g)![0];
|
||||
try {
|
||||
params.push(await message.client.users.fetch(id));
|
||||
} catch (error) {
|
||||
return message.channel.send(`No user found by the ID \`${id}\`!`);
|
||||
// Add argument to parameter list, do different stuff depending on the subcommand type.
|
||||
switch (type) {
|
||||
case TYPES.SUBCOMMAND:
|
||||
break;
|
||||
case TYPES.CHANNEL: {
|
||||
const id = param.match(/^<#(\d{17,19})>$/)![1];
|
||||
console.debug(id);
|
||||
try {
|
||||
params.push(message.guild!.channels.cache.get(id));
|
||||
} catch (error) {
|
||||
return message.channel.send(`No channel found by the ID \`${id}\` in this guild!`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else if (type === Command.TYPES.NUMBER) params.push(Number(param));
|
||||
else if (type !== Command.TYPES.SUBCOMMAND) params.push(param);
|
||||
case TYPES.ROLE: {
|
||||
const id = param.match(/^<@&(\d{17,19})>$/)![1];
|
||||
console.debug(id);
|
||||
try {
|
||||
params.push(await message.guild!.roles.fetch(id));
|
||||
} catch (error) {
|
||||
return message.channel.send(`No role found by the ID \`${id}\` in this guild!`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPES.EMOTE: {
|
||||
const id = param.match(/^<a?:.*?:(\d{17,19})>$/)![1];
|
||||
console.debug(id);
|
||||
try {
|
||||
params.push(message.client.emojis.cache.get(id));
|
||||
} catch (error) {
|
||||
return message.channel.send(`No emote found by the ID \`${id}\`!`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPES.MESSAGE: {
|
||||
const id = param.match(/(\d{17,19}\/\d{17,19}\/\d{17,19}$)|(^\d{17,19}-\d{17,19}$)/);
|
||||
console.log(id);
|
||||
/*try {
|
||||
params.push(await message.client.users.fetch(id));
|
||||
} catch (error) {
|
||||
return message.channel.send(`No user found by the ID \`${id}\`!`);
|
||||
}*/
|
||||
break;
|
||||
}
|
||||
case TYPES.USER: {
|
||||
const id = param.match(/^<@!?(\d{17,19})>$/)![1];
|
||||
console.debug(param, id);
|
||||
try {
|
||||
params.push(await message.client.users.fetch(id));
|
||||
} catch (error) {
|
||||
return message.channel.send(`No user found by the ID \`${id}\`!`);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TYPES.NUMBER:
|
||||
params.push(Number(param));
|
||||
break;
|
||||
case TYPES.ANY:
|
||||
params.push(param);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!message.member)
|
||||
|
|
Loading…
Reference in New Issue