diff --git a/language/LocaleLoader.js b/language/LocaleLoader.js index 1cab4ce..c9a258f 100644 --- a/language/LocaleLoader.js +++ b/language/LocaleLoader.js @@ -16,7 +16,7 @@ class LocaleLoader { template(locale, index) { // console.log(String.raw`${this.languages[locale][index]}`); - return (this.languages[locale] ? this.languages[locale][index] : `Locale \`${locale}\` does not exist.`) || `Entry \`${locale}:${index}\` does not exist.`; + return (this.languages[locale] ? this.languages[locale][index] : `Locale \`${locale}\` does not exist.`) || `Language entry \`${locale}:${index}\` does not exist.`; } diff --git a/language/languages/en_us/commands/en_us_information.lang b/language/languages/en_us/commands/en_us_information.lang index 0e447fe..be97e54 100644 --- a/language/languages/en_us/commands/en_us_information.lang +++ b/language/languages/en_us/commands/en_us_information.lang @@ -50,6 +50,9 @@ __**{component} HELP**__ Shows information about the bot and component usage. To show help for commands `{prefix}help [command]` +[C_HELP_404] +Dude stop, {component} doesn't exist! + //Guild command [C_GUILD_DESCRIPTION] diff --git a/structure/client/DiscordClient.js b/structure/client/DiscordClient.js index 4d842c6..9d755e2 100644 --- a/structure/client/DiscordClient.js +++ b/structure/client/DiscordClient.js @@ -87,6 +87,10 @@ class DiscordClient extends Client { return def; } + get prefix() { + return this._options.prefix; + } + } module.exports = DiscordClient; diff --git a/structure/client/Resolver.js b/structure/client/Resolver.js index 12b5525..d3cd092 100644 --- a/structure/client/Resolver.js +++ b/structure/client/Resolver.js @@ -15,12 +15,12 @@ class Resolver { * @returns * @memberof Resolver */ - components(str = '', type, exact = true) { + components(str = '', type = 'any', exact = true) { const string = str.toLowerCase(); const components = this.client.registry.components - .filter((c) => c.type === type) + .filter((c) => type === 'any' ? ['command', 'setting'].includes(c.type) : c.type === type) .filter(exact ? filterExact(string) : filterInexact(string)) //eslint-disable-line no-use-before-define .array(); @@ -28,6 +28,19 @@ class Resolver { } + componentsByTag(str, type, exact = true) { + + const key = str.toLowerCase(); + + const components = this.client.registry.components + .filter((c) => c.type === type) + .filter(exact ? (c) => c.tags.includes(key) : filterInexactTags(key)) + .array(); + + return components || []; + + } + timeAgo(diff, extraMin = false, extraHours = false, extraDays = false) { diff = parseInt(diff); @@ -500,4 +513,10 @@ const filterExact = (search) => (comp) => comp.id.toLowerCase() === search || const filterInexact = (search) => (comp) => comp.id.toLowerCase().includes(search) || comp.resolveable.toLowerCase().includes(search) || comp.aliases && (comp.aliases.some((ali) => `${comp.type}:${ali}`.toLowerCase().includes(search)) || - comp.aliases.some((ali) => ali.toLowerCase().includes(search))); \ No newline at end of file + comp.aliases.some((ali) => ali.toLowerCase().includes(search))); + +const filterInexactTags = (search) => (comp) => comp.id.toLowerCase().includes(search.toLowerCase()) || + comp.resolveable.toLowerCase().includes(search.toLowerCase()) || + comp.aliases && (comp.aliases.some((ali) => `${comp.type}:${ali}`.toLowerCase().includes(search.toLowerCase())) || + comp.aliases.some((ali) => ali.toLowerCase().includes(search.toLowerCase())) || + comp.tags.some((tag) => tag.toLowerCase().includes(search.toLowerCase()))); \ No newline at end of file diff --git a/structure/client/components/commands/information/Commands.js b/structure/client/components/commands/information/Commands.js index 78c2f18..af7e6ce 100644 --- a/structure/client/components/commands/information/Commands.js +++ b/structure/client/components/commands/information/Commands.js @@ -34,10 +34,10 @@ class CommandsCommand extends Command { params = params.join(' '); const [ mod ] = this.client.resolver.components(params, 'module', false); - if(!mod) { + if (!mod) { const [ command ] = this.client.resolver.components(params, 'command', false); if (!command) return message.format('C_COMMAND_INVALID'); - return message._showUsage(command); + return message.embed(command.usageEmbed(message, true)); } //list module's commands @@ -45,7 +45,7 @@ class CommandsCommand extends Command { let text = ''; for(const command of commands.values()) { - text += command.disabled ? `~~${command.name}~~\n` : `${command.name}`; //TODO: Denote disabled commands somehow + text += command.disabled ? `~~${command.name}~~\n` : `${command.name}\n`; //TODO: Denote disabled commands somehow } const embed = { diff --git a/structure/client/components/commands/information/Help.js b/structure/client/components/commands/information/Help.js index 19f4c12..defedf3 100644 --- a/structure/client/components/commands/information/Help.js +++ b/structure/client/components/commands/information/Help.js @@ -22,24 +22,31 @@ class HelpCommand extends Command { const [ key ] = params; let [ result ] = this.client.resolver.components(key, 'command'); if (!result) [ result ] = this.client.resolver.components(key, 'setting'); + if (!result) result = this.client.resolver.componentsByTag(key, 'any', false); if (!result) return message.embed({ description: message.format('C_HELP_404', { component: key }) }); - //let index = `${result.type.slice(0, 1).toUpperCase()}_${result.name.toUpperCase()}_HELP`; - return message.embed({ - description: message.format('C_HELP_TEMPLATE', { - desc: message.format(result.description), - component: result.name.toUpperCase(), - text: result.examples.map(ex => `\`{prefix}${result.type === 'command' ? result.name.toLowerCase() : `settings ${result.name.toLowerCase()}`} ${ex}\``).join('\n') - }), - fields: [ - { - name: 'Aliases', - value: result.aliases.map(al => al.toLowerCase()).join(', ') - } - ] - }); + if (result instanceof Array) { + + } else return message.embed(result.usageEmbed(message, true)); + + // //let index = `${result.type.slice(0, 1).toUpperCase()}_${result.name.toUpperCase()}_HELP`; + // const em = { + // description: message.format('C_HELP_TEMPLATE', { + // desc: message.format(result.description), + // component: result.name.toUpperCase(), + // text: result.examples.map((ex) => `\`{prefix}${result.type === 'command' ? result.name.toLowerCase() : `settings ${result.name.toLowerCase()}`} ${ex}\``).join('\n') + // }) + // }; + // if (result.aliases.length) em.fields = [ + // { + // name: 'Aliases', + // value: result.aliases.map((al) => al.toLowerCase()).join(', ') + // } + // ]; + + // return message.embed(em); } diff --git a/structure/client/components/observers/UtilityHook.js b/structure/client/components/observers/UtilityHook.js index ed47c46..07447ab 100644 --- a/structure/client/components/observers/UtilityHook.js +++ b/structure/client/components/observers/UtilityHook.js @@ -1,4 +1,5 @@ const { Observer } = require('../../../interfaces/'); +const { Util } = require('../../../../util'); const CONSTANTS = {}; @@ -14,7 +15,7 @@ class UtilityHook extends Observer { this.hooks = [ ['guildMemberAdd', this.welcome.bind(this)], ['guildMemberAdd', this.autorole.bind(this)] - ] + ]; } @@ -32,7 +33,12 @@ class UtilityHook extends Observer { const { guild } = member; const settings = await guild.settings(); const setting = settings.autorole; - if(!setting.enabled) return; + if (!setting.enabled) return; + if (!guild.me.hasPermission('MANAGE_ROLES')) return; + + const _roles = await guild.resolveRoles(setting.roles); + const roles = _roles.map((r) => r.id); + await member.roles.add(roles, 'Adding autoroles').catch(this.client.logger.error); } @@ -41,8 +47,8 @@ class UtilityHook extends Observer { const { guild, user } = member; const settings = await guild.settings(); const setting = settings.welcomer; - if(!setting.enabled) return; - + if (!setting.enabled) return; + const channel = await user.createDM(); const message = this._replaceTags(setting.message, member); await channel.send(message).catch(); @@ -52,13 +58,13 @@ class UtilityHook extends Observer { _replaceTags(text, member) { const { user, guild } = member; return text - .replace(/\{mention\}/g, `<@${member.id}>`) - .replace(/\{tag\}/g, Util.escapeMarkdown(user.tag)) - .replace(/\{user\}/g, Util.escapeMarkdown(user.username)) - .replace(/\{guildsize\}/g, guild.memberCount) - .replace(/\{guildname\}/g, guild.name) - .replace(/\{accage\}/g, this.client.resolver.timeAgo(Date.now()/1000 - user.createdTimestamp/1000)) //.replace(/a/, '1') - .replace(/\{id\}/g, user.id) + .replace(/\{mention\}/gu, `<@${member.id}>`) + .replace(/\{tag\}/gu, Util.escapeMarkdown(user.tag)) + .replace(/\{user\}/gu, Util.escapeMarkdown(user.username)) + .replace(/\{guildsize\}/gu, guild.memberCount) + .replace(/\{guildname\}/gu, guild.name) + .replace(/\{accage\}/gu, this.client.resolver.timeAgo(Date.now()/1000 - user.createdTimestamp/1000)) //.replace(/a/, '1') + .replace(/\{id\}/gu, user.id) .trim(); } diff --git a/structure/extensions/Message.js b/structure/extensions/Message.js index 7780e71..c675a58 100644 --- a/structure/extensions/Message.js +++ b/structure/extensions/Message.js @@ -60,7 +60,7 @@ const Message = Structures.extend('Message', (Message) => { } if (this.command.showUsage && !this.parameters.length && !Object.values(this.arguments).length) { - return this._showUsage(); + return this.embed(this.command.usageEmbed(this)); } this.client.emit('commandExecute', this); diff --git a/structure/interfaces/Command.js b/structure/interfaces/Command.js index 95c877c..4e4b9ce 100644 --- a/structure/interfaces/Command.js +++ b/structure/interfaces/Command.js @@ -1,4 +1,5 @@ const Component = require('./Component.js'); +const { stripIndents } = require('common-tags'); class Command extends Component { @@ -54,6 +55,42 @@ class Command extends Component { return `${this.module.id}:${this.id}`; } + usageEmbed(message, verbose = false) { + + const { guild } = message; + const prefix = guild?.prefix || this.client.prefix; + const fields = []; + + if (this.examples.length) { + fields.push({ + name: `》${message.format('GENERAL_EXAMPLES')}`, + value: this.examples.map((e) => this.rawExamples ? `\`${prefix}${e}\`` : `\`${prefix}${this.name} ${e}\``).join('\n') + }); + } + if (this.aliases.length && verbose) { + fields.push({ + name: `》${message.format('GENERAL_ALIASES')}`, + value: this.aliases.join(', ') + }); + } + if (this.arguments.length && verbose) { + fields.push({ + name: `》${message.format('GENERAL_ARGUMENTS')}`, + value: this.arguments.map((a) => `\`${a.types.length === 1 && a.types.includes('FLAG') ? '--' : ''}${a.name}${a.usage ? ` ${a.usage}` : ''}\`: ${message.format(`A_${a.name.toUpperCase()}_${this.name.toUpperCase()}_DESCRIPTION`)}`) + }); + } + + return { + author: { + name: `${this.name}${this.module ? ` (${this.module.resolveable})` : ''}` + }, + description: stripIndents`\`${prefix}${this.name}${this.usage ? ` ${this.usage}` : ''}\`${this.guildOnly ? ' *(guild-only)*' : ''} + ${message.format(this.description)}`, + fields + }; + + } + } module.exports = Command; \ No newline at end of file diff --git a/structure/interfaces/Setting.js b/structure/interfaces/Setting.js index 3e6bffa..166c8a0 100644 --- a/structure/interfaces/Setting.js +++ b/structure/interfaces/Setting.js @@ -20,6 +20,7 @@ class Setting extends Component { this.description = opts.description || `S_${opts.name.toUpperCase()}_DESCRIPTION`; this.examples = opts.examples || []; + this.rawExamples = Boolean(opts.rawExamples); this.usage = opts.usage || ''; this.archivable = opts.archivable === undefined ? true : Boolean(opts.archivable); @@ -92,6 +93,42 @@ class Setting extends Component { get display() { return this.name.toLowerCase(); } + + usageEmbed(message, verbose = false) { + + const { guild } = message; + const prefix = guild?.prefix || this.client.prefix; + const fields = []; + + if (this.examples.length) { + fields.push({ + name: `》${message.format('GENERAL_EXAMPLES')}`, + value: this.examples.map((e) => this.rawExamples ? `\`${prefix}${e}\`` : `\`${prefix}${this.name} ${e}\``).join('\n') + }); + } + if (this.aliases.length && verbose) { + fields.push({ + name: `》${message.format('GENERAL_ALIASES')}`, + value: this.aliases.join(', ') + }); + } + if (this.arguments.length && verbose) { + fields.push({ + name: `》${message.format('GENERAL_ARGUMENTS')}`, + value: this.arguments.map((a) => `\`${a.types.length === 1 && a.types.includes('FLAG') ? '--' : ''}${a.name}${a.usage ? ` ${a.usage}` : ''}\`: ${message.format(`A_${a.name.toUpperCase()}_${this.name.toUpperCase()}_DESCRIPTION`)}`) + }); + } + + return { + author: { + name: `${this.name}${this.module ? ` (${this.module.resolveable})` : ''}` + }, + description: stripIndents`\`${prefix}${this.name}${this.usage ? ` ${this.usage}` : ''}\`${this.guildOnly ? ' *(guild-only)*' : ''} + ${message.format(this.description)}`, + fields + }; + + } }