mirror of
				https://github.com/keanuplayz/TravBot-v3.git
				synced 2024-08-15 02:33:12 +00:00 
			
		
		
		
	Tried experimental porting of command types from CrossExchange
This commit is contained in:
		
							parent
							
								
									df3e4e8e6e
								
							
						
					
					
						commit
						0d0d134415
					
				
					 3 changed files with 175 additions and 57 deletions
				
			
		|  | @ -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…
	
	Add table
		Add a link
		
	
		Reference in a new issue