This commit is contained in:
nolan 2020-07-04 03:25:12 -07:00
commit 8929d20218
30 changed files with 373 additions and 47 deletions

View File

@ -61,8 +61,11 @@ View data about the server.
**Owner:** <@{owner}>
**Member count:** {members}
**Boost tier:** {tier}
**Boosters:** {boosters}
**Date created:** {createdAt}
**Server ID:** {id}
**Shard ID:** {shard}
**Channels:** Text {tc} / Voice {vc}
**Server voice region:** {region}

View File

@ -154,6 +154,9 @@ The command **{command}** can only be run by developers.
The command **{command}** is currently throttled.
*You can use this command again in `{remaining}` seconds.*
[I_CHANNELIGNORE_ERROR]
This channel is ignored by the bot, this should not be sending.
//Moderation Manager
[MODERATIONMANAGER_INFRACTION_MAXTARGETS]
You can only specify up to `{maxTargets}` unique {type}s, try again.

View File

@ -105,6 +105,15 @@ Successfully set member nicknames to log to {emoji_text-channel}**{changed}**.
[S_MEMBERLOG_DESCRIPTION]
Configure member logging for your server.
**Usable tags:**
{mention} - mentions the user
{tag} - username#discriminator
{user} - username
{guildsize} - member count of the server
{guildname} - name of the server
{accage} - age of the account
{id} - ID of the account
[S_MEMBERLOGS_TOGGLE]
Successfully turned member logging **{changed}**.

View File

@ -14,6 +14,26 @@ Successfully set the guild description to
[S_INDEX_TOGGLE]
Successfully toggled guild indexing `{toggle}`
//GUILD WELCOMER
[S_WELCOMER_DESCRIPTION]
Configure a message that is sent to new members upon join.
**Usable tags:**
{mention} - mentions the user
{tag} - username#discriminator
{user} - username
{guildsize} - member count of the server
{guildname} - name of the server
{accage} - age of the account
{id} - ID of the account
[S_WELCOMER_TOGGLE]
Successfully toggled the welcomer `{toggle}`.
[S_WELCOMER_SET]
Successfully set the welcomer message to
`{change}`
//guildPrefix Setting
[S_GUILDPREFIX_DESCRIPTION]

View File

@ -10,7 +10,7 @@ class Dispatcher {
const observers = this.client.registry.components
.filter((c) => c.type === 'observer' && !c.disabled)
.sort((a, b) => b.priority - a.priority);
.sort((a, b) => a.priority - b.priority);
for(const observer of observers.values()) {
for(const [hook, func] of observer.hooks) {

View File

@ -80,7 +80,7 @@ class Resolver {
add: ['add', '+'],
set: ['set', '='],
remove: ['remove', 'delete', '-'],
reset: ['clear', 'reset'],
reset: ['clear', 'reset', 'default'],
off: ['off', 'disable', 'false', 'no', 'n', 'f'],
on: ['on', 'enable', 'true', 'yes', 'y', 't']
};

View File

@ -1,4 +1,4 @@
const { Command } = require('../../../../interfaces/');
const { Command } = require('../../../../interfaces');
class GuildCommand extends Command {
@ -6,7 +6,7 @@ class GuildCommand extends Command {
super(client, {
name: 'guild',
module: 'utility',
module: 'information',
aliases: [
'server'
]
@ -40,7 +40,10 @@ class GuildCommand extends Command {
id: guild.id,
name: guild.name,
region: guild.region,
members: guild.memberCount
members: guild.memberCount,
boosters: guild.premiumSubscriptionCount,
tier: guild.premiumTier,
shard: guild.shardID
}),
thumbnail: {
url: guild.iconURL()

View File

@ -1,4 +1,4 @@
const { Command } = require('../../../../interfaces/');
const { Command } = require('../../../../interfaces');
const similarity = require('similarity');
class UserCommand extends Command {
@ -7,7 +7,7 @@ class UserCommand extends Command {
super(client, {
name: 'user',
module: 'utility',
module: 'information',
description: 'Display information about user.',
guildOnly: true,
arguments: [

View File

@ -0,0 +1,35 @@
const { Inhibitor } = require('../../../interfaces/');
class ChannelIgnore extends Inhibitor {
constructor(client) {
super(client, {
name: 'channelIgnore',
priority: 5,
guild: true
});
}
execute(message, command) {
if(message.member.admin) return super._succeed();
const { guild, member, channel } = message,
setting = guild._settings.ignore,
roles = member._roles;
if(!setting.enabled) return super._succeed();
if(setting.channels.includes(channel.id))
for(const role of roles) {
if(setting.roleBypass.includes(role)) return super._succeed();
return super._fail({ error: true, silent: true });
}
return super._succeed();
}
}
module.exports = ChannelIgnore;

View File

@ -13,7 +13,7 @@ class Restricted extends Inhibitor {
}
execute(message, command) {
if(command.restricted && !this.client._options.bot.owners.includes(message.author.id)) {
if(command.restricted && !message.author.developer) {
return super._fail();
}
return super._succeed();

View File

@ -384,6 +384,7 @@ class CommandHandler extends Observer {
async handleCommand(message) {
<<<<<<< HEAD
const inhibitors = await this._handleInhibitors(message);
const silent = inhibitors.filter((i) => i.inhibitor.silent);
@ -391,6 +392,11 @@ class CommandHandler extends Observer {
if(nonsilent.length === 0 && silent.length > 0) return undefined;
if(nonsilent.length > 0) return this.handleError(message, { type: 'inhibitor', ...nonsilent[0] });
=======
const inhibitor = await this._handleInhibitors(message);
//console.log(inhibitor);
if(inhibitor.error) return this.handleError(message, { type: 'inhibitor', ...inhibitor });
>>>>>>> 33c6f57150e4727e216796dc63e8dd5ceb587070
const resolved = await message.resolve();
if(resolved.error) {
@ -420,8 +426,13 @@ class CommandHandler extends Observer {
const reasons = (await Promise.all(promises)).filter((p) => p.error); // Filters out inhibitors with only errors.
if(reasons.length === 0) return [];
<<<<<<< HEAD
reasons.sort((a, b) => b.inhibitor.priority - a.inhibitor.priority); // Sorts inhibitor errors by most important.
return reasons;
=======
reasons.sort((a, b) => a.inhibitor.priority - b.inhibitor.priority); // Sorts inhibitor errors by most important.
return reasons[0];
>>>>>>> 33c6f57150e4727e216796dc63e8dd5ceb587070
}
@ -577,7 +588,8 @@ class CommandHandler extends Observer {
}
};
return message.respond(await messages[error.type](error), { emoji: 'failure' });
//return this.client.rateLimiter.limitSend(message.channel, await messages[error.type](error));
return message.limitedRespond(await messages[error.type](error), { emoji: 'failure', limit: 10, utility: error.type });
}

View File

@ -7,7 +7,18 @@ const CONSTANTS = {
YELLOW: 15120384, // message edit
LIGHT_BLUE: 11337726, // message pin
BLUE: 479397
}
},
IMAGES: {
PREMIUM_LIMIT: 2,
UPLOAD_LIMIT: {
'0': 8,
'1': 8,
'2': 50,
'3': 100
},
MB_DIVIDER: 1024*1024
},
WEEK: 7 * 24 * 60 * 60
};
class GuildLogger extends Observer {
@ -37,6 +48,85 @@ class GuildLogger extends Observer {
async storeAttachment(message) {
const { guild, member, author, channel } = message;
if(!guild || author.bot) return;
if(!message.attachments.size) return;
console.log('Store call')
const settings = await guild.settings(),
setting = settings.messageLog,
roles = member._roles,
{ ignoredRoles } = setting;
//if(guild._settings.premium < CONSTANTS.IMAGES.PREMIUM_LIMIT) return;
if(!setting.attachments || setting.ignoredChannels.includes(channel.id)) return;
if(setting.ignoredRoles.length && roles.length)
for(const role of ignoredRoles)
if(roles.includes(role)) return;
console.log('Can store')
const attachments = message.attachments.values();
for(const attachment of attachments) {
const TH = this.client.transactionHandler;
const { size, name, id } = attachment;
const fsize = size/CONSTANTS.IMAGES.MB_DIVIDER // File size in MB
if(fsize > CONSTANTS.IMAGES.UPLOAD_LIMIT[guild.premiumTier]) continue;
const buffer = await Util.downloadAsBuffer(attachment.url).catch(err => { this.client.logger.error(err); return null; });
if(!buffer) return;
const data = {
buffer,
id
};
try {
//TODO: test this
//const start = Date.now();
const result = await TH.send({
provider: 'mongodb',
request: {
type: 'insertOne',
collection: 'attachment_logs',
data
}
});
//console.log(`Took ${Date.now()-start}ms to insert image.`);
//console.log(result);
const metadata = {
database_ID: result.insertedId,
id,
guild: guild.id,
message: message.id,
author: author.id,
name,
size,
timestamp: Math.floor(Date.now()/1000),
removeAt: Math.floor(Date.now()/1000 + guild._settings.premium * CONSTANTS.WEEK * 0.25)
}
//console.log(metadata);
await TH.send({
provider: 'mongodb',
request: {
type: 'insertOne',
collection: 'attachment_logs_index',
data: metadata
}
});
//console.log(`Took ${Date.now()-start}ms to insert entire entry.`);
} catch (err) {
this.client.logger.error('Something went wrong with storing image to db:\n' + err.stack);
}
}
}
//TODO: Figure this thing out, this should be called from messageDelete and rawMessageDelete if any attachments are present
@ -67,7 +157,7 @@ class GuildLogger extends Observer {
const perms = logChannel.permissionsFor(guild.me);
if(!perms.has('SEND_MESSAGES') || !perms.has('VIEW_CHANNEL')) return;
if (ignoredRoles && member._roles.length)
if (ignoredRoles.length && member._roles.length)
for (const role of ignoredRoles)
if (member._roles.includes(role)) return;
@ -235,14 +325,15 @@ class GuildLogger extends Observer {
}
_replaceTags(text, member) {
const { user } = member;
const { user, guild } = member;
return text
.replace(/\{mention\}/g, `<@${member.id}>`)
.replace(/\{tag\}/g, `${Util.escapeMarkdown(user.tag)}`)
.replace(/\{user\}/g, `${user.username}`)
.replace(/\{guildsize\}/g, `${guild.memberCount}`)
.replace(/\{accage\}/g, `${this.client.resolver.timeAgo(Date.now()/1000 - user.createdTimestamp/1000)}`) //.replace(/a/, '1')
.replace(/\{id\}/g, `${user.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)
.trim();
}

View File

@ -0,0 +1,67 @@
const { Observer } = require('../../../interfaces/');
const CONSTANTS = {};
class UtilityHook extends Observer {
constructor(client) {
super(client, {
name: 'utility',
priority: 3
});
this.hooks = [
['guildMemberAdd', this.welcome.bind(this)],
['guildMemberAdd', this.autorole.bind(this)]
]
}
async stickyRole(member) {
const { guild } = member;
const settings = await guild.settings();
const setting = settings.stickyRole;
if(!setting.enabled) return;
}
async autorole(member) {
const { guild } = member;
const settings = await guild.settings();
const setting = settings.autorole;
if(!setting.enabled) return;
}
async welcome(member) {
const { guild, user } = member;
const settings = await guild.settings();
const setting = settings.welcomer;
if(!setting.enabled) return;
const channel = await user.createDM();
const message = this._replaceTags(setting.message, member);
await channel.send(message).catch();
}
_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)
.trim();
}
}
module.exports = UtilityHook;

View File

@ -24,8 +24,6 @@ class DmInfractionSetting extends Setting {
}
});
this.client = client;
}
async handle(message, args) {

View File

@ -58,8 +58,6 @@ class MemberLogsSetting extends Setting {
]
});
this.client = client;
}
async handle(message, args) {

View File

@ -36,8 +36,6 @@ class MessageLogsSetting extends Setting {
}
});
this.client = client;
}
async handle(message, params) {
@ -248,6 +246,11 @@ class MessageLogsSetting extends Setting {
value: await guild.resolveChannel(setting?.channel) || '`N/A`',
inline: true
},
{
name: '》Log attachments',
value: setting?.attachments || false,
inline: true
},
{
name: '》Ignored Roles',
value: roles?.map((r) => r.name).join(', ') || '`N/A`',

View File

@ -32,8 +32,6 @@ class ModerationLogsSetting extends Setting {
}
});
this.client = client;
}
async handle(message, params) {

View File

@ -51,8 +51,6 @@ class ModerationPointsSetting extends Setting {
}
});
this.client = client;
}
async handle(message, args) {

View File

@ -88,8 +88,6 @@ class MuteSetting extends Setting {
}
}
});
this.client = client;
}

View File

@ -26,8 +26,6 @@ class NicknameLogs extends Setting {
}
});
this.client = client;
}
async handle(message, params) {

View File

@ -26,8 +26,6 @@ class VoiceLogSettings extends Setting {
}
});
this.client = client;
}
async handle(message, params) {

View File

@ -26,8 +26,6 @@ class AutoroleSetting extends Setting {
custom: true
});
this.client = client;
}
async handle(message, params) {

View File

@ -29,8 +29,6 @@ class DescriptionSetting extends Setting {
custom: true
});
this.client = client;
}
async handle(message, params) {

View File

@ -20,8 +20,6 @@ class GuildPrefixSetting extends Setting {
},
custom: true
});
this.client = client;
}

View File

@ -30,8 +30,6 @@ class IgnoreSetting extends Setting {
custom: true
});
this.client = client;
}
async handle(message, params) {

View File

@ -26,8 +26,6 @@ class PermissionTypeSetting extends Setting {
'permissiontype reset'
]
});
this.client = client;
}

View File

@ -0,0 +1,77 @@
const { Setting } = require('../../../../interfaces/');
class WelcomerSetting extends Setting {
constructor(client) {
super(client, {
name: 'welcomer',
module: 'utility',
aliases: [
],
usage: '<value>',
examples: [
'welcomer Welcome to {guildname}!',
'welcomer <on|off>',
'welcomer reset'
],
guarded: false,
resolve: 'GUILD',
default: {
welcomer: {
message: 'Welcome to {guildname}!',
enabled: false
}
},
custom: true
});
}
async handle(message, params) {
const { guild } = message;
const setting = guild._settings[this.index] || this.default[this.index];
const { resolver } = this.client;
const response = await resolver.resolveMethod(params);
const langParams = {};
let index = null;
if(resolver.resolveBoolean(params[0]) !== null) {
setting.enabled = resolver.resolveBoolean(params[0]);
index = 'S_WELCOMER_TOGGLE';
langParams.toggle = message.format('ON_OFF_TOGGLE', { toggle: setting.enabled }, true)
} else {
setting.message = message.content.replace(guild.prefix, '').replace(message._caller, '').replace(message._settingCaller, '').trim();
index = 'S_WELCOMER_SET';
langParams.change = setting.message;
}
await message.guild._updateSettings({ [this.index]: setting });
return {
error: false,
msg: message.format(index, langParams)
};
}
async fields(guild) {
const setting = guild._settings[this.index] || this.default[this.index];
return [
{
name: '》Enabled',
value: setting.enabled,
inline: false
},
{
name: '》Message',
value: setting.message.substring(0,1021) + setting.message.length > 1021 ? '...' : '',
inline: false
}
]
}
}
module.exports = WelcomerSetting;

View File

@ -33,6 +33,10 @@ const GuildMember = Structures.extend('GuildMember', (GuildMember) => {
}
get admin() {
return this.user.developer || this.hasPermission('ADMINISTRATOR') || this.hasPermission('MANAGE_GUILD');
}
get timeSinceCached() {
return Date.now()-this._cached;
}

View File

@ -1,7 +1,6 @@
const { Structures } = require('discord.js');
const escapeRegex = require('escape-string-regexp');
const emojis = require('../../util/emojis.json');
const { Util, Emojis } = require('../../util/');
const { stripIndents } = require('common-tags')
@ -90,7 +89,7 @@ const Message = Structures.extend('Message', (Message) => {
if(typeof str === 'string') {
if(opts.emoji) {
const emoji = emojis[opts.emoji];
const emoji = Emojis[opts.emoji];
if(!emoji) this.command.client.logger.warn(`Invalid emoji provided to command ${this.command.resolveable}: "${opts.emoji}".`);
str = `${emoji} ${str}`;
}
@ -103,10 +102,25 @@ const Message = Structures.extend('Message', (Message) => {
}
async limitedRespond(str, opts = { attachments: [] }) {
if(typeof str === 'string') {
if(opts.emoji) {
const emoji = Emojis[opts.emoji];
if(!emoji) this.client.logger.warn(`Invalid emoji provided to command ${this.command.resolveable}: "${opts.emoji}".`);
str = `${emoji} ${str}`;
}
if(opts.reply) str = `<@!${this.author.id}> ${str}`;
}
return this.client.rateLimiter.limitSend(this.channel, str, opts.limit, opts.utility);
}
async edit(str, opts) {
if(!this.editable) return null;
if(typeof str === 'string') {
if(opts.emoji) str = `${emojis[opts.emoji]} ${str}`;
if(opts.emoji) str = `${Emojis[opts.emoji]} ${str}`;
if(opts.reply) str = `<@!${this.author.id}> ${str}`;
}
return super.edit(str);
@ -114,7 +128,7 @@ const Message = Structures.extend('Message', (Message) => {
async prompt(str, opts) {
if(typeof str === 'string') {
if(opts.emoji) str = `${emojis[opts.emoji]} ${str}`;
if(opts.emoji) str = `${Emojis[opts.emoji]} ${str}`;
if(opts.reply) str = `<@!${this.author.id}> ${str}`;
}
await this.channel.send(str);

View File

@ -11,6 +11,15 @@ class Util {
throw new Error("Class may not be instantiated.");
}
static downloadAsBuffer(source) {
return new Promise((resolve, reject) => {
fetch(source).then(res => {
if(res.ok) resolve(res.buffer());
else reject(res.statusText);
});
});
}
static readdirRecursive(directory) {
const result = [];