grant/revoke commands, command handler improvements, avatar command

This commit is contained in:
nolan 2020-05-08 02:50:54 -04:00
parent 7cbea4af81
commit a71cb26522
25 changed files with 626 additions and 257 deletions

View File

@ -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.`) || `Entry \`${locale}:${index}\` does not exist.`;
}
@ -74,6 +74,7 @@ class LocaleLoader {
}
}
}
parsed[matched] = text;
for(const [ key, value ] of Object.entries(parsed)) {
parsed[key] = value.replace(/^(\r)|(\r){1,}$/g, '');

View File

@ -0,0 +1,2 @@
[A_CHANNEL_GRANT_DESCRIPTION]
Specify channels to grant specific permissions to.

View File

@ -0,0 +1,47 @@
//Grant Command
[C_GRANT_DESCRIPTION]
Grant roles or users permissions for commands or modules.
[C_GRANT_RESOLVEERROR]
Unable to find a role or member, view `{prefix}cmd grant` for more help.
[C_GRANT_MISSINGPERMPARAM]
You must provide permissions to grant, view `{prefix}cmd grant` for more help.
[C_GRANT_DATABASEERROR]
There was an issue pushing the permissions to the database. Contact a bot developer.
[C_GRANT_SUCCESS]
Successfully granted **{resolveable}** the following permissions: {permissions}
[C_GRANT_SUCCESSALT]S
in channel(s) {channels}.
[C_GRANT_SUCCESSFAILED]
Some permissions have failed to add, likely because the {resolveable} already has the permissions or you did not supply valid permissions.
[C_GRANT_FAILED]
Failed to grant the following permissions: {failed}.
This is likely because the {resolveable} already has the permissions or you did not supply valid permissions.
//Revoke Command
[C_REVOKE_DESCRIPTION]
Revoke permissions granted to roles or users.
NOTE: If a role is deleted or a user left the guild, you must use the ID to revoke the permissions.
[C_REVOKE_RESOLVEERROR]
Unable to find a role or user, view `{prefix}cmd revoke` for more help.
[C_REVOKE_MISSINGPERMPARAM]
You must provide permissions to revoke, view `{prefix}cmd grant` for more help.
[C_REVOKE_DATABASEERROR]
There was an issue removing the permissions from the database. Contact a bot developer.
//Permissions Command
[C_PERMISSIONS_DESCRIPTION]
View permissions granted to roles or users.

View File

@ -1,20 +0,0 @@
//Grant Command
[C_GRANT_DESCRIPTION]
Grant roles or users permissions for commands or modules.
[C_GRANT_RESOLVEERROR]
Unable to find any roles or members, view `{prefix}cmd grant` for more help.
[C_GRANT_MISSINGPERMPARAM]
You must provide permissions to grant, view `{prefix}cmd grant` for more help.
//Revoke Command
[C_REVOKE_DESCRIPTION]
Revoke permissions granted to roles or users.
//Permissions Command
[C_PERMISSIONS_DESCRIPTION]
View permissions granted to roles or users.

View File

@ -70,4 +70,8 @@ Search result for keyword: `{key}`
Found {matches} matches, displaying {count}
[C_USER_HELP]
To search server members with similar names use `{prefix}user search <arguments..>`
To search server members with similar names use `{prefix}user search <arguments..>`
//Avatar Command
[C_AVATAR_FORMATERROR]
Unable to find an avatar with those arguments, try a different size or format.

View File

@ -18,4 +18,4 @@ Example Usage
Aliases
[GENERAL_ARGUMENTS]
Arguments
Arguments

View File

@ -4,7 +4,7 @@
"description": "New iteration of GalacticBot",
"main": "index.js",
"scripts": {
"start": "node --trace-warnings --inspect index.js"
"start": "node --trace-warnings index.js"
},
"repository": {
"type": "git",

View File

@ -43,7 +43,7 @@ class Resolver {
async resolveMemberAndUser(string, guild) {
const str = string.toLowerCase();
const index = guild ? guild.members : this.client.users;
const index = guild ? guild.members.cache : this.client.users.cache;
let member = null;
if(/<@!?(\d{17,21})>/iy.test(str)) { //mentions
@ -54,7 +54,7 @@ class Resolver {
member = await index.fetch(matches[1]);
} catch(e) {
try {
member = await this.client.users.fetch(matches[1]);
member = await this.client.users.cache.fetch(matches[1]);
} catch(e) {} //eslint-disable-line no-empty
} //eslint-disable-line no-empty
}
@ -66,16 +66,17 @@ class Resolver {
member = await index.fetch(matches[1]);
} catch(e) {
try {
member = await this.client.users.fetch(matches[1]);
member = await this.client.users.cache.fetch(matches[1]);
} catch(e) {} //eslint-disable-line no-empty
} //eslint-disable-line no-empty
}
} else if(/(.{2,32})#(\d{4})/iy.test(str)) { //username#discrim
const matches = /(.{2,32})#(\d{4})/iy.exec(str);
member = guild
? guild.members.filter(m=>m.user.username === matches[1] && m.user.discriminator === matches[2]).first()
: this.client.users.filter(u=>u.username === matches[1] && u.discriminator === matches[2]).first();
? guild.members.cache.filter(m=>m.user.username === matches[1] && m.user.discriminator === matches[2]).first()
: this.client.users.cache.filter(u=>u.username === matches[1] && u.discriminator === matches[2]).first();
}
return member || null;
}
@ -100,13 +101,13 @@ class Resolver {
if(/<@!?([0-9]{17,21})>/.test(resolveable)) {
let id = resolveable.match(/<@!?([0-9]{17,21})>/)[1];
let user = await users.fetch(id).catch(err => { if(err.code === 10013) return false; else { console.warn(err); return false; } });
let user = await users.fetch(id).catch(err => { if(err.code === 10013) return false; else { this.client.logger.warn(err); return false; } });
if(user) resolved.push(user);
} else if(/(id\:)?([0-9]{17,21})/.test(resolveable)) {
let id = resolveable.match(/(id\:)?([0-9]{17,21})/)[2];
let user = await users.fetch(id).catch(err => { if(err.code === 10013) return false; else { console.warn(err); return false; } });
let user = await users.fetch(id).catch(err => { if(err.code === 10013) return false; else { this.client.logger.warn(err); return false; } });
if(user) resolved.push(user);
} else if(/^\@?([\S\s]{1,32})\#([0-9]{4})/.test(resolveable)) {
@ -114,29 +115,29 @@ class Resolver {
let m = resolveable.match(/^\@?([\S\s]{1,32})\#([0-9]{4})/);
let username = m[1].toLowerCase();
let discrim = m[2].toLowerCase();
let [ user ] = users.cache.filter(u => {
let user = users.cache.filter(u => {
return u.username.toLowerCase() === username && u.discriminator === discrim;
}).first(1);
}).first();
if(user) resolved.push(user);
} else if(!strict) {
let name = resolveable.toLowerCase();
let [ user ] = users.cache.filter(u => {
let user = users.cache.filter(u => {
return u.username.toLowerCase().includes(name);
}).first(1);
}).first();
if(user) resolved.push(user);
}
}
return resolved.length > 0 ? resolved : false;
return resolved.length ? resolved : false;
}
async resolveUser(resolveable, strict) {
let [ result ] = await this.resolveUsers([ resolveable ], strict);
let result = await this.resolveUsers([ resolveable ], strict);
return result;
}
@ -234,7 +235,16 @@ class Resolver {
let name = /^\#?([a-z0-9\-\_0]*)/i;
let id = /^\<\#([0-9]*)\>/i;
if (name.test(resolveable)) {
if (id.test(resolveable)) {
let match = resolveable.match(id);
let ch = match[1];
let channel = channels.resolve(ch);
if (channel) resolved.push(channel);
} else if (name.test(resolveable)) {
let match = resolveable.match(name);
let ch = match[1].toLowerCase();
@ -246,16 +256,7 @@ class Resolver {
if(channel) resolved.push(channel);
} else if(id.test(resolveable)) {
let match = resolveable.match(id);
let ch = match[1];
let channel = channels.resolve(ch);
if(channel) resolved.push(channel);
}
}
}
@ -288,16 +289,16 @@ class Resolver {
let match = resolveable.match(id);
let r_id = match[2];
let role = await roles.fetch(r_id).catch(console.error);
let role = await roles.fetch(r_id).catch(this.client.logger.error);
if(role) resolved.push(role);
} else {
let [ role ] = roles.cache.filter(r => {
let role = roles.cache.filter(r => {
if(!strict) return r.name.toLowerCase().includes(resolveable.toLowerCase());
return r.name.toLowerCase() === resolveable.toLowerCase();
}).first(1);
}).first();
if(role) resolved.push(role);

View File

@ -0,0 +1,162 @@
const { Command } = require('../../../../interfaces/');
const { stripIndents } = require('common-tags');
class GrantCommand extends Command {
constructor(client) {
super(client, {
name: 'grant',
module: 'administration',
usage: "<role|user> <permissions..>",
examples: [
"\"Server Moderators\" module:moderation",
"@nolan#2887 command:kick"
],
memberPermissions: ['ADMINISTRATOR', 'MANAGE_SERVER'],
showUsage: true,
guildOnly: true,
arguments: [
{
name: 'channel',
aliases: [
'channels'
],
type: 'CHANNEL',
types: ['FLAG', 'VERBAL'],
infinite: true
}
]
});
}
async execute(message, { params, args }) {
// console.log(args.channel);
const _permissions = await message.guild.permissions();
const [ parse, ...perms ] = params;
const resolveable = await this._parseResolveable(message, parse);
if(!resolveable) return undefined;
if(perms.length === 0) {
await message.respond(message.format('C_GRANT_MISSINGPERMPARAM'), { emoji: 'failure' });
return undefined;
}
const permissions = this.client.registry.components.filter(c=>
c.type === 'command'
|| c.type === 'module'
);
let parsed = [];
if(perms.join(' ') === 'all') {
parsed = this.client.registry.components.filter(c=>c.grantable && c.type === 'command').map(c=>c.resolveable);
} else {
for(const perm of perms) {
const search = permissions.filter(filterInexact(perm)).first();
if(!search) failed.push(perm);
if(search.type === 'module') {
for(const component of search.components.values()) {
if(component.type === 'command') parsed.push(component.resolveable);
//add check for grantable
}
} else {
//add check for grantable
parsed.push(search.resolveable);
}
}
}
if(!_permissions[resolveable.id]) {
_permissions[resolveable.id] = {
global: [],
channels: {}
};
}
let existing = _permissions[resolveable.id];
let pushed = [];
let failed = [];
if(args.channel) {
for(const channel of args.channel.value) {
const existingChannel = existing.channels[channel.id];
if(existingChannel) {
for(const perm of parsed) {
if(existingChannel.includes(perm)) {
failed.push(perm);
} else {
existingChannel.push(perm);
pushed.push(perm);
}
}
} else {
existing.channels[channel.id] = parsed;
pushed.concat(parsed)
}
}
} else {
for(const perm of parsed) {
if(existing.global.includes(perm)) {
failed.push(perm);
} else {
existing.global.push(perm);
pushed.push(perm);
}
}
}
delete _permissions._id; //some bullshit..
try {
await this.client.transactionHandler.send({
provider: 'mongodb',
request: {
type: 'updateOne',
collection: 'permissions',
query: {
guildId: message.guild.id
},
data: _permissions
}
});
} catch(error) {
await message.respond(message.format('C_GRANT_DATABASEERROR'), { emoji: 'failure' });
return undefined;
}
if(pushed.length > 0) {
return await message.respond(stripIndents`${message.format('C_GRANT_SUCCESS', { resolveable: resolveable.name || resolveable.user?.tag, permissions: pushed.map(p=>`\`${p}\``).join(', ') })}${args.channel ? ` ${message.format('C_GRANT_SUCCESSALT', { channels: args.channel.value.map(c=>`\`#${c.name}\``).join(', ')})}`: '.'}
${failed.length > 0 ? message.format('C_GRANT_SUCCESSFAILED', { resolveable: resolveable.user ? 'user' : 'role' }) : ''}`, { emoji: 'success' });
} else {
return await message.respond(message.format('C_GRANT_FAILED', { failed: failed.map(f=>`\`${f}\``).join(', '), resolveable: resolveable.user ? 'user' : 'role' }), { emoji: 'failure' })
}
}
async _parseResolveable(message, resolveable) {
let parsed = await this.client.resolver.resolveRoles(resolveable, message.guild);
if(!parsed) {
parsed = await this.client.resolver.resolveMembers(resolveable, message.guild);
if(!parsed) {
await message.respond(message.format('C_GRANT_RESOLVEERROR'), { emoji: 'failure' });
return null;
}
}
return parsed[0];
}
}
module.exports = GrantCommand;
const filterInexact = (search) => {
return 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))));
};

View File

@ -0,0 +1,40 @@
const { Command } = require('../../../../interfaces/');
const { stripIndents } = require('common-tags');
class GrantCommand extends Command {
constructor(client) {
super(client, {
name: 'permissions',
module: 'administration',
usage: "<role|user>",
aliases: [
'perms',
'permission',
'perm'
],
examples: [
"Server Moderators",
"@nolan#2887"
],
memberPermissions: ['ADMINISTRATOR', 'MANAGE_SERVER'],
guildOnly: true
});
}
async execute(message) {
await message.guild.permissions();
message.respond(`\`\`\`js
${JSON.stringify(message.guild._permissions)}\`\`\``);
}
}
module.exports = GrantCommand;

View File

@ -0,0 +1,189 @@
const { Command } = require('../../../../interfaces/');
class RevokeCommand extends Command {
constructor(client) {
super(client, {
name: 'revoke',
module: 'administration',
usage: "<role|user> <permissions..>",
examples: [
"\"Server Moderators\" module:moderation",
"@nolan#2887 command:kick",
"132620781791346688 moderation"
],
memberPermissions: ['ADMINISTRATOR'],
showUsage: true,
guildOnly: true,
arguments: [
{
name: 'channel',
aliases: [
'channels'
],
type: 'CHANNEL',
types: ['FLAG', 'VERBAL'],
infinite: true
}
]
});
}
async execute(message, { params, args }) {
const _permissions = await message.guild.permissions();
const [ parse, ...perms ] = params;
const resolveable = await this._parseResolveable(message, parse);
if((resolveable && !_permissions[resolveable?.id]) && !_permissions[parse]) {
await message.respond(message.format('C_REVOKE_RESOLVEERROR', { id: resolveable.id }), { emoji: 'failure' });
return undefined;
}
if(perms.length === 0) {
await message.respond(message.format('C_REVOKE_MISSINGPERMPARAM'), { emoji: 'failure' });
return undefined;
}
const permission = resolveable ? _permissions[resolveable.id] : _permissions[parse];
const permissions = this.client.registry.components.filter(c=>
c.type === 'command'
|| c.type === 'module'
);
let parsed = [];
if(perms[0] === 'all') {
parsed = this.client.registry.components.filter(c=>c.grantable && c.type === 'command').map(c=>c.resolveable);
} else {
for(const perm of perms) {
const search = permissions.filter(filterInexact(perm)).first();
if(search?.type === 'module') {
for(const component of search.components.values()) {
if(component.type === 'command') parsed.push(component.resolveable);
//add check for grantable
}
} else if (search?.type === 'command'){
//add check for grantable
parsed.push(search.resolveable);
} else {
if(args.channel) {
for(let channel of args.channel.value) {
if(permission.channels[channel.id].includes(perm)) parsed.push(perm);
}
} else {
if(permission.global.includes(perm)) parsed.push(perm);
}
}
}
}
let removed = [];
if(args.channel) {
for(const channel of args.channel.value) {
const existingChannel = permission.channels[channel.id];
if(existingChannel) {
for(const parse of parsed) {
const index = existingChannel.indexOf(parse);
if(index > -1) {
removed.push(parse);
permission.channels[channel.id].splice(index, 1);
}
}
if(existingChannel.length === 0) delete permission.channels[channel.id];
} else {
continue;
}
}
} else {
for(const parse of parsed) {
const index = permission.global.indexOf(parse);
if(index > -1) {
removed.push(parse);
permission.global.splice(index, 1);
}
}
}
//check for deletion, saves DB space.
//NOTE: DO NOT REMOVE THE _PERMISSIONS VARIABLE.
console.log(permission.global.length, Object.keys(permission.channels).length);
if(permission.global.length === 0
&& Object.keys(permission.channels).length === 0) {
try {
const blah = await this.client.transactionHandler.send({
provider: 'mongodb',
request: {
type: 'remove',
collection: 'permissions',
query: {
guildId: message.guild.id
}
}
});
console.log(blah);
} catch(error) {
this.client.logger.warn(`Attempted to delete collection permissions:${message.guild.id} but failed.`);
}
}
delete _permissions._id; //some bullshit..
if(permission.global.length === 0 && Object.keys(permission.channels).length === 0) {
try {
await this.client.transactionHandler.send({
provider: 'mongodb',
request: {
type: 'remove',
collection: 'permissions',
query: {
guildId: message.guild.id
}
}
});
} catch(error) {
this.client.logger.warn(`Attempted to delete collection permissions:${message.guild.id} but failed.`);
}
} else {
try {
await this.client.transactionHandler.send({
provider: 'mongodb',
request: {
type: 'updateOne',
collection: 'permissions',
query: {
guildId: message.guild.id
},
data: _permissions
}
});
} catch(error) {
await message.respond(message.format('C_REVOKE_DATABASEERROR'), { emoji: 'failure' });
return undefined;
}
}
}
async _parseResolveable(message, resolveable) {
let parsed = await this.client.resolver.resolveRoles(resolveable, message.guild)[0];
if(!parsed) {
parsed = await this.client.resolver.resolveMemberAndUser(resolveable, message.guild);
}
return parsed || null;
}
}
module.exports = RevokeCommand;
const filterInexact = (search) => {
return 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))));
};

View File

@ -1,101 +0,0 @@
const { Command, Argument } = require('../../../../interfaces/');
class GrantCommand extends Command {
constructor(client) {
super(client, {
name: 'grant',
module: 'administrator',
usage: "<role|user> <permissions..>",
examples: [
"\"Server Moderators\" module:moderation",
"@nolan#2887 command:kick"
],
memberPermissions: ['ADMINISTRATOR'],
showUsage: true,
guildOnly: true,
arguments: [
new Argument(client, {
name: 'channel',
aliases: [
'channels'
],
type: 'CHANNEL',
types: ['FLAG', 'VERBAL'],
infinite: true
})
]
});
}
async execute(message, { params, args }) {
const _permissions = await message.guild.permissions_();
const [ parse, ...perms ] = params;
const resolveable = await this._parseResolveable(message, parse);
if(perms.length === 0) {
await message.respond(message.format('C_GRANT_MISSINGPERMPARAM'), { emoji: 'failure' });
return undefined;
}
const permissions = this.client.registry.components.filter(c=>
c.type === 'command'
|| c.type === 'module'
);
let parsed = [];
let failed = [];
for(const perm of perms) {
const search = permissions.filter(filterInexact(perm)).first();
if(!search) failed.push(perm);
if(search.type === 'module') {
for(const component of search.components.values()) {
if(component.type === 'command') parsed.push(component.resolveable);
//add check for grantable
}
} else {
//add check for grantable
parsed.push(search.resolveable);
}
}
let data = {};
let existing = _permissions[resolveable.id];
if(existing) {
for(let perm of parsed) {
if(existing.includes(perm)) failed.push(perm);
else existing.push(perm);
}
} else {
existing = parsed;
}
data = existing;
}
async _parseResolveable(message, resolveable) {
let parsed = await this.client.resolver.resolveRoles(resolveable, message.guild);
if(!parsed) {
parsed = await this.client.resolver.resolveMembers(resolveable, message.guild);
if(!parsed) {
await message.respond(message.format('C_GRANT_RESOLVEERROR'), { emoji: 'failure' });
return null;
}
}
return parsed[0];
}
}
module.exports = GrantCommand;
const filterInexact = (search) => {
return 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))));
};

View File

@ -1,37 +0,0 @@
const { Command, Argument } = require('../../../../interfaces/');
class RevokeCommand extends Command {
constructor(client) {
super(client, {
name: 'revoke',
module: 'administrator',
usage: "<role|user> <permissions..>",
examples: [
"\"Server Moderators\" module:moderation",
"@nolan#2887 command:kick"
],
memberPermissions: ['ADMINISTRATOR'],
showUsage: true,
guildOnly: true,
arguments: [
new Argument(client, {
name: 'channel',
type: 'CHANNEL',
types: ['FLAG', 'VERBAL'],
})
]
});
}
async execute(message, { params, args }) {
}
}
module.exports = RevokeCommand;

View File

@ -16,14 +16,14 @@ class ComponentCommand extends Command {
],
usage: '[component..]',
arguments: [
// new Argument(client, {
// {
// name: 'reload',
// type: 'STRING',
// types: ['FLAG'],
// aliases: [ 'r' ],
// description: "Reloads the language library",
// default: 'all'
// })
// }
],
});
@ -38,7 +38,7 @@ class ComponentCommand extends Command {
const method = params.shift().toLowerCase();
let response;
if (method === 'reload')
if (method === 'reload' || method === 'r')
response = this._handleReload(params);
else if (method === 'enable')
response = this._handleDisableEnable(params.shift().toLowerCase(), true);

View File

@ -3,7 +3,7 @@ const { username } = require('os').userInfo();
let _storage = null; //eslint-disable-line no-unused-vars
const { Command, Argument } = require('../../../../interfaces/');
const { Command } = require('../../../../interfaces/');
@ -21,18 +21,18 @@ class Evaluate extends Command {
usage: '<code>',
restricted: true,
arguments: [
new Argument(client, {
{
name: 'log',
type: 'BOOLEAN',
types: ['FLAG'],
description: "Logs the output in the console."
}),
new Argument(client, {
},
{
name: 'hide',
type: 'BOOLEAN',
types: ['FLAG'],
description: "Hides the output from the channel."
})
}
],
showUsage: true
});

View File

@ -14,12 +14,12 @@ class CommandsCommand extends Command {
],
usage: '[module]',
arguments: [
// new Argument(client, {
// {
// name: 'user',
// type: 'BOOLEAN',
// types: ['VERBAL', 'FLAG'],
// default: true
// }),
// },
]
});

View File

@ -1,6 +1,6 @@
const { stripIndents } = require('common-tags');
const { Command, Argument } = require('../../../../interfaces/');
const { Command } = require('../../../../interfaces/');
class PingCommand extends Command {
@ -11,26 +11,34 @@ class PingCommand extends Command {
module: 'utility',
aliases: ['args', 'arg', 'argument'],
arguments: [
new Argument(client, {
{
name: 'apple',
type: 'BOOLEAN',
types: ['VERBAL', 'FLAG'],
default: true
}),
new Argument(client, {
},
{
name: 'banana',
aliases: ['bans', 'bananas'],
type: 'INTEGER',
types: ['FLAG', 'VERBAL'],
default: 0
}),
new Argument(client, {
},
{
name: 'carrot',
aliases: ['cars', 'carrots'],
type: 'STRING',
type: 'CHANNEL',
required: true,
types: ['FLAG', 'VERBAL']
})
types: ['FLAG', 'VERBAL'],
infinite: true
},
{
name: 'format',
type: 'STRING',
types: ['VERBAL', 'FLAG'],
options: [ 'webp', 'png', 'jpeg', 'jpg', 'gif' ],
default: 'webp'
}
],
restricted: true,
archivable: false

View File

@ -0,0 +1,63 @@
const { Command } = require('../../../../interfaces/');
class AvatarCommand extends Command {
constructor(client) {
super(client, {
name: 'avatar',
module: 'utility',
aliases: [
'pfp'
],
arguments: [
{
name: 'size',
type: 'INTEGER',
types: ['VERBAL', 'FLAG'],
options: [ 16, 32, 64, 128, 256, 512, 1024, 2048 ],
default: 512,
required: true
},
{
name: 'format',
type: 'STRING',
types: ['VERBAL', 'FLAG'],
options: [ 'webp', 'png', 'jpeg', 'jpg', 'gif' ],
default: 'webp',
required: true
}
]
});
this.client = client;
}
async execute(message, { params, args }) {
let user = params.length ? await this.client.resolver.resolveUser(params[0]) : message.author;
if (!user) user = message.author;
let avatar = null;
try {
avatar = user.displayAvatarURL({ format: args.format?.value || 'webp', size: args.size?.value || 512, dynamic: true });
} catch(error) {
message.respond(message.format('C_AVATAR_FORMATERROR'), { emoji: 'failure' });
return undefined;
}
return await message.embed({
author: {
name: user.tag
},
image: {
url: avatar
}
});
}
}
module.exports = AvatarCommand;

View File

@ -1,4 +1,4 @@
const { Command, Argument } = require('../../../../interfaces/');
const { Command } = require('../../../../interfaces/');
const { stripIndents } = require('common-tags');
@ -15,18 +15,18 @@ class SettingCommand extends Command {
'set'
],
arguments: [
new Argument(client, {
{
name: 'user',
type: 'BOOLEAN',
types: ['VERBAL', 'FLAG'],
default: true
}),
new Argument(client, {
},
{
name: 'all',
type: 'BOOLEAN',
types: ['VERBAL', 'FLAG'],
default: true
})
}
],
showUsage: true
});

View File

@ -1,4 +1,4 @@
const { Command, Argument } = require('../../../../interfaces/');
const { Command } = require('../../../../interfaces/');
const similarity = require('similarity');
class UserCommand extends Command {
@ -11,13 +11,12 @@ class UserCommand extends Command {
description: "Display information about user.",
guildOnly: true,
arguments: [
new Argument(client, {
{
name: 'search',
type: 'STRING',
types: ['FLAG', 'VERBAL'],
requiredValue: true
}),
}
]
});

View File

@ -1,7 +1,7 @@
const { stripIndents } = require('common-tags');
const escapeRegex = require('escape-string-regexp');
const { Observer } = require('../../../interfaces/');
const { Argument, Observer } = require('../../../interfaces/');
class CommandHandler extends Observer {
@ -32,11 +32,8 @@ class CommandHandler extends Observer {
|| message.webhookID
|| message.author.bot
|| (message.guild && (!message.guild.available || message.guild.banned))) return undefined;
await message.author.settings();
if (message.guild) {
await message.guild.settings();
if (!message.member) await message.guild.members.fetch(message.author.id);
}
@ -80,6 +77,8 @@ class CommandHandler extends Observer {
const command = this.client.resolver.components(commandName, 'command', true)[0];
if(!command) return null;
message._caller = commandName; //Used for hidden commands as aliases.
//Eventually search for custom commands here.
@ -156,7 +155,7 @@ class CommandHandler extends Observer {
const { shortFlags, longFlags, keys } = await this._createFlags(command.arguments);
const regex = new RegExp(`([0-9]*)(${Object.keys(longFlags).map(k=>escapeRegex(k)).join('|')})([0-9]*)`, 'i');
let parsedArguments = [];
let params = [];
@ -207,7 +206,7 @@ class CommandHandler extends Observer {
currentArgument = longFlags[match[2]];
if(params.length > 0 && ['INTEGER', 'FLOAT'].includes(currentArgument.type)) { //15 pts
const lastItem = params[params.length-1];
const beforeError = await this._handleTypeParsing(currentArgument, lastItem);
const beforeError = await this._handleTypeParsing(currentArgument, lastItem, message.guild);
if(beforeError) {
continue;
} else {
@ -219,7 +218,7 @@ class CommandHandler extends Observer {
}
}
const value = match[1] || match[3];
const error = await this._handleTypeParsing(currentArgument, value);
const error = await this._handleTypeParsing(currentArgument, value, message.guild);
if(value) {
if(error) {
if(currentArgument.required) {
@ -240,7 +239,7 @@ class CommandHandler extends Observer {
}
} else {
if(currentArgument) {
const error = await this._handleTypeParsing(currentArgument, word);
const error = await this._handleTypeParsing(currentArgument, word, message.guild);
if(error) {
if(currentArgument.default !== null) {
params.push(word);
@ -276,7 +275,7 @@ class CommandHandler extends Observer {
} else {
const lastArgument = parsedArguments[parsedArguments.length-1];
if(lastArgument && lastArgument.type === 'BOOLEAN' && lastArgument.value === lastArgument.default) {
const error = await this._handleTypeParsing(lastArgument, word);
const error = await this._handleTypeParsing(lastArgument, word, message.guild);
if(!error) continue;
}
params.push(word);
@ -286,6 +285,8 @@ class CommandHandler extends Observer {
}
}
if(currentArgument) parsedArguments.push(currentArgument);
const blah = parsedArguments.filter(a=>a.requiredArgument && !a.value);
const missingArgument = blah[0];
if(missingArgument) return this._handleError({ type: 'argument', info: { argument: missingArgument, missing: true }, message });
@ -299,11 +300,11 @@ class CommandHandler extends Observer {
}
async _handleTypeParsing(argument, string) {
async _handleTypeParsing(argument, string, guild) {
const parse = async (argument, string) => {
const parse = async (argument, string, guild) => {
const { error, value } = await this.constructor.parseType(argument.type, string); //Cannot access static functions through "this".
const { error, value } = await this.constructor.parseType(argument.type, string, this.client.resolver, guild); //Cannot access static functions through "this".
if(error) return { error: true };
if(['INTEGER', 'FLOAT'].includes(argument.type)) {
@ -316,13 +317,26 @@ class CommandHandler extends Observer {
}
}
if(argument.options.length > 0) {
let found = null;
for(const option of argument.options) {
if(option !== value) {
continue;
} else {
found = option;
}
}
if(!found) return { error: true };
else return { error: false, value: found };
}
return { error: false, value };
};
const { error, value } = await parse(argument, string);
const { error, value } = await parse(argument, string, guild);
if(!error) {
if(!error && value !== undefined) {
argument.infinite
? argument.value.push(value)
: argument.value = value;
@ -338,7 +352,8 @@ class CommandHandler extends Observer {
let longFlags = {};
let keys = [];
for(const arg of args) {
for(let arg of args) {
arg = new Argument(this.client, arg)
let letters = [];
let names = [ arg.name, ...arg.aliases ];
@ -436,7 +451,7 @@ class CommandHandler extends Observer {
}
static async parseType(type, str) { //this is in the class for a reason, will soon reference to a user resolver etc.
static async parseType(type, str, resolver, guild) { //this is in the class for a reason, will soon reference to a user resolver etc.
//INTEGER AND FLOAT ARE SAME FUNCTION
@ -471,13 +486,15 @@ class CommandHandler extends Observer {
MEMBER: (str) => { //eslint-disable-line no-unused-vars
},
CHANNEL: (str) => { //eslint-disable-line no-unused-vars
CHANNEL: async (str, resolver, guild) => { //eslint-disable-line no-unused-vars
const channels = await resolver.resolveChannels(str, guild, true);
if(channels.length === 0) return { error: true };
else return { error: false, value: channels[0] }
}
};
return await types[type](str);
return await types[type](str, resolver, guild);
}

View File

@ -16,23 +16,17 @@ const Guild = Structures.extend('Guild', (Guild) => {
//Fetch and cache settings
async settings() {
if (!this._settings)
this._settings = this.client.transactionHandler.send({ provider: 'mongodb', request: { collection: 'guilds', type: 'findOne', query: { guildId: this.id } } });
if (this._settings instanceof Promise)
this._settings = await this._settings || null;
if (!this._settings)
this._settings = this.client.defaultConfig;
if(!this._settings) this._settings = this.client.transactionHandler.send({ provider: 'mongodb', request: { collection: 'guilds', type: 'findOne', query: { guildId: this.id } } });
if(this._settings instanceof Promise) this._settings = await this._settings || null;
if(!this._settings) this._settings = { guildId: this.id, ...this.client.defaultConfig } ;
return this._settings;
}
//Fetch and cache perms
async permissions_() {
if (!this._permissions)
this._permissions = this.client.transactionHandler.send({ provider: 'mongodb', request: { collection: 'permissions', type: 'findOne', query: { guildId: this.id } } });
if (this._permissions instanceof Promise)
this._permissions = await this._permissions || null;
if (!this._permissions)
this._permissions = {};
async permissions() {
if(!this._permissions) this._permissions = this.client.transactionHandler.send({ provider: 'mongodb', request: { collection: 'permissions', type: 'findOne', query: { guildId: this.id } } });
if(this._permissions instanceof Promise) this._permissions = await this._permissions || null;
if(!this._permissions) this._permissions = { guildId: this.id };
return this._permissions;
}
@ -58,7 +52,7 @@ const Guild = Structures.extend('Guild', (Guild) => {
}
async _dbUpdateOne(data, collection) { //Update property (upsert true) - updateOne
var index = this.dbIndex(collection);
var index = this.dbIndex(collection); //eslint-disable-line no-unused-vars
try {
await this.client.transactionHandler.send({
provider: 'mongodb',
@ -108,7 +102,7 @@ const Guild = Structures.extend('Guild', (Guild) => {
}
}
dbIndex(collection) {
_dbIndex(collection) {
return {
'guilds': this._settings,
'permissions': this._permissions

View File

@ -24,7 +24,7 @@ const Message = Structures.extend('Message', (Message) => {
format(index, parameters = { }, code = false) {
let language = this.author._settings.locale || 'en_us';
let language = 'en_us';
if(this.guild && this.guild._settings.locale) language = this.guild._settings.locale;
parameters.prefix = this.guild?.prefix || this.client._options.bot.prefix;
@ -144,7 +144,7 @@ const Message = Structures.extend('Message', (Message) => {
if(component.arguments.length > 0) {
fields.push({
name: `${this.format('GENERAL_ARGUMENTS')}`,
value: component.arguments.map(a=>`${a.name}: ${this.format(`A_${a.name.toUpperCase}_DESCRIPTION`)}`)
value: component.arguments.map(a=>`${a.name}: ${this.format(`A_${a.name.toUpperCase()}_${component.name.toUpperCase()}_DESCRIPTION`)}`)
});
}
@ -154,7 +154,7 @@ const Message = Structures.extend('Message', (Message) => {
icon_url: this.client.user.avatarURL()
},
description: stripIndents`\`${prefix}${component.name}${component.usage ? ` ${component.usage}` : ''}\`
${this.format(`C_${component.name.toUpperCase()}_DESCRIPTION`)}${component.guildOnly ? ' *(guild-only)*' : ''}`,
${this.format(component.description)}${component.guildOnly ? ' *(guild-only)*' : ''}`,
fields
};

View File

@ -24,6 +24,8 @@ class Argument {
this.infinite = Boolean(options.infinite); //Accepts infinite amount of arguments e.g. -u @nolan @navy. If false, will only detect one user.
// Will turn value into an array instead of a string!!!
this.options = options.options || [];
this.min = typeof options.min === 'number' ? options.min : null; //Min/max will only be used for INTEGER/FLOAT types.
this.max = typeof options.max === 'number' ? options.max : null;
@ -31,10 +33,8 @@ class Argument {
this.value = this.infinite ? [] : null; //The value provided to the flag; assigned in the command handler.
}
}
module.exports = Argument;

View File

@ -18,7 +18,7 @@ class Command extends Component {
this.module = opts.module;
this.aliases = opts.aliases || [];
this.description = `C_${opts.name}_DESCRIPTION`;
this.description = `C_${opts.name.toUpperCase()}_DESCRIPTION`;
this.usage = opts.usage || '';
this.examples = opts.examples || [];