forked from Galactic/galactic-bot
commit
a146997534
44
Manager.js
44
Manager.js
@ -1,4 +1,5 @@
|
||||
const { EventEmitter } = require('events');
|
||||
const { inspect } = require('util');
|
||||
|
||||
const ShardManager = require('./middleware/ShardManager.js');
|
||||
const Logger = require('./middleware/logger/Logger.js');
|
||||
@ -13,6 +14,7 @@ class Manager extends EventEmitter {
|
||||
this.logger = new Logger(this);
|
||||
|
||||
this._built = false;
|
||||
this.readyAt = null;
|
||||
|
||||
this.shardManager.on('message', this._handleMessage.bind(this));
|
||||
// this.on('built', () => {
|
||||
@ -23,8 +25,9 @@ class Manager extends EventEmitter {
|
||||
|
||||
_handleMessage(shard, message) {
|
||||
|
||||
if(message._logger) return this.logger._handleMessage(shard, message);
|
||||
if(message._webhook) return undefined; //todo
|
||||
if (message._mEval) return this.eval(shard, message);
|
||||
if (message._logger) return this.logger._handleMessage(shard, message);
|
||||
if (message._webhook) return undefined; //todo
|
||||
}
|
||||
|
||||
async build() {
|
||||
@ -33,9 +36,46 @@ class Manager extends EventEmitter {
|
||||
|
||||
this._built = true;
|
||||
this.emit('built');
|
||||
this.readyAt = Date.now();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {*} shard The shard from which the eval came and to which it will be returned
|
||||
* @param {*} script The script to be executed
|
||||
* @memberof Manager
|
||||
* @private
|
||||
*/
|
||||
async eval(shard, { script }) {
|
||||
|
||||
this.logger.write('info', `Incoming manager eval from shard ${shard.id}:\n${script}`);
|
||||
let result = null,
|
||||
error = null;
|
||||
|
||||
try {
|
||||
// eslint-disable-next-line no-eval
|
||||
result = eval(script);
|
||||
if (result instanceof Promise) result = await result;
|
||||
//if(typeof result !== 'string') result = inspect(result);
|
||||
} catch (err) {
|
||||
error = err.stack || err;
|
||||
}
|
||||
|
||||
return shard.send({
|
||||
_evalResult: true,
|
||||
script,
|
||||
result,
|
||||
error
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
get uptime() {
|
||||
return this.readyAt !== null ? Date.now() - this.readyAt : 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = Manager;
|
@ -138,7 +138,7 @@ class Shard extends EventEmitter {
|
||||
if(!message || message._eval !== script) return;
|
||||
child.removeListener('message', listener);
|
||||
this._evals.delete(script);
|
||||
if(!message._error) resolve(message._result); else reject(new Error(message._error));
|
||||
if(!message._error) resolve(message._result); else reject(new Error(message._error.stack));
|
||||
};
|
||||
child.on('message', listener);
|
||||
|
||||
|
@ -13,7 +13,7 @@ const RateLimiter = require('./RateLimiter.js');
|
||||
const StorageManager = require('../storage/StorageManager.js');
|
||||
const ModerationManager = require('../moderation/ModerationManager.js');
|
||||
|
||||
const { Guild, GuildMember, User, Message } = require('../../structure/extensions/'); //eslint-disable-line no-unused-vars
|
||||
const { Guild, GuildMember, User, Message, TextChannel, Role } = require('../../structure/extensions/'); //eslint-disable-line no-unused-vars
|
||||
const { Command, Observer, Inhibitor, Setting } = require('../../structure/interfaces/');
|
||||
const { DefaultGuild } = require('../../util/defaults/');
|
||||
|
||||
@ -40,6 +40,8 @@ class DiscordClient extends Client {
|
||||
//TODO: Default config for users and guilds.
|
||||
this._defaultConfig = null;
|
||||
|
||||
this._evals = new Map();
|
||||
|
||||
process.on('message', this._handleMessage.bind(this));
|
||||
|
||||
}
|
||||
@ -70,9 +72,41 @@ class DiscordClient extends Client {
|
||||
|
||||
}
|
||||
|
||||
async resolveUsers() {
|
||||
|
||||
return this.resolver.resolveUsers(...arguments);
|
||||
|
||||
}
|
||||
|
||||
async resolveUser() {
|
||||
|
||||
return this.resolver.resolveUser(...arguments);
|
||||
|
||||
}
|
||||
|
||||
async _handleMessage(message) {
|
||||
//Handle misc. messages.
|
||||
|
||||
if (message._evalResult) this.evalResult(message);
|
||||
}
|
||||
|
||||
async managerEval(script) {
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
|
||||
this._evals.set(script, { resolve, reject });
|
||||
process.send({ _mEval: true, script });
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
evalResult({ script, result, error }) {
|
||||
|
||||
const promise = this._evals.get(script);
|
||||
if (result) promise.resolve(result);
|
||||
else promise.reject(error);
|
||||
this._evals.delete(script);
|
||||
|
||||
}
|
||||
|
||||
get defaultConfig() {
|
||||
|
@ -1,4 +1,5 @@
|
||||
const timestring = require('timestring');
|
||||
const timestring = require('timestring');
|
||||
const moment = require('moment');
|
||||
|
||||
class Resolver {
|
||||
|
||||
@ -15,6 +16,7 @@ class Resolver {
|
||||
* @returns
|
||||
* @memberof Resolver
|
||||
*/
|
||||
|
||||
resolveComponent(arg, strict = true, type = 'any') {
|
||||
|
||||
const string = arg.toLowerCase();
|
||||
@ -27,6 +29,32 @@ class Resolver {
|
||||
|
||||
}
|
||||
|
||||
resolveCases(str = '', max = 0) {
|
||||
|
||||
const cases = [];
|
||||
|
||||
const matches = str.match(/(\d{1,6})(?:\.\.\.?|-)(\d{1,6})?/iu);
|
||||
if(matches) {
|
||||
const [ , first, second ] = matches;
|
||||
let difference = Math.abs((second ? second : max) - parseInt(first));
|
||||
if(difference+parseInt(first) > max) difference = max;
|
||||
new Array(difference+1).fill(0)
|
||||
.forEach((item, i) => {
|
||||
const number = i+parseInt(first);
|
||||
if(number <= max) cases.push(i+parseInt(first));
|
||||
});
|
||||
} else {
|
||||
const split = str.split(' ');
|
||||
for(const string of split) {
|
||||
const number = parseInt(string);
|
||||
if(number <= max && !cases.includes(number)) cases.push(number);
|
||||
}
|
||||
}
|
||||
|
||||
return cases;
|
||||
|
||||
}
|
||||
|
||||
components(str = '', type, exact = true) {
|
||||
|
||||
const string = str.toLowerCase();
|
||||
@ -360,6 +388,7 @@ class Resolver {
|
||||
* @param {array<string>} [resolveables=[]] an array of channel resolveables (name, id)
|
||||
* @param {guild} guild the guild in which to look for channels
|
||||
* @param {boolean} [strict=false] whether or not partial names are resolved
|
||||
* @param {function} [filter=()] filter the resolving channels
|
||||
* @returns {array<GuildChannel> || false} an array of guild channels or false if none were resolved
|
||||
* @memberof Resolver
|
||||
*/
|
||||
@ -484,6 +513,20 @@ class Resolver {
|
||||
return time;
|
||||
}
|
||||
|
||||
resolveDate(string) {
|
||||
let date = null;
|
||||
const matches = string.match(/([0-9]{4}(?:\/|-)[0-9]{2}(?:\/|-)[0-9]{2})/gimu); //YYYY-MM-DD is REQUIRED.
|
||||
if(matches && matches.length > 0) {
|
||||
try {
|
||||
const string = matches[0].replace(/\//giu, '-');
|
||||
date = moment(string);
|
||||
} catch(error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return date;
|
||||
}
|
||||
|
||||
|
||||
async infinite(args = [], resolvers = [], strict, guild) {
|
||||
let parsed = [], //eslint-disable-line prefer-const
|
||||
|
@ -1,6 +1,6 @@
|
||||
const { Command } = require('../../../../interfaces/');
|
||||
const { GuildMember } = require('../../../../extensions/');
|
||||
const { Emojis } = require('../../../../../util/');
|
||||
const { Emojis, Util } = require('../../../../../util/');
|
||||
|
||||
class GrantCommand extends Command {
|
||||
|
||||
@ -131,7 +131,7 @@ class GrantCommand extends Command {
|
||||
|
||||
|
||||
return message.respond(message.format('C_GRANT_SUCCESS', {
|
||||
targets: parsed.map((p) => p instanceof GuildMember ? `**${p.user.tag}**` : `**${p.name}**`).join(' '),
|
||||
targets: parsed.map((p) => p instanceof GuildMember ? `**${Util.escapeMarkdown(p.user.tag)}**` : `**${Util.escapeMarkdown(p.name)}**`).join(' '),
|
||||
permissions: parsedPermissions.map((p) => `\`${p}\``).join(', '),
|
||||
channel: args.channel ? ` ${message.format('C_GRANT_SUCCESSCHANNELS', { channels: args.channel.value.map((c) => `**#${c.name}**`).join(', '), plural: args.channel.value.length === 1 ? '' : 's' })}` : ''
|
||||
}), {
|
||||
|
@ -7,6 +7,7 @@ class ImportCommand extends Command {
|
||||
super(client, {
|
||||
name: 'import',
|
||||
module: 'administration',
|
||||
tags: ['log', 'logs', 'logging'],
|
||||
memberPermissions: ['ADMINISTRATOR'],
|
||||
archivable: false,
|
||||
restricted: true
|
||||
|
@ -1,8 +1,13 @@
|
||||
const { Role, MessageAttachment } = require('discord.js');
|
||||
const { Command } = require('../../../../interfaces/');
|
||||
|
||||
const { Util, Emojis } = require('../../../../../util/');
|
||||
const { stripIndents } = require('common-tags');
|
||||
|
||||
const Constants = {
|
||||
pageSize: 9
|
||||
};
|
||||
|
||||
class PermissionsCommand extends Command {
|
||||
|
||||
constructor(client) {
|
||||
@ -10,7 +15,7 @@ class PermissionsCommand extends Command {
|
||||
super(client, {
|
||||
name: 'permissions',
|
||||
module: 'administration',
|
||||
usage: "<list|role-name|user-name>",
|
||||
usage: "['list'|role|user]",
|
||||
aliases: [
|
||||
'perms',
|
||||
'permission',
|
||||
@ -23,15 +28,7 @@ class PermissionsCommand extends Command {
|
||||
],
|
||||
arguments: [
|
||||
{
|
||||
name: 'user',
|
||||
aliases: ['users'],
|
||||
type: 'BOOLEAN',
|
||||
types: ['VERBAL', 'FLAG'],
|
||||
default: true
|
||||
},
|
||||
{
|
||||
name: 'raw',
|
||||
aliases: ['json'],
|
||||
name: 'export',
|
||||
type: 'BOOLEAN',
|
||||
types: ['VERBAL', 'FLAG'],
|
||||
default: true
|
||||
@ -46,110 +43,149 @@ class PermissionsCommand extends Command {
|
||||
async execute(message, { params, args }) {
|
||||
|
||||
const permissions = await message.guild.permissions();
|
||||
if(args.raw) {
|
||||
|
||||
if(args.export) {
|
||||
await this._displayRaw(message, permissions);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if(params.length === 0) {
|
||||
await this._showPermissions(message, Boolean(args.user));
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if(params[0] === 'list') {
|
||||
await this._listAvailablePermissions(message);
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
const parameters = params.join(' ');
|
||||
let resolveable = await this._parseResolveable(message, parameters);
|
||||
if(resolveable.user) resolveable = resolveable.user;
|
||||
const type = resolveable.tag ? 'user' : 'role';
|
||||
const permission = permissions[resolveable?.id || parameters];
|
||||
if(resolveable && !permission) {
|
||||
await message.respond(message.format('C_PERMISSIONS_PERMISSIONSNOTFOUND', { resolveable: resolveable.tag || resolveable.name, type, they: type === 'user' ? 'they ' : '' }), { emoji: 'failure' });
|
||||
return undefined;
|
||||
}
|
||||
if(resolveable) {
|
||||
if(resolveable.user) resolveable = resolveable.user;
|
||||
|
||||
if(!permission) {
|
||||
await message.respond(message.format('C_PERMISSIONS_NOTFOUND'), { emoji: 'failure' });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const embed = {
|
||||
author: {
|
||||
name: `${resolveable?.user?.tag || resolveable?.tag || resolveable?.name || parameters}'s Permissions`,
|
||||
icon_url: resolveable.displayAvatarURL ? resolveable.displayAvatarURL() : message.guild.iconURL()
|
||||
},
|
||||
description: `${message.format('C_PERMISSIONS_GLOBAL', { permissions: permission.global.length > 0 ? this._displayNames(permission.global).map((p) => `\`${p}\``).join(', ') : "`N/A`" })} ${Object.values(permission.channels).length > 0 ? message.format('C_PERMISSIONS_GLOBALALT') : ''}`,
|
||||
fields: []
|
||||
};
|
||||
|
||||
let update = false;
|
||||
for(const [channelId, perms] of Object.entries(permission.channels)) {
|
||||
const channel = await this.client.resolver.resolveChannel(channelId, true, message.guild);
|
||||
if(!channel) {
|
||||
delete permission.channels[channelId];
|
||||
update = true;
|
||||
continue;
|
||||
} else {
|
||||
if(embed.fields.length === 25) {
|
||||
embed.description += `\n${message.format('C_PERMISSIONS_MAXFIELDS')}`;
|
||||
break;
|
||||
const type = resolveable.tag ? 'user' : 'role';
|
||||
const permission = permissions[resolveable?.id || parameters];
|
||||
|
||||
if(resolveable && !permission) {
|
||||
await message.respond(message.format('C_PERMISSIONS_PERMISSIONSNOTFOUND', { resolveable: resolveable.tag || resolveable.name, type, they: type === 'user' ? 'they ' : '' }), { emoji: 'failure' });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if(!permission) {
|
||||
await message.respond(message.format('C_PERMISSIONS_NOTFOUND'), { emoji: 'failure' });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const embed = {
|
||||
author: {
|
||||
name: `${resolveable?.user?.tag || resolveable?.tag || resolveable?.name || parameters}'s Permissions`,
|
||||
icon_url: resolveable.displayAvatarURL ? resolveable.displayAvatarURL() : message.guild.iconURL() //eslint-disable-line camelcase
|
||||
},
|
||||
description: `${message.format('C_PERMISSIONS_GLOBAL', { permissions: permission.global.length > 0 ? this._displayNames(permission.global).map((p) => `\`${p}\``).join(', ') : "`N/A`" })} ${Object.values(permission.channels).length > 0 ? message.format('C_PERMISSIONS_GLOBALALT') : ''}`,
|
||||
fields: []
|
||||
};
|
||||
|
||||
let update = false;
|
||||
for(const [channelId, perms] of Object.entries(permission.channels)) {
|
||||
const channel = await this.client.resolver.resolveChannel(channelId, true, message.guild);
|
||||
if(!channel) {
|
||||
delete permission.channels[channelId];
|
||||
update = true;
|
||||
continue;
|
||||
} else {
|
||||
if(embed.fields.length === 25) {
|
||||
embed.description += `\n${message.format('C_PERMISSIONS_MAXFIELDS')}`;
|
||||
break;
|
||||
}
|
||||
embed.fields.push({
|
||||
name: `#${channel.name}`,
|
||||
value: this._displayNames(perms).map((p) => `\`${p}\``).join(', ')
|
||||
});
|
||||
}
|
||||
embed.fields.push({
|
||||
name: `#${channel.name}`,
|
||||
value: this._displayNames(perms).map((p) => `\`${p}\``).join(', ')
|
||||
});
|
||||
}
|
||||
if(update) {
|
||||
delete permissions._id;
|
||||
try {
|
||||
await this.client.storageManager.mongodb.permissions.updateOne(
|
||||
{ guildId: message.guild.id },
|
||||
permissions
|
||||
);
|
||||
} catch(error) {
|
||||
this.client.logger.error(`Error removing channel permissions to ${message.guild.id}:\n${error.stack || error}`);
|
||||
}
|
||||
}
|
||||
|
||||
return message.embed(embed);
|
||||
}
|
||||
if(update) {
|
||||
delete permissions._id;
|
||||
try {
|
||||
await this.client.storageManager.mongodb.permissions.updateOne(
|
||||
{ guildId: message.guild.id },
|
||||
permissions
|
||||
);
|
||||
} catch(error) {
|
||||
this.client.logger.error(`Error removing channel permissions to ${message.guild.id}:\n${error.stack || error}`);
|
||||
|
||||
//End of displaying user/role permissions.
|
||||
|
||||
const _permissions = [];
|
||||
for(const [key, value] of Object.entries(permissions)) {
|
||||
if(value?.global?.length === 0 && Object.keys(value?.channels).length === 0) {
|
||||
await this._deletePermission(message.guild, key);
|
||||
continue;
|
||||
}
|
||||
|
||||
if(!Number.isNaN(parseInt(key))) _permissions.push({ ...value, id: key });
|
||||
}
|
||||
|
||||
let currentPage = 1;
|
||||
if(parameters.length > 0) {
|
||||
const number = parseInt(parameters[0]);
|
||||
if(!Number.isNaN(number) && number > 1) {
|
||||
currentPage = number;
|
||||
}
|
||||
}
|
||||
|
||||
return message.embed(embed);
|
||||
|
||||
const size = _permissions.length;
|
||||
if(size === 0) {
|
||||
return message.respond(message.format('C_PERMISSIONS_NOPERMISSIONS'), {
|
||||
emoji: 'failure'
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
let { items, page, maxPage } = Util.paginate(_permissions, currentPage, Constants.pageSize); //eslint-disable-line prefer-const
|
||||
|
||||
async _showPermissions(message, user = false) {
|
||||
const embed = {
|
||||
author: {
|
||||
name: message.format('C_PERMISSIONS_SHOWTITLE', { user }, true),
|
||||
name: `Guild Permissions`,
|
||||
icon_url: message.guild.iconURL() //eslint-disable-line camelcase
|
||||
},
|
||||
description: message.format('C_PERMISSIONS_SHOWDESCRIPTION', { resolve: user ? 'user' : 'role' }),
|
||||
fields: []
|
||||
fields: [],
|
||||
footer: {
|
||||
text: `• Page ${page}/${maxPage} | ${size} Results`
|
||||
}
|
||||
};
|
||||
|
||||
for(const item of items) {
|
||||
item.resolveable = await this._parseResolveable(message, item.id);
|
||||
item.permissions = this._displayNames(item.global);
|
||||
}
|
||||
|
||||
const display = (items) => {
|
||||
items = items.sort((a, b) => b.permissions.length - a.permissions.length);
|
||||
for(const item of items) {
|
||||
const field = {
|
||||
name: `${item.resolveable instanceof Role ? Emojis.role : Emojis.member} ${Util.escapeMarkdown(item.resolveable.display)}`,
|
||||
value: item.permissions.map((n) => `\`${n}\``).join('\n'),
|
||||
inline: true
|
||||
};
|
||||
|
||||
const channels = Object.keys(item.channels).length;
|
||||
if(channels > 0) {
|
||||
field.value += `\n\`..${channels} channel${channels === 1 ? '' : 's'}\``;
|
||||
}
|
||||
|
||||
embed.fields.push(field);
|
||||
}
|
||||
};
|
||||
|
||||
const permissions = message.guild._permissions;
|
||||
for(const [id, value] of Object.entries(permissions)) {
|
||||
if(id === '_id' || id === 'guildId') continue;
|
||||
const item = await this.client.resolver[user ? 'resolveUser' : 'resolveRole'](id, true, message.guild); //dont kill me
|
||||
if(item instanceof Role && user
|
||||
|| !user && !(item instanceof Role)
|
||||
|| !item) continue;
|
||||
if(embed.fields.length === 25) {
|
||||
embed.description += `\n${message.format('C_PERMISSIONS_MAXFIELDS')}`;
|
||||
break;
|
||||
}
|
||||
const name = item?.user?.tag || item?.tag || item?.name || id; //please dont kill me again
|
||||
const channels = Object.values(value.channels).length;
|
||||
if(channels === 0 && value.global.length === 0) {
|
||||
embed.description += `\n\n${message.format('C_PERMISSIONS_NOPERMISSIONS')}`;
|
||||
break;
|
||||
}
|
||||
display(items.filter((i) => i.resolveable instanceof Role));
|
||||
display(items.filter((i) => !(i.resolveable instanceof Role)));
|
||||
|
||||
const empty = items.length % 3;
|
||||
for(let i = 0; i<empty; i++) {
|
||||
embed.fields.push({
|
||||
name,
|
||||
value: stripIndents`${this._displayNames(value.global).map((n) => `\`${n}\``).join('\n')}
|
||||
${channels > 0 ? `\`..${channels} channel${channels === 1 ? '' : 's'}\`` : ''}`
|
||||
name: '\u200b',
|
||||
value: '\u200b',
|
||||
inline: true
|
||||
});
|
||||
}
|
||||
|
||||
@ -162,16 +198,18 @@ class PermissionsCommand extends Command {
|
||||
const components = this.client.registry.components.filter((c) => c.type === 'command' || c.type === 'module' && c.components.some((c) => c.type === 'command'))
|
||||
.sort((a, b) => a - b);
|
||||
|
||||
return await message.respond(message.format('C_PERMISSIONS_LIST', { permissions: components.map((c) => `\`${c.resolveable}\``).join(', ') }), { emoji: 'success' });
|
||||
return message.respond(message.format('C_PERMISSIONS_LIST', {
|
||||
permissions: components.map((c) => `\`${c.resolveable}\``).join(', ')
|
||||
}), { emoji: 'success' });
|
||||
|
||||
}
|
||||
|
||||
async _parseResolveable(message, resolveable) {
|
||||
let parsed = await this.client.resolver.resolveRole(resolveable, false, message.guild);
|
||||
let parsed = await this.client.resolver.resolveRole(resolveable, true, message.guild);
|
||||
if(!parsed) {
|
||||
parsed = await this.client.resolver.resolveMember(resolveable, false, message.guild);
|
||||
parsed = await this.client.resolver.resolveMember(resolveable, true, message.guild);
|
||||
if(!parsed) {
|
||||
parsed = await this.client.resolver.resolveUser(resolveable, false);
|
||||
parsed = await this.client.resolver.resolveUser(resolveable, true);
|
||||
if(!parsed) return null;
|
||||
}
|
||||
}
|
||||
@ -183,7 +221,10 @@ class PermissionsCommand extends Command {
|
||||
const string = JSON.stringify(permissions);
|
||||
const attachment = new MessageAttachment(Buffer.from(string), "permissions.json");
|
||||
|
||||
return await message.respond(message.format('C_PERMISSIONS_JSON'), { emoji: 'success', attachments: [ attachment ] });
|
||||
return message.respond(message.format('C_PERMISSIONS_JSON'), {
|
||||
emoji: 'success',
|
||||
files: [ attachment ]
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@ -199,7 +240,7 @@ class PermissionsCommand extends Command {
|
||||
temp.push(component.resolveable);
|
||||
}
|
||||
}
|
||||
temp.length === module.components.filter((c) => c.type === 'command').size
|
||||
temp.length === module.components.filter((c) => c.type === 'command').size //eslint-disable-line no-unused-expressions
|
||||
? names.push(module.resolveable)
|
||||
: names = names.concat(temp);
|
||||
temp = [];
|
||||
@ -209,6 +250,25 @@ class PermissionsCommand extends Command {
|
||||
|
||||
}
|
||||
|
||||
async _deletePermission(guild, key) {
|
||||
|
||||
const permissions = guild._permissions;
|
||||
delete permissions[key];
|
||||
delete permissions._id;
|
||||
|
||||
try {
|
||||
await this.client.storageManager.mongodb.permissions.updateOne(
|
||||
{ guildId: guild.id },
|
||||
permissions
|
||||
);
|
||||
} catch(error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
const { User, GuildMember } = require('discord.js');
|
||||
|
||||
const { Command } = require('../../../../interfaces/');
|
||||
const { Util } = require('../../../../../util/');
|
||||
|
||||
class RevokeCommand extends Command {
|
||||
|
||||
@ -122,7 +123,7 @@ class RevokeCommand extends Command {
|
||||
};
|
||||
|
||||
return message.respond(message.format('C_REVOKE_SUCCESS', {
|
||||
targets: parsed.map((p) => `**${name(p)}**`).join(' '),
|
||||
targets: parsed.map((p) => `**${Util.escapeMarkdown(name(p))}**`).join(' '),
|
||||
permissions: parsedPermissions.map((p) => `\`${p}\``).join(', '),
|
||||
channel: args.channel ? ` ${message.format('C_REVOKE_SUCCESSCHANNELS', { channels: args.channel.value.map((c) => `**#${c.name}**`).join(', '), plural: args.channel.value.length === 1 ? '' : 's' })}` : ''
|
||||
}), { emoji: 'success' });
|
||||
|
@ -1,5 +1,7 @@
|
||||
const { inspect } = require('util');
|
||||
const { username } = require('os').userInfo();
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const moment = require('moment');
|
||||
|
||||
let _storage = null; //eslint-disable-line
|
||||
|
||||
@ -42,7 +44,7 @@ class Evaluate extends Command {
|
||||
async execute(message, { params, args }) {
|
||||
|
||||
params = params.join(' ');
|
||||
const { guild, author, member } = message; //eslint-disable-line no-unused-vars
|
||||
const { guild, author, member, client } = message; //eslint-disable-line no-unused-vars
|
||||
|
||||
try {
|
||||
let evaled = eval(params); //eslint-disable-line no-eval
|
||||
|
109
structure/client/components/commands/developer/Stats.js
Normal file
109
structure/client/components/commands/developer/Stats.js
Normal file
@ -0,0 +1,109 @@
|
||||
const { Command } = require('../../../../interfaces/');
|
||||
|
||||
class StatsCommand extends Command {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
super(client, {
|
||||
name: 'about',
|
||||
module: 'developer',
|
||||
aliases: [
|
||||
'stats',
|
||||
'info'
|
||||
],
|
||||
usage: '',
|
||||
restricted: true,
|
||||
arguments: [
|
||||
{
|
||||
name: 'log',
|
||||
type: 'BOOLEAN',
|
||||
types: ['FLAG'],
|
||||
description: 'Logs the output in the console.'
|
||||
}
|
||||
],
|
||||
showUsage: false
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async execute(message, { params, args }) {
|
||||
|
||||
// TODO:
|
||||
// Add some stuff that only shows when a dev runs the command, for instance amount of cached messages etc
|
||||
|
||||
const { guild } = message;
|
||||
const { shard } = this.client;
|
||||
|
||||
const evalFunc = (thisArg) => {
|
||||
return {
|
||||
users: thisArg.users.cache.size,
|
||||
guilds: thisArg.guilds.cache.size,
|
||||
channels: thisArg.channels.cache.size,
|
||||
memory: Math.floor(process.memoryUsage().heapUsed / 1024 / 1024),
|
||||
uptime: thisArg.uptime
|
||||
};
|
||||
};
|
||||
|
||||
const mEvalFunc = (thisArg) => {
|
||||
return {
|
||||
memory: Math.floor(process.memoryUsage().heapUsed / 1024 / 1024),
|
||||
uptime: Date.now() - thisArg.readyAt
|
||||
};
|
||||
};
|
||||
|
||||
const shardResults = await shard.broadcastEval(evalFunc).catch((error) => this.client.logger.error(error));
|
||||
const managerResult = await this.client.managerEval(`(${mEvalFunc})(this)`).catch((error) => this.client.logger.error(error));
|
||||
|
||||
const descValues = {
|
||||
devs: await this.client.resolveUsers(this.client._options.bot.owners).then((owners) => {
|
||||
return owners.map((o) => o.tag).join(', ');
|
||||
}),
|
||||
uptime: this.client.resolver.timeAgo(Math.floor(managerResult.uptime/1000)),
|
||||
memory: managerResult.memory,
|
||||
shards: shard.count
|
||||
};
|
||||
|
||||
const shardValues = {
|
||||
cachedUsers: this.client.users.cache.size,
|
||||
guilds: this.client.guilds.cache.size,
|
||||
channels: this.client.channels.cache.size,
|
||||
uptime: this.client.resolver.timeAgo(Math.floor(this.client.uptime/1000)),
|
||||
memory: Math.floor(process.memoryUsage().heapUsed / 1024 / 1024)
|
||||
};
|
||||
|
||||
const totalValues = shardResults.reduce((acc, curr) => {
|
||||
Object.entries(curr).forEach(([key, val]) => {
|
||||
if (!acc[key]) acc[key] = 0;
|
||||
acc[key] += val;
|
||||
});
|
||||
return acc;
|
||||
}, {});
|
||||
totalValues.uptime = this.client.resolver.timeAgo(Math.floor(totalValues.uptime / 1000));
|
||||
|
||||
const embed = {
|
||||
title: message.format('C_STATS_TITLE', {
|
||||
client: this.client.user.tag,
|
||||
version: require('../../../../../package.json').version
|
||||
}),
|
||||
description: message.format('C_STATS_DESC', descValues),
|
||||
fields: [
|
||||
{
|
||||
name: message.format('C_STATS_CURRENT_SHARD', { shard: guild.shardID }),
|
||||
value: message.format('C_STATS_CURRENT_SHARDS_VALUE', shardValues),
|
||||
inline: true
|
||||
},
|
||||
{
|
||||
name: message.format('C_STATS_TOTAL', { shards: shard.count }),
|
||||
value: message.format('C_STATS_TOTAL_VALUE', totalValues),
|
||||
inline: true
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
message.embed(embed);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = StatsCommand;
|
@ -1,6 +1,21 @@
|
||||
const { Command } = require('../../../../interfaces');
|
||||
const similarity = require('similarity');
|
||||
const { Util } = require('../../../../../util');
|
||||
const { Util, Emojis } = require('../../../../../util');
|
||||
|
||||
const Constants = {
|
||||
Badges: {
|
||||
DISCORD_EMPLOYEE: Emojis['discord-staff'],
|
||||
DISCORD_PARTNER: Emojis['discord-partner'],
|
||||
HYPESQUAD_EVENTS: Emojis['hypesquad-events'],
|
||||
BUGHUNTER_LEVEL_1: Emojis['bughunter'], //eslint-disable-line dot-notation
|
||||
BUGHUNTER_LEVEL_2: Emojis['bughunter-gold'],
|
||||
HOUSE_BRAVERY: Emojis['hypesquad-bravery'],
|
||||
HOUSE_BRILLIANCE: Emojis['hypesquad-brilliance'],
|
||||
HOUSE_BALANCE: Emojis['hypesquad-balance'],
|
||||
EARLY_SUPPORTER: Emojis['early-supporter'],
|
||||
VERIFIED_DEVELOPER: Emojis['bot-developer']
|
||||
}
|
||||
};
|
||||
|
||||
class UserCommand extends Command {
|
||||
|
||||
@ -86,66 +101,83 @@ class UserCommand extends Command {
|
||||
if (!user) return message.formattedRespond('C_USER_404');
|
||||
} else user = message.author;
|
||||
|
||||
const member = await message.guild.members.fetch(user.id).catch();
|
||||
const member = await message.guild.members.fetch(user.id).catch((error) => {}); //eslint-disable-line
|
||||
const { activities } = user.presence;
|
||||
|
||||
const flags = user.flags || await user.fetchFlags();
|
||||
const badges = flags.toArray().filter((f) => Constants.Badges[f])
|
||||
.map((f) => Constants.Badges[f]);
|
||||
|
||||
response = {
|
||||
title: `**${user.tag}**`,
|
||||
author: {
|
||||
name: Util.escapeMarkdown(user.tag),
|
||||
icon_url: user.displayAvatarURL() //eslint-disable-line camelcase
|
||||
},
|
||||
description: response,
|
||||
thumbnail: {
|
||||
url: user.avatarURL() || user.defaultAvatarURL
|
||||
},
|
||||
fields: [
|
||||
{
|
||||
name: message.format('C_USER_DATA_NAME'),
|
||||
value: message.format('C_USER_DATA', {
|
||||
id: user.id,
|
||||
created: user.createdAt.toDateString(),
|
||||
status: user.presence.status,
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
activity: activities.length > 0 ? activities[0].type === 'CUSTOM_STATUS' ? `${activities[0].name}: ${activities[0].state || 'emoji'}` : activities[0].name : 'Nothing',
|
||||
globalActivity: user.lastMessage ? user.lastMessage.createdAt.toDateString() : 'N/A'
|
||||
}),
|
||||
inline: true
|
||||
}
|
||||
],
|
||||
fields: [],
|
||||
footer: {
|
||||
text: `ID: ${user.id}`
|
||||
text: `• User ID: ${user.id}`
|
||||
}
|
||||
};
|
||||
|
||||
const userField = {
|
||||
name: message.format('C_USER_DATA_NAME'),
|
||||
value: message.format('C_USER_DATA', {
|
||||
id: user.id,
|
||||
bot: user.bot ? ` ${Emojis.bot}` : '',
|
||||
created: user.createdAt.toDateString(),
|
||||
status: user.presence.status,
|
||||
// eslint-disable-next-line no-nested-ternary
|
||||
activity: activities.length > 0 ? activities[0].type === 'CUSTOM_STATUS' ? `${activities[0].name}: ${activities[0].state || 'emoji'}` : activities[0].name : 'Nothing',
|
||||
globalActivity: user.lastMessage ? user.lastMessage.createdAt.toDateString() : 'N/A'
|
||||
})
|
||||
};
|
||||
|
||||
if(badges.length > 0) {
|
||||
userField.value += `\n${message.format('C_USER_BADGES', {
|
||||
badges: badges.join(' ')
|
||||
})}`;
|
||||
}
|
||||
|
||||
response.fields.push(userField);
|
||||
|
||||
if (member) {
|
||||
|
||||
response.fields.push({
|
||||
const memberField = {
|
||||
name: message.format('C_USER_MEMBER_NAME'),
|
||||
value: message.format('C_USER_MEMBER', {
|
||||
nickname: member.nickname ? member.nickname : 'N/A',
|
||||
joined: member.joinedAt ? member.joinedAt.toDateString() : 'N/A',
|
||||
serverActivity: member.lastMessage ? member.lastMessage.createdAt.toDateString() : 'N/A'
|
||||
}),
|
||||
inline: true
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
const roles = member.roles.cache.filter((r) => r.name !== '@everyone').sort((a, b) => b.rawPosition - a.rawPosition);
|
||||
let counter = 0;
|
||||
if (roles.size) response.fields.push({
|
||||
name: message.format('C_USER_ROLES_TITLE'),
|
||||
value: roles.map((r) => {
|
||||
const str = `<@&${r.id}>`;
|
||||
counter += str.length;
|
||||
return counter <= 950 ? str : '';
|
||||
}).join(' ')
|
||||
});
|
||||
const maxRoles = 30;
|
||||
|
||||
if(roles.size > 0) {
|
||||
memberField.value += `\n${message.format('C_USER_MEMBER_ROLES', {
|
||||
roles: roles.size > maxRoles ? `${roles.slice(0, maxRoles).map((r) => `<@&${r.id}>`).join(' ')} \`...${maxRoles-roles.size} more roles\`` : roles.map((r) => `<@&${r.id}>`).join(' ')
|
||||
})}`;
|
||||
}
|
||||
|
||||
response.fields.push(memberField);
|
||||
|
||||
// let counter = 0;
|
||||
// if (roles.size) response.fields.push({
|
||||
// name: message.format('C_USER_ROLES_TITLE'),
|
||||
// value: roles.map((r) => {
|
||||
// const str = `<@&${r.id}>`;
|
||||
// counter += str.length;
|
||||
// return counter <= 950 ? str : '';
|
||||
// }).join(' ')
|
||||
// });
|
||||
|
||||
const highestColouredRole = member.roles.cache.filter((role) => role.color !== 0).sort((a, b) => b.rawPosition - a.rawPosition).first();
|
||||
if (highestColouredRole) response.color = highestColouredRole.color;
|
||||
}
|
||||
|
||||
const flags = user.flags || await user.fetchFlags();
|
||||
if (flags.bitfield) response.fields.push({
|
||||
name: message.format('C_USER_FLAGS'),
|
||||
value: flags.toArray().join(', ')
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
270
structure/client/components/commands/moderation/History.js
Normal file
270
structure/client/components/commands/moderation/History.js
Normal file
@ -0,0 +1,270 @@
|
||||
const { Command } = require('../../../../interfaces/');
|
||||
const { MessageAttachment } = require('discord.js');
|
||||
|
||||
const { stripIndents } = require('common-tags');
|
||||
const moment = require('moment');
|
||||
|
||||
const { UploadLimit } = require('../../../../../util/Constants.js');
|
||||
const { Util } = require('../../../../../util/');
|
||||
|
||||
const Constants = {
|
||||
PageSize: 5,
|
||||
MaxCharacters: 256,
|
||||
MaxCharactersVerbose: 128, //Displays more information in the field, decreases characters.
|
||||
Types: {
|
||||
'NOTE': ['note', 'notes'],
|
||||
'WARN': ['warn', 'warning', 'warns', 'warnings'],
|
||||
'MUTE': ['mute', 'mutes', 'tempmute', 'tempmutes'],
|
||||
'UNMUTE': ['unmute', 'unmutes', 'untempmute', 'untempmutes'],
|
||||
'KICK': ['kick', 'kicks'],
|
||||
'SOFTBAN': ['softban', 'softbans'],
|
||||
'BAN': ['ban', 'bans', 'hardban', 'hardbans'],
|
||||
'UNBAN': ['unban', 'unbans', 'unhardban', 'unhardbans'],
|
||||
'VCMUTE': ['vcmute', 'vcmutes', 'vctempmute', 'vctempmutes'],
|
||||
'VCUNMUTE': ['vcunmute', 'vcunmutes', 'vctempunmute', 'vctempunmutes'],
|
||||
'VCKICK': ['vckick', 'vckicks'],
|
||||
'VCBAN': ['vcban', 'vcbans'],
|
||||
'VCUNBAN': ['vcunban', 'vcunbans'],
|
||||
'PRUNE': ['prune', 'prunes', 'purge', 'purges'],
|
||||
'SLOWMODE': ['slowmode', 'slowmodes'],
|
||||
'ADDROLE': ['addrole', 'addroles', 'roleadd', 'roleadds'],
|
||||
'REMOVEROLE': ['removerole', 'removeroles', 'roleremove', 'roleremoves'],
|
||||
'NICKNAME': ['nickname', 'nicknames', 'dehoist', 'dehoists'],
|
||||
'LOCKDOWN': ['lockdown', 'lockdowns'],
|
||||
'UNLOCKDOWN': ['unlockdown', 'unlockdowns']
|
||||
}
|
||||
};
|
||||
|
||||
class HistoryCommand extends Command {
|
||||
|
||||
constructor(client) {
|
||||
|
||||
super(client, {
|
||||
name: 'history',
|
||||
module: 'moderation',
|
||||
usage: "[user..|channel..]",
|
||||
aliases: [
|
||||
'moderation'
|
||||
],
|
||||
memberPermissions: ['MANAGE_MESSAGES'],
|
||||
guildOnly: true,
|
||||
arguments: [
|
||||
{
|
||||
name: 'before', //Search for moderation actions before x
|
||||
usage: '<date>',
|
||||
type: 'DATE',
|
||||
types: ['FLAG'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'after', //Search for moderation actions after x
|
||||
usage: '<date>',
|
||||
type: 'DATE',
|
||||
types: ['FLAG'],
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'oldest',
|
||||
aliases: ['old'],
|
||||
type: 'BOOLEAN',
|
||||
types: ['FLAG'],
|
||||
default: true
|
||||
},
|
||||
{
|
||||
name: 'type',
|
||||
aliases: ['types'],
|
||||
type: 'STRING',
|
||||
types: ['FLAG'],
|
||||
infinite: true,
|
||||
required: true
|
||||
},
|
||||
{
|
||||
name: 'pagesize',
|
||||
type: 'INTEGER',
|
||||
types: ['FLAG'],
|
||||
required: true,
|
||||
default: 10,
|
||||
min: 1,
|
||||
max: 10
|
||||
},
|
||||
{
|
||||
name: 'verbose', //Shows IDs for users/channels.
|
||||
type: 'BOOLEAN',
|
||||
types: ['FLAG'],
|
||||
default: true
|
||||
},
|
||||
{
|
||||
name: 'export', //Export moderation actions in a JSON.
|
||||
type: 'BOOLEAN',
|
||||
types: ['FLAG'],
|
||||
default: true
|
||||
},
|
||||
{
|
||||
name: 'private', //Send moderation history in DMs.
|
||||
type: 'BOOLEAN',
|
||||
types: ['FLAG'],
|
||||
default: true
|
||||
} //filter, exclude, verbose (NO PAGESIZE)
|
||||
],
|
||||
throttling: {
|
||||
usages: 2,
|
||||
duration: 10
|
||||
}
|
||||
});
|
||||
|
||||
this.client = client;
|
||||
|
||||
}
|
||||
|
||||
async execute(message, { params, args }) {
|
||||
|
||||
if(args.export) return this._exportLogs(message, Boolean(args.private));
|
||||
|
||||
const query = {
|
||||
guild: message.guild.id
|
||||
};
|
||||
|
||||
const { parsed, parameters } = await this.client.resolver.infinite(params, [
|
||||
this.client.resolver.resolveMember.bind(this.client.resolver),
|
||||
this.client.resolver.resolveUser.bind(this.client.resolver),
|
||||
this.client.resolver.resolveChannel.bind(this.client.resolver)
|
||||
], true, message.guild, (c) => c.type === 'text');
|
||||
|
||||
if(parsed.length > 0) query.target = { $in: parsed.map((p) => p.id) }; //Add resolved ids to the query.
|
||||
if(args.before || args.after) {
|
||||
query.timestamp = {};
|
||||
if(args.before) query.timestamp.$lt = args.before.value.valueOf(); //Add before timestamps to the query.
|
||||
if(args.after) query.timestamp.$gt = args.after.value.valueOf(); //Add after timestamps to the query.
|
||||
}
|
||||
|
||||
if(args.type) {
|
||||
const filter = [];
|
||||
for(const value of args.type.value) {
|
||||
for(const [ type, matches ] of Object.entries(Constants.Types)) {
|
||||
if(matches.includes(value.toLowerCase())) {
|
||||
filter.push(type);
|
||||
}
|
||||
}
|
||||
}
|
||||
query.type = { $in: filter };
|
||||
}
|
||||
|
||||
const pageSize = args.pagesize ? args.pagesize.value : Constants.PageSize;
|
||||
|
||||
let page = 1;
|
||||
if(parameters.length > 0) {
|
||||
const number = parseInt(parameters[0]);
|
||||
if(!Number.isNaN(number) && number > 1) {
|
||||
page = number;
|
||||
}
|
||||
}
|
||||
|
||||
const collectionSize = await this.client.storageManager.mongodb.infractions.count(query);
|
||||
if(collectionSize === 0) {
|
||||
return message.respond(message.format('C_HISTORY_NORESULTS'), {
|
||||
emoji: 'failure'
|
||||
});
|
||||
}
|
||||
|
||||
const maxPage = Math.ceil(collectionSize/pageSize);
|
||||
if(page > maxPage) page = maxPage;
|
||||
|
||||
const infractions = await this.client.storageManager.mongodb.db.collection('infractions').find(query)
|
||||
.sort({ timestamp: args.oldest ? 1 : -1 })
|
||||
.skip((page-1)*pageSize).limit(pageSize)
|
||||
.toArray();
|
||||
|
||||
const embed = {
|
||||
author: {
|
||||
name: 'Infraction History',
|
||||
icon_url: message.guild.iconURL() //eslint-disable-line camelcase
|
||||
},
|
||||
fields: [],
|
||||
footer: {
|
||||
text: `• Page ${page}/${maxPage} | ${collectionSize} Results`
|
||||
}
|
||||
};
|
||||
|
||||
let long = false;
|
||||
const handleReason = (text) => {
|
||||
const MaxCharacters = Constants[args.verbose ? 'MaxCharactersVerbose' : 'MaxCharacters'];
|
||||
text = Util.escapeMarkdown(text);
|
||||
if(text.length > MaxCharacters) {
|
||||
text = `${text.substring(0, MaxCharacters-3)}...`;
|
||||
long = true;
|
||||
}
|
||||
text = text.replace(/\\n/giu, ' ');
|
||||
return text;
|
||||
};
|
||||
|
||||
for(let i = 0; i<infractions.length; i++) {
|
||||
const infraction = infractions[i];
|
||||
let target = null;
|
||||
if(infraction.targetType === 'user') {
|
||||
target = await this.client.resolver.resolveUser(infraction.target);
|
||||
} else {
|
||||
target = await this.client.resolver.resolveChannel(infraction.target, true, message.guild);
|
||||
}
|
||||
|
||||
const executor = await this.client.resolver.resolveUser(infraction.executor);
|
||||
|
||||
let string = stripIndents`**Target:** ${Util.escapeMarkdown(target.display)}${args.verbose ? ` (${target.id})` : ''}
|
||||
**Moderator:** ${executor ? `${Util.escapeMarkdown(executor.tag)}${args.verbose ? ` (${infraction.executor})` : ''}` : infraction.executor}`;
|
||||
|
||||
if(infraction.duration) string += `\n**Duration:** ${Util.duration(infraction.duration)}`;
|
||||
if(infraction.points) string += `\n**Points:** ${infraction.points}`;
|
||||
|
||||
string += `\n**Reason:** \`${handleReason(infraction.reason)}\``;
|
||||
if(i !== infractions.length-1) string += `\n\u200b`; //Space out cases (as long as its not at the end)
|
||||
|
||||
embed.fields.push({
|
||||
name: `__**${infraction.type} \`[case-${infraction.case}]\`** *(${moment(infraction.timestamp).fromNow()})*__`,
|
||||
value: string
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
if(long) embed.footer.text += ` • To see the full reason, use the ${message.guild.prefix}case command.`;
|
||||
|
||||
const type = message.format('C_HISTORY_SUCCESSTYPE', { old: Boolean(args.oldest) }, true);
|
||||
message.respond(message.format('C_HISTORY_SUCCESS', {
|
||||
targets: parsed.length > 0 ? message.format('C_HISTORY_SUCCESSTARGETS', {
|
||||
plural: parsed.length === 1 ? '' : 's',
|
||||
targets: parsed.map((p) => `**${Util.escapeMarkdown(p.display)}**`).join(' ')
|
||||
}) : '',
|
||||
type
|
||||
}), {
|
||||
emoji: 'success',
|
||||
embed,
|
||||
dm: Boolean(args.private)
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
async _exportLogs(message, priv = false) {
|
||||
|
||||
const logs = await this.client.storageManager.mongodb.infractions.find({ guild: message.guild.id });
|
||||
const string = JSON.stringify(logs);
|
||||
const attachment = new MessageAttachment(Buffer.from(string), `${message.guild.id}-moderation.json`);
|
||||
|
||||
const bytes = Buffer.byteLength(attachment.attachment);
|
||||
|
||||
const premium = priv ? '0' : message.guild.premiumTier;
|
||||
if(bytes > UploadLimit[premium]*1024*1024) {
|
||||
message.respond(message.format('C_HISTORY_FAILEXPORT', {
|
||||
amount: bytes,
|
||||
max: UploadLimit[premium]*1024*1024
|
||||
}));
|
||||
}
|
||||
|
||||
message.respond(message.format('C_HISTORY_SUCCESSEXPORT'), {
|
||||
files: [ attachment ],
|
||||
emoji: 'success',
|
||||
dm: priv
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
module.exports = HistoryCommand;
|
@ -200,8 +200,7 @@ class PruneCommand extends Command {
|
||||
.handleInfraction(Prune, message, {
|
||||
targets: parsed,
|
||||
data: {
|
||||
amount: int,
|
||||
message: message.id
|
||||
amount: int
|
||||
},
|
||||
reason
|
||||
});
|
||||
|
@ -46,15 +46,19 @@ class AvatarCommand extends Command {
|
||||
message.respond(message.format('C_AVATAR_FORMATERROR'), { emoji: 'failure' });
|
||||
return undefined;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return await message.embed({
|
||||
return message.embed({
|
||||
author: {
|
||||
name: user.tag
|
||||
name: user.tag,
|
||||
icon_url: user.displayAvatarURL()
|
||||
},
|
||||
description: `[**Link to Image**](${avatar})`,
|
||||
image: {
|
||||
url: avatar
|
||||
},
|
||||
footer: {
|
||||
text: `• Format: .${args.format?.value || 'webp'} | Size: ${args.size?.value || '128'}`
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -488,6 +488,11 @@ class CommandHandler extends Observer {
|
||||
const time = await this.client.resolver.resolveTime(str);
|
||||
if(!time) return { error: true };
|
||||
return { error: false, value: time };
|
||||
},
|
||||
DATE: async(str) => {
|
||||
const date = await this.client.resolver.resolveDate(str);
|
||||
if(!date) return { error: true };
|
||||
return { error: false, value: date };
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -34,7 +34,6 @@ class GuildLogger extends Observer {
|
||||
});
|
||||
|
||||
this.hooks = [
|
||||
// ['message', this.storeAttachment.bind(this)], //Attachment logging
|
||||
['messageDelete', this.messageDelete.bind(this)],
|
||||
['messageDeleteBulk', this.messageDeleteBulk.bind(this)],
|
||||
['messageUpdate', this.messageEdit.bind(this)],
|
||||
|
@ -38,6 +38,10 @@ const GuildMember = Structures.extend('GuildMember', (GuildMember) => {
|
||||
return Date.now()-this._cached;
|
||||
}
|
||||
|
||||
get display() {
|
||||
return this.user.tag;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
@ -94,7 +94,7 @@ const Message = Structures.extend('Message', (Message) => {
|
||||
|
||||
}
|
||||
|
||||
async respond(str, opts = { files: [], embed: null }) {
|
||||
async respond(str, opts = { files: [], embed: null, dm: false }) {
|
||||
|
||||
if(typeof str === 'string') {
|
||||
if(opts.emoji) {
|
||||
@ -105,7 +105,7 @@ const Message = Structures.extend('Message', (Message) => {
|
||||
if(opts.reply) str = `<@!${this.author.id}> ${str}`;
|
||||
}
|
||||
|
||||
this._pending = await this.channel.send(str, { files: opts.files, embed: opts.embed });
|
||||
this._pending = await this[opts.dm ? 'author' : 'channel'].send(str, { files: opts.files, embed: opts.embed });
|
||||
return this._pending;
|
||||
|
||||
}
|
||||
|
17
structure/extensions/Role.js
Normal file
17
structure/extensions/Role.js
Normal file
@ -0,0 +1,17 @@
|
||||
const { Structures } = require('discord.js');
|
||||
|
||||
const Role = Structures.extend('Role', (Role) => {
|
||||
|
||||
class ExtendedRole extends Role {
|
||||
|
||||
get display() {
|
||||
return this.name;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ExtendedRole;
|
||||
|
||||
});
|
||||
|
||||
module.exports = Role;
|
17
structure/extensions/TextChannel.js
Normal file
17
structure/extensions/TextChannel.js
Normal file
@ -0,0 +1,17 @@
|
||||
const { Structures } = require('discord.js');
|
||||
|
||||
const TextChannel = Structures.extend('TextChannel', (TextChannel) => {
|
||||
|
||||
class ExtendedTextChannel extends TextChannel {
|
||||
|
||||
get display() {
|
||||
return `#${this.name}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ExtendedTextChannel;
|
||||
|
||||
});
|
||||
|
||||
module.exports = TextChannel;
|
@ -68,6 +68,10 @@ const User = Structures.extend('User', (User) => {
|
||||
return this.client._options.bot.prefix;
|
||||
}
|
||||
|
||||
get display() {
|
||||
return this.tag;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return ExtendedUser;
|
||||
|
@ -2,5 +2,7 @@ module.exports = {
|
||||
Message: require('./Message.js'),
|
||||
Guild: require('./Guild.js'),
|
||||
GuildMember: require('./GuildMember.js'),
|
||||
User: require('./User.js')
|
||||
User: require('./User.js'),
|
||||
TextChannel: require('./TextChannel.js'),
|
||||
Role: require('./Role.js')
|
||||
};
|
@ -14,7 +14,8 @@ const Constants = {
|
||||
'CHANNEL',
|
||||
'TEXTCHANNEL',
|
||||
'VOICECHANNEL',
|
||||
'TIME'
|
||||
'TIME',
|
||||
'DATE'
|
||||
],
|
||||
ArgumentTypes: [
|
||||
'FLAG',
|
||||
|
@ -75,7 +75,7 @@ class Command extends Component {
|
||||
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`)}`)
|
||||
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`)}`).join('\n')
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
//Command Arguments
|
||||
//Grant Command
|
||||
|
||||
[A_CHANNEL_GRANT_DESCRIPTION]
|
||||
Specify channels to grant specific permissions to.
|
||||
|
||||
@ -8,8 +8,5 @@ Specify channels to grant specific permissions to.
|
||||
Specify channels to revoke permissions from.
|
||||
|
||||
//Permissions Command
|
||||
[A_USER_PERMISSIONS_DESCRIPTION]
|
||||
Enable viewing all user's permissions.
|
||||
|
||||
[A_RAW_PERMISSIONS_DESCRIPTION]
|
||||
Upload a raw JSON file of all of the permissions in the guild.
|
||||
[A_EXPORT_PERMISSIONS_DESCRIPTION]
|
||||
Export a JSON file of all of the permissions in the server.
|
@ -100,7 +100,10 @@ Filter messages sent by specified users.
|
||||
[A_BOTS_PRUNE_DESCRIPTION]
|
||||
Filter messages sent by bots.
|
||||
|
||||
[A_TEXT_PRUNE_DESCRIPTION]
|
||||
[A_HUMANS_PRUNE_DESCRIPTION]
|
||||
Filter messages sent by non-bots.
|
||||
|
||||
[A_CONTAINS_PRUNE_DESCRIPTION]
|
||||
Filter messages containing specified text.
|
||||
|
||||
[A_STARTSWITH_PRUNE_DESCRIPTION]
|
||||
@ -109,6 +112,15 @@ Filter messages starting with specified text.
|
||||
[A_ENDSWITH_PRUNE_DESCRIPTION]
|
||||
Filter messages ending with specified text.
|
||||
|
||||
[A_TEXT_PRUNE_DESCRIPTION]
|
||||
Filter messages containing text.
|
||||
|
||||
[A_INVITES_PRUNE_DESCRIPTION]
|
||||
Filter messages containing discord invites.
|
||||
|
||||
[A_LINKS_PRUNE_DESCRIPTION]
|
||||
Filter messages containing links.
|
||||
|
||||
[A_EMOJIS_PRUNE_DESCRIPTION]
|
||||
Filter messages containing emojis.
|
||||
|
||||
@ -122,20 +134,33 @@ Filter messages containing images.
|
||||
Filter messages containing any attachment.
|
||||
|
||||
[A_AFTER_PRUNE_DESCRIPTION]
|
||||
Filter messages after a specified message ID.
|
||||
Filter messages after a specified message.
|
||||
|
||||
[A_BEFORE_PRUNE_DESCRIPTION]
|
||||
Filter messages before a specified message ID.
|
||||
Filter messages before a specified message.
|
||||
|
||||
[A_AND_PRUNE_DESCRIPTION]
|
||||
Use a logical AND for all checks.
|
||||
Use a logical AND for checks.
|
||||
|
||||
[A_NOT_PRUNE_DESCRIPTION]
|
||||
Use a logical NOT for all checks.
|
||||
Use a logical NOT for checks.
|
||||
|
||||
[A_SILENT_PRUNE_DESCRIPTION]
|
||||
Deletes the command message and the execution message.
|
||||
|
||||
//History Arguments
|
||||
[A_BEFORE_HISTORY_DESCRIPTION]
|
||||
|
||||
[A_AFTER_HISTORY_DESCRIPTION]
|
||||
|
||||
[A_TYPE_HISTORY_DESCRIPTION]
|
||||
|
||||
[A_OLDEST_HISTORY_DESCRIPTION]
|
||||
Sort history by oldest.
|
||||
|
||||
[A_VERBOSE_HISTORY_DESCRIPTION]
|
||||
Display user IDs.
|
||||
|
||||
//Settings Arguments
|
||||
//Mute Setting
|
||||
[A_CREATE_MUTE_SETTINGS]
|
||||
|
@ -5,4 +5,4 @@ View or edit user-only settings.
|
||||
View all guild, user, or restricted settings.
|
||||
|
||||
[A_RAW_SETTINGS]
|
||||
Upload a raw JSON file of all of the settings for your user or guild.
|
||||
Export a JSON file of all of the settings in the server.
|
@ -85,6 +85,10 @@ Channel-specific permissions are listed below.
|
||||
[C_PERMISSIONS_PERMISSIONSNOTFOUND]
|
||||
Found {type} **{resolveable}** but {they}had no permissions.
|
||||
|
||||
[C_PERMISSIONS_NOPERMISSIONS]
|
||||
Your server has no granted permissions to any roles or users.
|
||||
If you would like to grant permissions, use the command `{prefix}grant` for more information.
|
||||
|
||||
//Disable Command
|
||||
[C_DISABLE_DESCRIPTION]
|
||||
Disable commands in your server to prevent usage.
|
||||
|
@ -2,4 +2,33 @@
|
||||
Evaluates javascript code.
|
||||
|
||||
[C_RELOAD_DESCRIPTION]
|
||||
Reloads components and language files.
|
||||
Reloads components and language files.
|
||||
|
||||
[C_STATS_TITLE]
|
||||
Statistics for {client} ({version}).
|
||||
|
||||
[C_STATS_DESC]
|
||||
**Developers:** {devs}
|
||||
**Manager Uptime:** {uptime}
|
||||
**Managed Shards:** {shards}
|
||||
**Manager Memory Consumption:** {memory}MB
|
||||
|
||||
[C_STATS_CURRENT_SHARD]
|
||||
__**Current shard**__ [{shard}]
|
||||
|
||||
[C_STATS_CURRENT_SHARDS_VALUE]
|
||||
**Cached users:** {cachedUsers}
|
||||
**Guilds:** {guilds}
|
||||
**Channels:** {channels}
|
||||
**Uptime:** {uptime}
|
||||
**Memory Consumption:** {memory}MB
|
||||
|
||||
[C_STATS_TOTAL]
|
||||
__**All shards**__
|
||||
|
||||
[C_STATS_TOTAL_VALUE]
|
||||
**Cached users:** {users}
|
||||
**Guilds:** {guilds}
|
||||
**Channels:** {channels}
|
||||
**Average Uptime:** {uptime}
|
||||
**Total Memory Consumption:** {memory}MB
|
@ -133,8 +133,7 @@ __Members__
|
||||
__Channels__
|
||||
|
||||
[C_GUILD_CHANNELS]
|
||||
**Total:**
|
||||
{totalChannels}/500
|
||||
**Total:** {totalChannels}/500
|
||||
{emoji_category-channel} **Categories:** {cat}
|
||||
{emoji_text-channel} **Text:** {tc}
|
||||
{emoji_voice-channel} **Voice:** {vc}
|
||||
@ -175,12 +174,15 @@ Search for users or view user information.
|
||||
__User Data__
|
||||
|
||||
[C_USER_DATA]
|
||||
**User:** <@{id}>
|
||||
**User:** <@{id}>{bot}
|
||||
**Account created:** {created}
|
||||
**Status:** {status}
|
||||
**Activity:** {activity}
|
||||
**Last global activity:** {globalActivity}
|
||||
|
||||
[C_USER_BADGES]
|
||||
**Badges:** {badges}
|
||||
|
||||
[C_USER_MEMBER_NAME]
|
||||
__Member Data__
|
||||
|
||||
@ -189,6 +191,9 @@ __Member Data__
|
||||
**Server join date:** {joined}
|
||||
**Last server activity:** {serverActivity}
|
||||
|
||||
[C_USER_MEMBER_ROLES]
|
||||
**Roles:** {roles}
|
||||
|
||||
[C_USER_SEARCH_TITLE]
|
||||
Search result for: `{key}`
|
||||
|
||||
@ -207,9 +212,6 @@ To search server members with similar names use `{prefix}user search <arguments.
|
||||
[C_USER_ROLES_TITLE]
|
||||
__Member Roles__
|
||||
|
||||
[C_USER_FLAGS]
|
||||
__User Badges__
|
||||
|
||||
//Commands Command
|
||||
|
||||
[C_COMMANDS_DESCRIPTION]
|
||||
|
@ -330,4 +330,38 @@ You must provide roles to remove from members.
|
||||
I don't have permission to manage roles
|
||||
|
||||
[C_REMOVEROLE_ROLEHIERARCHY]
|
||||
the provided role(s) have a higher position than yours
|
||||
the provided role(s) have a higher position than yours
|
||||
|
||||
//History Command
|
||||
[C_HISTORY_DESCRIPTION]
|
||||
Display moderation history for the server or for certain users.
|
||||
|
||||
[C_HISTORY_ERROR]
|
||||
I had issues finding moderation history in your server.
|
||||
|
||||
[C_HISTORY_NORESULTS]
|
||||
There are no results for that search query.
|
||||
|
||||
[C_HISTORY_SUCCESSTYPE]
|
||||
switch({old}) {
|
||||
case true:
|
||||
'oldest';
|
||||
break;
|
||||
case false:
|
||||
'newest';
|
||||
break;
|
||||
}
|
||||
|
||||
[C_HISTORY_SUCCESS]
|
||||
Fetched the {type} moderation cases{targets}.
|
||||
|
||||
[C_HISTORY_SUCCESSTARGETS]
|
||||
for target{plural}: {targets}
|
||||
|
||||
[C_HISTORY_SUCCESSEXPORT]
|
||||
Attached the JSON-formatted moderation file in the file below.
|
||||
You may want to format this file for your viewing pleasure.
|
||||
|
||||
[C_HISTORY_FAILEXPORT]
|
||||
Unable to upload JSON file, the file is too large to upload here. **\`[amount/max]\`**
|
||||
If you absolutely need this, contact a bot developer in our support server.
|
6
util/Constants.js
Normal file
6
util/Constants.js
Normal file
@ -0,0 +1,6 @@
|
||||
exports.UploadLimit = {
|
||||
'0': 8,
|
||||
'1': 8,
|
||||
'2': 50,
|
||||
'3': 100
|
||||
};
|
13
util/Util.js
13
util/Util.js
@ -11,6 +11,19 @@ class Util {
|
||||
throw new Error("Class may not be instantiated.");
|
||||
}
|
||||
|
||||
static paginate(items, page = 1, pageLength = 10) {
|
||||
const maxPage = Math.ceil(items.length / pageLength);
|
||||
if(page < 1) page = 1;
|
||||
if(page > maxPage) page = maxPage;
|
||||
const startIndex = (page - 1) * pageLength;
|
||||
return {
|
||||
items: items.length > pageLength ? items.slice(startIndex, startIndex + pageLength) : items,
|
||||
page,
|
||||
maxPage,
|
||||
pageLength
|
||||
};
|
||||
}
|
||||
|
||||
static downloadAsBuffer(source) {
|
||||
return new Promise((resolve, reject) => {
|
||||
fetch(source).then((res) => {
|
||||
|
@ -9,7 +9,8 @@
|
||||
"voice-channel": "<:voicechannel:716414422662512762>",
|
||||
"online": "<:online:741718263058268220>",
|
||||
"offline": "<:offline:741718263188422787>",
|
||||
"member": "<:members:741721081261588589>",
|
||||
"member": "<:member:743722505826730034>",
|
||||
"members": "<:members:741721081261588589>",
|
||||
"store-channel": "<:store:741727831955865610>",
|
||||
"invite": "<:invite:741721080846221425>",
|
||||
"owner": "<:owner:741721640789999669>",
|
||||
@ -27,5 +28,17 @@
|
||||
"category-channel": "<:category:741731053818871901>",
|
||||
"gif": "<:gif:741729824267305064>",
|
||||
"book": "📕",
|
||||
"role": "<:role:743563678292639794>"
|
||||
"role": "<:role:743563678292639794>",
|
||||
"discord-staff": "<:discord_staff:743733186043052093>",
|
||||
"discord-partner": "<:discord_partner:743733185984331858>",
|
||||
"hypesquad-events": "<:hypesquad_events:743733934545829960>",
|
||||
"bughunter": "<:bughunter:743733185598324837>",
|
||||
"bughunter-gold": "<:bughunter_gold:743734254415904818>",
|
||||
"hypesquad-bravery": "<:house_bravery:743733185698988093>",
|
||||
"hypesquad-brilliance": "<:house_brilliance:743733185778810917>",
|
||||
"hypesquad-balance": "<:house_balance:743733185913028618> ",
|
||||
"early-supporter": "<:early_supporter:743733185485078550>",
|
||||
"bot-developer": "<:bot_developer:743734629793660969>",
|
||||
"discord-nitro": "<:discord_nitro:743733186252767282>",
|
||||
"bot": "<:bot:743733185531347055>"
|
||||
}
|
@ -960,7 +960,7 @@ director@1.2.7:
|
||||
|
||||
discord.js@discordjs/discord.js:
|
||||
version "12.2.0"
|
||||
resolved "https://codeload.github.com/discordjs/discord.js/tar.gz/153a030c1fc04fd2a144108680dbf7bb1d5b9cc9"
|
||||
resolved "https://codeload.github.com/discordjs/discord.js/tar.gz/fb1dd6b53aee68722b057f9a460eb618c61dd1c6"
|
||||
dependencies:
|
||||
"@discordjs/collection" "^0.1.5"
|
||||
"@discordjs/form-data" "^3.0.1"
|
||||
|
Loading…
Reference in New Issue
Block a user