Advertisement
Guest User

ah code stuff

a guest
Mar 20th, 2019
104
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 17.06 KB | None | 0 0
  1. const path = require('path');
  2. const discord = require('discord.js');
  3. const Command = require('./commands/base');
  4. const CommandGroup = require('./commands/group');
  5. const CommandMessage = require('./commands/message');
  6. const ArgumentType = require('./types/base');
  7. /** Handles registration and searching of commands and groups */
  8. class CommandRegistry {
  9. /** @param {CommandoClient} [client] - Client to use */
  10. constructor(client) {
  11. /**
  12. * The client this registry is for
  13. * @name CommandRegistry#client
  14. * @type {CommandoClient}
  15. * @readonly
  16. */
  17. Object.defineProperty(this, 'client', { value: client });
  18.  
  19. /**
  20. * Registered commands
  21. * @type {Collection<string, Command>}
  22. */
  23. this.commands = new discord.Collection();
  24.  
  25. /**
  26. * Registered command groups
  27. * @type {Collection<string, CommandGroup>}
  28. */
  29. this.groups = new discord.Collection();
  30.  
  31. /**
  32. * Registered argument types
  33. * @type {Collection<string, ArgumentType>}
  34. */
  35. this.types = new discord.Collection();
  36.  
  37. /**
  38. * Registered objects for the eval command
  39. * @type {Object}
  40. */
  41. this.evalObjects = {};
  42.  
  43. /**
  44. * Fully resolved path to the bot's commands directory
  45. * @type {?string}
  46. */
  47. this.commandsPath = null;
  48. }
  49.  
  50. /**
  51. * Registers a single group
  52. * @param {CommandGroup|Function|string[]|string} group - A CommandGroup instance, a constructor,
  53. * an array of [ID, Name], or the group ID
  54. * @param {string} [name] - Name for the group (if the first argument is the group ID)
  55. * @return {CommandRegistry}
  56. * @see {@link CommandRegistry#registerGroups}
  57. */
  58. registerGroup(group, name) {
  59. if(typeof group === 'string') return this.registerGroups([[group, name]]);
  60. return this.registerGroups([group]);
  61. }
  62.  
  63. /**
  64. * Registers multiple groups
  65. * @param {CommandGroup[]|Function[]|Array<string[]>} groups - An array of CommandGroup instances, constructors,
  66. * or arrays of [ID, Name]
  67. * @return {CommandRegistry}
  68. */
  69. registerGroups(groups) {
  70. if(!Array.isArray(groups)) throw new TypeError('Groups must be an Array.');
  71. for(let group of groups) {
  72. if(typeof group === 'function') {
  73. group = new group(this.client); // eslint-disable-line new-cap
  74. } else if(Array.isArray(group)) {
  75. group = new CommandGroup(this.client, ...group);
  76. } else if(!(group instanceof CommandGroup)) {
  77. group = new CommandGroup(this.client, group.id, group.name, null, group.commands);
  78. }
  79.  
  80. const existing = this.groups.get(group.id);
  81. if(existing) {
  82. existing.name = group.name;
  83. this.client.emit('debug', `Group ${group.id} is already registered; renamed it to "${group.name}".`);
  84. } else {
  85. this.groups.set(group.id, group);
  86. /**
  87. * Emitted when a group is registered
  88. * @event CommandoClient#groupRegister
  89. * @param {CommandGroup} group - Group that was registered
  90. * @param {CommandRegistry} registry - Registry that the group was registered to
  91. */
  92. this.client.emit('groupRegister', group, this);
  93. this.client.emit('debug', `Registered group ${group.id}.`);
  94. }
  95. }
  96. return this;
  97. }
  98.  
  99. /**
  100. * Registers a single command
  101. * @param {Command|Function} command - Either a Command instance, or a constructor for one
  102. * @return {CommandRegistry}
  103. * @see {@link CommandRegistry#registerCommands}
  104. */
  105. registerCommand(command) {
  106. return this.registerCommands([command]);
  107. }
  108.  
  109. /**
  110. * Registers multiple commands
  111. * @param {Command[]|Function[]} commands - An array of Command instances or constructors
  112. * @return {CommandRegistry}
  113. */
  114. registerCommands(commands) {
  115. if(!Array.isArray(commands)) throw new TypeError('Commands must be an Array.');
  116. for(let command of commands) {
  117. if(typeof command === 'function') command = new command(this.client); // eslint-disable-line new-cap
  118.  
  119. // Verify that it's an actual command
  120. if(!(command instanceof Command)) {
  121. this.client.emit('warn', `Attempting to register an invalid command object: ${command}; skipping.`);
  122. continue;
  123. }
  124.  
  125. // Make sure there aren't any conflicts
  126. if(this.commands.some(cmd => cmd.name === command.name || cmd.aliases.includes(command.name))) {
  127. throw new Error(`A command with the name/alias "${command.name}" is already registered.`);
  128. }
  129. for(const alias of command.aliases) {
  130. if(this.commands.some(cmd => cmd.name === alias || cmd.aliases.includes(alias))) {
  131. throw new Error(`A command with the name/alias "${alias}" is already registered.`);
  132. }
  133. }
  134. const group = this.groups.find(grp => grp.id === command.groupID);
  135. if(!group) throw new Error(`Group "${command.groupID}" is not registered.`);
  136. if(group.commands.some(cmd => cmd.memberName === command.memberName)) {
  137. throw new Error(`A command with the member name "${command.memberName}" is already registered in ${group.id}`);
  138. }
  139.  
  140. // Add the command
  141. command.group = group;
  142. group.commands.set(command.name, command);
  143. this.commands.set(command.name, command);
  144. /**
  145. * Emitted when a command is registered
  146. * @event CommandoClient#commandRegister
  147. * @param {Command} command - Command that was registered
  148. * @param {CommandRegistry} registry - Registry that the command was registered to
  149. */
  150. this.client.emit('commandRegister', command, this);
  151. this.client.emit('debug', `Registered command ${group.id}:${command.memberName}.`);
  152. }
  153.  
  154. return this;
  155. }
  156.  
  157. /**
  158. * Registers all commands in a directory. The files must export a Command class constructor or instance.
  159. * @param {string|RequireAllOptions} options - The path to the directory, or a require-all options object
  160. * @return {CommandRegistry}
  161. */
  162. registerCommandsIn(options) {
  163. const obj = require('require-all')(options);
  164. const commands = [];
  165. for(const group of Object.values(obj)) {
  166. for(let command of Object.values(group)) {
  167. if(typeof command.default === 'function') command = command.default;
  168. commands.push(command);
  169. }
  170. }
  171. if(typeof options === 'string' && !this.commandsPath) this.commandsPath = options;
  172. return this.registerCommands(commands);
  173. }
  174.  
  175. /**
  176. * Registers a single argument type
  177. * @param {ArgumentType|Function} type - Either an ArgumentType instance, or a constructor for one
  178. * @return {CommandRegistry}
  179. * @see {@link CommandRegistry#registerTypes}
  180. */
  181. registerType(type) {
  182. return this.registerTypes([type]);
  183. }
  184.  
  185. /**
  186. * Registers multiple argument types
  187. * @param {ArgumentType[]|Function[]} types - An array of ArgumentType instances or constructors
  188. * @return {CommandRegistry}
  189. */
  190. registerTypes(types) {
  191. if(!Array.isArray(types)) throw new TypeError('Types must be an Array.');
  192. for(let type of types) {
  193. if(typeof type === 'function') type = new type(this.client); // eslint-disable-line new-cap
  194.  
  195. // Verify that it's an actual type
  196. if(!(type instanceof ArgumentType)) {
  197. this.client.emit('warn', `Attempting to register an invalid argument type object: ${type}; skipping.`);
  198. continue;
  199. }
  200.  
  201. // Make sure there aren't any conflicts
  202. if(this.types.has(type.id)) throw new Error(`An argument type with the ID "${type.id}" is already registered.`);
  203.  
  204. // Add the type
  205. this.types.set(type.id, type);
  206. /**
  207. * Emitted when an argument type is registered
  208. * @event CommandoClient#typeRegister
  209. * @param {ArgumentType} type - Argument type that was registered
  210. * @param {CommandRegistry} registry - Registry that the type was registered to
  211. */
  212. this.client.emit('typeRegister', type, this);
  213. this.client.emit('debug', `Registered argument type ${type.id}.`);
  214. }
  215.  
  216. return this;
  217. }
  218.  
  219. /**
  220. * Registers all argument types in a directory. The files must export an ArgumentType class constructor or instance.
  221. * @param {string|RequireAllOptions} options - The path to the directory, or a require-all options object
  222. * @return {CommandRegistry}
  223. */
  224. registerTypesIn(options) {
  225. const obj = require('require-all')(options);
  226. const types = [];
  227. for(const type of Object.values(obj)) types.push(type);
  228. return this.registerTypes(types);
  229. }
  230.  
  231. /**
  232. * Registers the default argument types, groups, and commands. This is equivalent to:
  233. * ```js
  234. * registry.registerDefaultTypes()
  235. * .registerDefaultGroups()
  236. * .registerDefaultCommands();
  237. * ```
  238. * @return {CommandRegistry}
  239. */
  240. registerDefaults() {
  241. this.registerDefaultTypes();
  242. this.registerDefaultGroups();
  243. this.registerDefaultCommands();
  244. return this;
  245. }
  246.  
  247. /**
  248. * Registers the default groups ("util" and "commands")
  249. * @return {CommandRegistry}
  250. */
  251. registerDefaultGroups() {
  252. return this.registerGroups([
  253. ['commands', 'Commands', true],
  254. ['util', 'Utility']
  255. ]);
  256. }
  257.  
  258. /**
  259. * Registers the default commands to the registry
  260. * @param {Object} [options] - Object specifying what commands to register
  261. * @param {boolean} [options.help=true] - Whether or not to register the built-in help command
  262. * @param {boolean} [options.prefix=true] - Whether or not to register the built-in prefix command
  263. * @param {boolean} [options.eval_=true] - Whether or not to register the built-in eval command
  264. * @param {boolean} [options.ping=true] - Whether or not to register the built-in ping command
  265. * @param {boolean} [options.commandState=true] - Whether or not to register the built-in command state commands
  266. * (enable, disable, reload, list groups)
  267. * @return {CommandRegistry}
  268. */
  269. registerDefaultCommands({ help = true, prefix = true, ping = true, eval_ = true, commandState = true } = {}) {
  270. if(help) this.registerCommand(require('./commands/util/help'));
  271. if(prefix) this.registerCommand(require('./commands/util/prefix'));
  272. if(ping) this.registerCommand(require('./commands/util/ping'));
  273. if(eval_) this.registerCommand(require('./commands/util/eval'));
  274. if(commandState) {
  275. this.registerCommands([
  276. require('./commands/commands/groups'),
  277. require('./commands/commands/enable'),
  278. require('./commands/commands/disable'),
  279. require('./commands/commands/reload'),
  280. require('./commands/commands/load'),
  281. require('./commands/commands/unload')
  282. ]);
  283. }
  284. return this;
  285. }
  286.  
  287. /**
  288. * Registers the default argument types to the registry. These are:
  289. * - string
  290. * - integer
  291. * - float
  292. * - boolean
  293. * - user
  294. * - member
  295. * - role
  296. * - channel
  297. * - message
  298. * - command
  299. * - group
  300. * @return {CommandRegistry}
  301. */
  302. registerDefaultTypes() {
  303. this.registerTypes([
  304. require('./types/string'),
  305. require('./types/integer'),
  306. require('./types/float'),
  307. require('./types/boolean'),
  308. require('./types/user'),
  309. require('./types/member'),
  310. require('./types/role'),
  311. require('./types/channel'),
  312. require('./types/message'),
  313. require('./types/command'),
  314. require('./types/group')
  315. ]);
  316. return this;
  317. }
  318.  
  319. /**
  320. * Reregisters a command (does not support changing name, group, or memberName)
  321. * @param {Command|Function} command - New command
  322. * @param {Command} oldCommand - Old command
  323. */
  324. reregisterCommand(command, oldCommand) {
  325. if(typeof command === 'function') command = new command(this.client); // eslint-disable-line new-cap
  326. if(command.name !== oldCommand.name) throw new Error('Command name cannot change.');
  327. if(command.groupID !== oldCommand.groupID) throw new Error('Command group cannot change.');
  328. if(command.memberName !== oldCommand.memberName) throw new Error('Command memberName cannot change.');
  329. command.group = this.resolveGroup(command.groupID);
  330. command.group.commands.set(command.name, command);
  331. this.commands.set(command.name, command);
  332. /**
  333. * Emitted when a command is reregistered
  334. * @event CommandoClient#commandReregister
  335. * @param {Command} newCommand - New command
  336. * @param {Command} oldCommand - Old command
  337. */
  338. this.client.emit('commandReregister', command, oldCommand);
  339. this.client.emit('debug', `Reregistered command ${command.groupID}:${command.memberName}.`);
  340. }
  341.  
  342. /**
  343. * Unregisters a command
  344. * @param {Command} command - Command to unregister
  345. */
  346. unregisterCommand(command) {
  347. this.commands.delete(command.name);
  348. command.group.commands.delete(command.name);
  349. /**
  350. * Emitted when a command is unregistered
  351. * @event CommandoClient#commandUnregister
  352. * @param {Command} command - Command that was unregistered
  353. */
  354. this.client.emit('commandUnregister', command);
  355. this.client.emit('debug', `Unregistered command ${command.groupID}:${command.memberName}.`);
  356. }
  357.  
  358. /**
  359. * Registers a single object to be usable by the eval command
  360. * @param {string} key - The key for the object
  361. * @param {Object} obj - The object
  362. * @return {CommandRegistry}
  363. * @see {@link CommandRegistry#registerEvalObjects}
  364. */
  365. registerEvalObject(key, obj) {
  366. const registerObj = {};
  367. registerObj[key] = obj;
  368. return this.registerEvalObjects(registerObj);
  369. }
  370.  
  371. /**
  372. * Registers multiple objects to be usable by the eval command
  373. * @param {Object} obj - An object of keys: values
  374. * @return {CommandRegistry}
  375. */
  376. registerEvalObjects(obj) {
  377. Object.assign(this.evalObjects, obj);
  378. return this;
  379. }
  380.  
  381. /**
  382. * Finds all groups that match the search string
  383. * @param {string} [searchString] - The string to search for
  384. * @param {boolean} [exact=false] - Whether the search should be exact
  385. * @return {CommandGroup[]} All groups that are found
  386. */
  387. findGroups(searchString = null, exact = false) {
  388. if(!searchString) return this.groups;
  389.  
  390. // Find all matches
  391. const lcSearch = searchString.toLowerCase();
  392. const matchedGroups = this.groups.filterArray(
  393. exact ? groupFilterExact(lcSearch) : groupFilterInexact(lcSearch)
  394. );
  395. if(exact) return matchedGroups;
  396.  
  397. // See if there's an exact match
  398. for(const group of matchedGroups) {
  399. if(group.name.toLowerCase() === lcSearch || group.id === lcSearch) return [group];
  400. }
  401. return matchedGroups;
  402. }
  403.  
  404. /**
  405. * A CommandGroupResolvable can be:
  406. * * A CommandGroup
  407. * * A group ID
  408. * @typedef {CommandGroup|string} CommandGroupResolvable
  409. */
  410.  
  411. /**
  412. * Resolves a CommandGroupResolvable to a CommandGroup object
  413. * @param {CommandGroupResolvable} group - The group to resolve
  414. * @return {CommandGroup} The resolved CommandGroup
  415. */
  416. resolveGroup(group) {
  417. if(group instanceof CommandGroup) return group;
  418. if(typeof group === 'string') {
  419. const groups = this.findGroups(group, true);
  420. if(groups.length === 1) return groups[0];
  421. }
  422. throw new Error('Unable to resolve group.');
  423. }
  424.  
  425. /**
  426. * Finds all commands that match the search string
  427. * @param {string} [searchString] - The string to search for
  428. * @param {boolean} [exact=false] - Whether the search should be exact
  429. * @param {Message} [message] - The message to check usability against
  430. * @return {Command[]} All commands that are found
  431. */
  432. findCommands(searchString = null, exact = false, message = null) {
  433. if(!searchString) return message ? this.commands.filterArray(cmd => cmd.isUsable(message)) : this.commands;
  434.  
  435. // Find all matches
  436. const lcSearch = searchString.toLowerCase();
  437. const matchedCommands = this.commands.filterArray(
  438. exact ? commandFilterExact(lcSearch) : commandFilterInexact(lcSearch)
  439. );
  440. if(exact) return matchedCommands;
  441.  
  442. // See if there's an exact match
  443. for(const command of matchedCommands) {
  444. if(command.name === lcSearch || (command.aliases && command.aliases.some(ali => ali === lcSearch))) {
  445. return [command];
  446. }
  447. }
  448.  
  449. return matchedCommands;
  450. }
  451.  
  452. /**
  453. * A CommandResolvable can be:
  454. * * A Command
  455. * * A command name
  456. * * A CommandMessage
  457. * @typedef {Command|string} CommandResolvable
  458. */
  459.  
  460. /**
  461. * Resolves a CommandResolvable to a Command object
  462. * @param {CommandResolvable} command - The command to resolve
  463. * @return {Command} The resolved Command
  464. */
  465. resolveCommand(command) {
  466. if(command instanceof Command) return command;
  467. if(command instanceof CommandMessage) return command.command;
  468. if(typeof command === 'string') {
  469. const commands = this.findCommands(command, true);
  470. if(commands.length === 1) return commands[0];
  471. }
  472. throw new Error('Unable to resolve command.');
  473. }
  474.  
  475. /**
  476. * Resolves a command file path from a command's group ID and memberName
  477. * @param {string} group - ID of the command's group
  478. * @param {string} memberName - Member name of the command
  479. * @return {string} Fully-resolved path to the corresponding command file
  480. */
  481. resolveCommandPath(group, memberName) {
  482. return path.join(this.commandsPath, group, `${memberName}.js`);
  483. }
  484. }
  485.  
  486. function groupFilterExact(search) {
  487. return grp => grp.id === search || grp.name.toLowerCase() === search;
  488. }
  489.  
  490. function groupFilterInexact(search) {
  491. return grp => grp.id.includes(search) || grp.name.toLowerCase().includes(search);
  492. }
  493.  
  494. function commandFilterExact(search) {
  495. return cmd => cmd.name === search ||
  496. (cmd.aliases && cmd.aliases.some(ali => ali === search)) ||
  497. `${cmd.groupID}:${cmd.memberName}` === search;
  498. }
  499.  
  500. function commandFilterInexact(search) {
  501. return cmd => cmd.name.includes(search) ||
  502. `${cmd.groupID}:${cmd.memberName}` === search ||
  503. (cmd.aliases && cmd.aliases.some(ali => ali.includes(search)));
  504. }
  505.  
  506. module.exports = CommandRegistry;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement