diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 00000000..4fee75bb --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,17 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch via NPM", + "runtimeExecutable": "npm", + "runtimeArgs": ["run-script", "start"], + "port": 9229, + "timeout": 15000 + } + ] +} diff --git a/locale/en.json b/locale/en.json index dfb57ee0..3ba67fad 100644 --- a/locale/en.json +++ b/locale/en.json @@ -1155,7 +1155,10 @@ "premiumOnly": "This command is only available for premium subscribers!", "rateLimit": "Slow down! Please wait {{ cooldown }} seconds and try again.", "role": "You do not have permission to use this command.\nYou need one of the following roles: {{{ roles }}}", - "viewAuditLogs": "View Audit Logs" + "viewAuditLogs": "View Audit Logs", + "manageMessages": "permissions.manageMessages", + "readMessageHistory": "permissions.readMessageHistory", + "missing": "permissions.missing" }, "prompt": { "canceled": "Canceled" diff --git a/package-lock.json b/package-lock.json index cd92624f..8c335f1f 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "discord-invite-manager", - "version": "8.2.1", + "version": "8.3.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 7161e92f..87d9e38f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "discord-invite-manager", - "version": "8.2.1", + "version": "8.3.0", "description": "", "main": "./bin/bot.js", "scripts": { diff --git a/scripts/dev-run.js b/scripts/dev-run.js index 541b6dc3..f3317ed0 100644 --- a/scripts/dev-run.js +++ b/scripts/dev-run.js @@ -15,7 +15,14 @@ child.on('error', error => console.log(error)); child.on('close', () => { child = spawn( 'node', - ['--inspect', './bin/bot.js', '--no-rabbitmq', config.devToken, '1', '1'], + [ + '--inspect-brk=9229', + './bin/bot.js', + '--no-rabbitmq', + config.devToken, + '1', + '1' + ], { stdio: 'inherit' } diff --git a/src/client.ts b/src/client.ts index 39109939..2ac0f04f 100644 --- a/src/client.ts +++ b/src/client.ts @@ -200,8 +200,9 @@ export class IMClient extends Client { this.music = new MusicService(this); // Services - this.rabbitmq.init(); this.cmds.init(); + this.rabbitmq.init(); + this.scheduler.init(); this.disabledGuilds = new Set(); @@ -323,7 +324,7 @@ export class IMClient extends Client { this.dbl = new DBL(this.config.bot.dblToken, this); } - this.setActivity(); + await this.setActivity(); this.activityInterval = setInterval( () => this.setActivity(), 1 * 60 * 1000 @@ -506,7 +507,7 @@ export class IMClient extends Client { if (modLogChannelId) { const logChannel = guild.channels.get(modLogChannelId) as TextChannel; if (logChannel) { - this.msg.sendEmbed(logChannel, embed); + await this.msg.sendEmbed(logChannel, embed); } } } @@ -554,7 +555,7 @@ export class IMClient extends Client { } ] }); - this.msg.sendEmbed(logChannel, embed); + await this.msg.sendEmbed(logChannel, embed); } } @@ -569,18 +570,8 @@ export class IMClient extends Client { createdAt: new Date(), updatedAt: new Date() }, - { - id: guild.id, - name: guild.name, - icon: guild.iconURL, - memberCount: guild.memberCount, - banReason: null - }, - { - id: message.author.id, - discriminator: message.author.discriminator, - name: message.author.username - } + guild, + message.author ); } @@ -619,7 +610,11 @@ export class IMClient extends Client { public async setActivity() { if (this.dbl) { - this.dbl.postStats(this.guilds.size, this.shardId - 1, this.shardCount); + await this.dbl.postStats( + this.guilds.size, + this.shardId - 1, + this.shardCount + ); } await this.updateGatewayInfo(); @@ -660,13 +655,13 @@ export class IMClient extends Client { private async onConnect() { console.error('DISCORD CONNECT'); this.gatewayConnected = true; - this.rabbitmq.sendStatusToManager(); + await this.rabbitmq.sendStatusToManager(); } private async onDisconnect(err: Error) { console.error('DISCORD DISCONNECT'); this.gatewayConnected = false; - this.rabbitmq.sendStatusToManager(err); + await this.rabbitmq.sendStatusToManager(err); if (err) { console.error(err); diff --git a/src/framework/services/Commands.ts b/src/framework/services/Commands.ts index ba5ece02..af2010d1 100644 --- a/src/framework/services/Commands.ts +++ b/src/framework/services/Commands.ts @@ -30,7 +30,7 @@ export class CommandsService { this.commandCalls = new Map(); } - public async init() { + public init() { console.log(`Loading commands...`); // Load all commands @@ -151,7 +151,7 @@ export class CommandsService { if (content === '') { // If the content is an empty string then the user just mentioned the // bot, so we'll print the prefix to help them out. - this.client.msg.sendReply( + await this.client.msg.sendReply( message, t('bot.mentionHelp', { prefix: sets.prefix @@ -225,7 +225,7 @@ export class CommandsService { if (!lastCall.warned) { lastCall.warned = true; lastCall.last = now + cooldown * 1000; - this.client.msg.sendReply( + await this.client.msg.sendReply( message, t('permissions.rateLimit', { cooldown: cooldown.toString() @@ -247,7 +247,7 @@ export class CommandsService { : false; if (!isPremium && cmd.premiumOnly) { - this.client.msg.sendReply(message, t('permissions.premiumOnly')); + await this.client.msg.sendReply(message, t('permissions.premiumOnly')); return; } @@ -267,7 +267,7 @@ export class CommandsService { console.error( `Could not get member ${message.author.id} for ${guild.id}` ); - this.client.msg.sendReply(message, t('permissions.memberError')); + await this.client.msg.sendReply(message, t('permissions.memberError')); return; } @@ -283,7 +283,7 @@ export class CommandsService { if (perms && perms.length > 0) { // Check that we have at least one of the required roles if (!perms.some(p => member.roles.indexOf(p) >= 0)) { - this.client.msg.sendReply( + await this.client.msg.sendReply( message, t('permissions.role', { roles: perms.map(p => `<@&${p}>`).join(', ') @@ -293,7 +293,7 @@ export class CommandsService { } } else if (cmd.strict) { // Allow commands that require no roles, if strict is not true - this.client.msg.sendReply(message, t('permissions.adminOnly')); + await this.client.msg.sendReply(message, t('permissions.adminOnly')); return; } } @@ -310,7 +310,7 @@ export class CommandsService { !(channel as GuildChannel).permissionsOf(this.client.user.id).has(p) ); if (missingPerms.length > 0) { - this.client.msg.sendReply( + await this.client.msg.sendReply( message, t(`permissions.missing`, { channel: `<#${channel.id}>`, @@ -395,7 +395,7 @@ export class CommandsService { let val: any = true; if (!skipVal) { if (!hasVal) { - this.client.msg.sendReply( + await this.client.msg.sendReply( message, `Missing required value for flag \`${flag.name}\`.\n` + `\`${cmd.usage.replace('{prefix}', sets.prefix)}\`\n` + @@ -406,7 +406,7 @@ export class CommandsService { try { val = await resolver.resolve(rawVal, context, []); } catch (e) { - this.client.msg.sendReply( + await this.client.msg.sendReply( message, `Invalid value for \`${flag.name}\`: ${e.message}\n` + `\`${cmd.usage.replace('{prefix}', sets.prefix)}\`\n` + @@ -443,7 +443,7 @@ export class CommandsService { const val = await resolver.resolve(rawVal, context, args); if (typeof val === typeof undefined && arg.required) { - this.client.msg.sendReply( + await this.client.msg.sendReply( message, t('resolvers.missingRequired', { name: arg.name, @@ -456,7 +456,7 @@ export class CommandsService { args.push(val); } catch (e) { - this.client.msg.sendReply( + await this.client.msg.sendReply( message, t('resolvers.invalid', { name: arg.name, @@ -473,56 +473,68 @@ export class CommandsService { } // Run command + let error: any = null; try { await cmd.action(message, args, flags, context); } catch (e) { - console.error(e); + // Save the error for later so error handling doesn't count to command process time + error = e; + } + + const execTime = Date.now() - start; + + if (error) { + console.error(error); + withScope(scope => { - scope.setTag('command', cmd.name); - scope.setExtra('message', message.content); if (guild) { scope.setUser({ id: guild.id }); } - captureException(e); + scope.setTag('command', cmd.name); + scope.setExtra('message', message.content); + captureException(error); }); - this.client.msg.sendReply( + + if (guild) { + this.client.dbQueue.addIncident( + { + id: null, + guildId: guild.id, + error: error.message, + details: { + command: cmd.name, + author: message.author.id, + message: message.content + } + }, + guild + ); + } + + await this.client.msg.sendReply( message, t('cmd.error', { - error: e.message + error: error.message }) ); - return; } - const execTime: number = Date.now() - start; - // Ignore messages that are not in guild chat or from disabled guilds if (guild && !this.client.disabledGuilds.has(guild.id)) { // We have to add the guild and members too, in case our DB does not have them yet - /*this.client.dbQueue.addCommandUsage( + this.client.dbQueue.addCommandUsage( { id: null, guildId: guild.id, memberId: message.author.id, command: cmd.name, args: args.join(' '), - time: execTime, - createdAt: new Date(), - updatedAt: new Date() + errored: error !== null, + time: execTime }, - { - id: guild.id, - name: guild.name, - icon: guild.iconURL, - memberCount: guild.memberCount, - banReason: null - }, - { - id: message.author.id, - name: message.author.username, - discriminator: message.author.discriminator - } - );*/ + guild, + message.author + ); } } } diff --git a/src/framework/services/DBQueue.ts b/src/framework/services/DBQueue.ts index f12d2997..33468778 100644 --- a/src/framework/services/DBQueue.ts +++ b/src/framework/services/DBQueue.ts @@ -1,22 +1,30 @@ +import { Guild } from 'eris'; + import { IMClient } from '../../client'; import { commandUsage, CommandUsageAttributes, - GuildAttributes, guilds, + IncidentAttributes, + incidents, LogAttributes, logs, - MemberAttributes, members } from '../../sequelize'; +import { BasicUser } from '../../types'; export class DBQueueService { private client: IMClient = null; + private guilds: Set = new Set(); + private doneGuilds: Set = new Set(); + + private users: Set = new Set(); + private doneUsers: Set = new Set(); + private logActions: LogAttributes[] = []; - private guilds: GuildAttributes[] = []; - private members: MemberAttributes[] = []; private cmdUsages: CommandUsageAttributes[] = []; + private incidents: IncidentAttributes[] = []; public constructor(client: IMClient) { this.client = client; @@ -24,48 +32,87 @@ export class DBQueueService { setInterval(() => this.syncDB(), 10000); } - public addLogAction( - action: LogAttributes, - guild: GuildAttributes, - member: MemberAttributes - ) { - this.guilds.push(guild); - this.members.push(member); + public addLogAction(action: LogAttributes, guild: Guild, user: BasicUser) { + if (!this.doneGuilds.has(guild.id)) { + this.guilds.add(guild); + } + + if (!this.doneUsers.has(user.id)) { + this.users.add(user); + } + this.logActions.push(action); } public addCommandUsage( cmdUsage: CommandUsageAttributes, - guild: GuildAttributes, - member: MemberAttributes + guild: Guild, + user: BasicUser ) { - this.guilds.push(guild); - this.members.push(member); + if (!this.doneGuilds.has(guild.id)) { + this.guilds.add(guild); + } + + if (!this.doneUsers.has(user.id)) { + this.users.add(user); + } + this.cmdUsages.push(cmdUsage); } + public addIncident(incident: IncidentAttributes, guild: Guild) { + if (!this.doneGuilds.has(guild.id)) { + this.guilds.add(guild); + } + this.incidents.push(incident); + } + private async syncDB() { - if (this.logActions.length === 0 && this.cmdUsages.length === 0) { + if ( + this.logActions.length === 0 && + this.cmdUsages.length === 0 && + this.incidents.length === 0 + ) { return; } console.time('syncDB'); - await guilds.bulkCreate(this.guilds, { - updateOnDuplicate: ['name', 'icon', 'memberCount'] - }); - this.guilds = []; - - await members.bulkCreate(this.members, { - updateOnDuplicate: ['name', 'discriminator'] - }); - this.members = []; + const newGuilds = [...this.guilds.values()]; + this.guilds.clear(); + await guilds.bulkCreate( + newGuilds.map(guild => ({ + id: guild.id, + name: guild.name, + icon: guild.iconURL, + memberCount: guild.memberCount, + banReason: null + })), + { + updateOnDuplicate: ['name', 'icon', 'memberCount'] + } + ); + newGuilds.forEach(g => this.doneGuilds.add(g.id)); - await logs.bulkCreate(this.logActions); - this.logActions = []; + const users = [...this.users.values()]; + this.users.clear(); + await members.bulkCreate( + users.map(user => ({ + id: user.id, + name: user.username, + discriminator: user.discriminator + })), + { + updateOnDuplicate: ['name', 'discriminator'] + } + ); + users.forEach(u => this.doneUsers.add(u.id)); - await commandUsage.bulkCreate(this.cmdUsages); - this.cmdUsages = []; + await Promise.all([ + logs.bulkCreate(this.logActions).then(() => (this.logActions = [])), + commandUsage.bulkCreate(this.cmdUsages).then(() => (this.cmdUsages = [])), + incidents.bulkCreate(this.incidents).then(() => (this.incidents = [])) + ]); console.timeEnd('syncDB'); } diff --git a/src/framework/services/Messaging.ts b/src/framework/services/Messaging.ts index ec25ef07..2a3845f1 100644 --- a/src/framework/services/Messaging.ts +++ b/src/framework/services/Messaging.ts @@ -122,6 +122,41 @@ export class MessagingService { const content = convertEmbedToPlain(e); + const handleException = (err: Error, reportIndicent = true): undefined => { + withScope(scope => { + if (target instanceof GuildChannel) { + scope.setUser({ id: target.guild.id }); + scope.setExtra( + 'permissions', + target.permissionsOf(this.client.user.id).json + ); + } + scope.setExtra('channel', target.id); + scope.setExtra('message', embed); + scope.setExtra('content', content); + if (fallbackUser) { + scope.setExtra('fallbackUser', fallbackUser.id); + } + captureException(err); + }); + if (reportIndicent && target instanceof GuildChannel) { + this.client.dbQueue.addIncident( + { + id: null, + guildId: target.guild.id, + error: err.message, + details: { + channel: target.id, + embed, + content + } + }, + target.guild + ); + } + return undefined; + }; + return new Promise((resolve, reject) => { // Fallback functions when sending message fails const sendDM = (error?: any) => { @@ -147,41 +182,9 @@ export class MessagingService { return dmChannel .createMessage(msg) .then(resolve) - .catch(err2 => { - withScope(scope => { - if (target instanceof GuildChannel) { - scope.setUser({ id: target.guild.id }); - scope.setExtra( - 'permissions', - target.permissionsOf(this.client.user.id).json - ); - } - scope.setExtra('channel', target.id); - scope.setExtra('message', embed); - scope.setExtra('content', content); - scope.setExtra('fallbackUser', fallbackUser.id); - captureException(err2); - }); - return undefined; - }); + .catch(err2 => handleException(err2, false)); }) - .catch(err2 => { - withScope(scope => { - if (target instanceof GuildChannel) { - scope.setUser({ id: target.guild.id }); - scope.setExtra( - 'permissions', - target.permissionsOf(this.client.user.id).json - ); - } - scope.setExtra('channel', target.id); - scope.setExtra('message', embed); - scope.setExtra('content', content); - scope.setExtra('fallbackUser', fallbackUser.id); - captureException(err2); - }); - return undefined; - }); + .catch(err2 => handleException(err2, false)); }; const sendPlain = (error?: any) => { @@ -199,20 +202,7 @@ export class MessagingService { .createMessage(content) .then(resolve) .catch(err => { - withScope(scope => { - if (target instanceof GuildChannel) { - scope.setUser({ id: target.guild.id }); - scope.setExtra( - 'permissions', - target.permissionsOf(this.client.user.id).json - ); - } - scope.setExtra('channel', target.id); - scope.setExtra('message', embed); - scope.setExtra('content', content); - captureException(err); - }); - + handleException(err); return sendDM(error); }); }; @@ -235,20 +225,7 @@ export class MessagingService { .createMessage({ embed: e }) .then(resolve) .catch(error => { - withScope(scope => { - if (target instanceof GuildChannel) { - scope.setUser({ id: target.guild.id }); - scope.setExtra( - 'permissions', - target.permissionsOf(this.client.user.id).json - ); - } - scope.setExtra('channel', target.id); - scope.setExtra('message', embed); - scope.setExtra('content', content); - captureException(error); - }); - + handleException(error); return sendPlain(error); }); }; @@ -379,7 +356,7 @@ export class MessagingService { const sudo: boolean = (prevMsg as any).__sudo; if (prevMsg.author.id === this.client.user.id) { - prevMsg.edit({ embed }); + await prevMsg.edit({ embed }); if (!author) { throw new Error( 'Either the message of the original author must be passed, or you must explicitly specify the original author' @@ -411,7 +388,7 @@ export class MessagingService { .getReaction(upSymbol, 10) .catch(() => [] as User[]); if (users.find(u => u.id === author.id)) { - prevMsg.removeReaction(upSymbol, this.client.user.id); + await prevMsg.removeReaction(upSymbol, this.client.user.id); } } @@ -422,7 +399,7 @@ export class MessagingService { .getReaction(downSymbol, 10) .catch(() => [] as User[]); if (users.find(u => u.id === author.id)) { - prevMsg.removeReaction(downSymbol, this.client.user.id); + await prevMsg.removeReaction(downSymbol, this.client.user.id); } } @@ -442,18 +419,18 @@ export class MessagingService { const isUp = emoji.name === upSymbol; if (isUp && page > 0) { - this.showPaginated(prevMsg, page - 1, maxPage, render, author); + await this.showPaginated(prevMsg, page - 1, maxPage, render, author); } else if (!isUp && page < maxPage) { - this.showPaginated(prevMsg, page + 1, maxPage, render, author); + await this.showPaginated(prevMsg, page + 1, maxPage, render, author); } }; this.client.on('messageReactionAdd', func); - const timeOut = () => { + const timeOut = async () => { this.client.removeListener('messageReactionAdd', func); - prevMsg.removeReaction(upSymbol, this.client.user.id); - prevMsg.removeReaction(downSymbol, this.client.user.id); + await prevMsg.removeReaction(upSymbol, this.client.user.id); + await prevMsg.removeReaction(downSymbol, this.client.user.id); }; timer = setTimeout(timeOut, 15000); diff --git a/src/framework/services/RabbitMq.ts b/src/framework/services/RabbitMq.ts index d2301a9f..a55ae524 100644 --- a/src/framework/services/RabbitMq.ts +++ b/src/framework/services/RabbitMq.ts @@ -148,7 +148,7 @@ export class RabbitMqService { } public async sendStatusToManager(err?: Error) { - this.sendToManager({ + await this.sendToManager({ id: 'status', cmd: ShardCommand.STATUS, connected: this.client.gatewayConnected, @@ -186,13 +186,13 @@ export class RabbitMqService { switch (cmd) { case ShardCommand.STATUS: - this.sendStatusToManager(); + await this.sendStatusToManager(); break; case ShardCommand.CUSTOM: const self = await this.client.getSelf(); - sendResponse({ + await sendResponse({ self, guilds: this.client.guilds.map(g => ({ id: g.id, @@ -204,7 +204,7 @@ export class RabbitMqService { break; case ShardCommand.CACHE: - sendResponse(this.getCacheSizes()); + await sendResponse(this.getCacheSizes()); break; case ShardCommand.DIAGNOSE: @@ -262,7 +262,7 @@ export class RabbitMqService { const premium = await this.client.cache.premium.get(guildId); - sendResponse({ + await sendResponse({ owner, premium, settings: sets, @@ -275,12 +275,12 @@ export class RabbitMqService { case ShardCommand.FLUSH_CACHE: Object.values(this.client.cache).forEach(c => c.flush(guildId)); - sendResponse({}); + await sendResponse({}); break; case ShardCommand.RELOAD_MUSIC_NODES: - this.client.music.loadMusicNodes(); - sendResponse({}); + await this.client.music.loadMusicNodes(); + await sendResponse({}); break; case ShardCommand.LEAVE_GUILD: @@ -291,7 +291,7 @@ export class RabbitMqService { } await guild.leave(); - sendResponse({}); + await sendResponse({}); break; case ShardCommand.SUDO: @@ -309,11 +309,11 @@ export class RabbitMqService { this.client.channelGuildMap[channel.id] = guild.id; guild.channels.add(channel); - channel.listener = data => { + channel.listener = async data => { console.log(data); delete this.client.channelGuildMap[channel.id]; guild.channels.remove(channel); - sendResponse({ data }); + await sendResponse({ data }); }; const args = content.args ? content.args.join(' ') : ''; @@ -330,7 +330,7 @@ export class RabbitMqService { this.client ); (fakeMsg as any).__sudo = true; - this.client.cmds.onMessage(fakeMsg); + await this.client.cmds.onMessage(fakeMsg); break; case ShardCommand.OWNER_DM: @@ -338,9 +338,9 @@ export class RabbitMqService { const user = await this.client.getRESTUser(content.userId); const userChannel = await user.getDMChannel(); await userChannel.createMessage(content.message); - sendResponse({ ok: true }); + await sendResponse({ ok: true }); } catch (e) { - sendResponse({ ok: false, error: e }); + await sendResponse({ ok: false, error: e }); } break; @@ -366,7 +366,7 @@ export class RabbitMqService { inline: true }); - dmChannel.createMessage({ + await dmChannel.createMessage({ embed }); break; diff --git a/src/framework/services/Scheduler.ts b/src/framework/services/Scheduler.ts index eeeccb76..4829dece 100644 --- a/src/framework/services/Scheduler.ts +++ b/src/framework/services/Scheduler.ts @@ -12,7 +12,7 @@ export class SchedulerService { private client: IMClient = null; private scheduledActionTimers: Map; private scheduledActionFunctions: { - [k in ScheduledActionType]: (args: any) => void + [k in ScheduledActionType]: (args: any) => void; }; public constructor(client: IMClient) { @@ -21,7 +21,10 @@ export class SchedulerService { this.scheduledActionFunctions = { [ScheduledActionType.unmute]: this.unmute.bind(this) }; - this.scheduleScheduledActions(); + } + + public async init() { + await this.scheduleScheduledActions(); } public async addScheduledAction( diff --git a/src/modules/general/commands/config/botConfig.ts b/src/modules/general/commands/config/botConfig.ts index fa70beda..e1185617 100644 --- a/src/modules/general/commands/config/botConfig.ts +++ b/src/modules/general/commands/config/botConfig.ts @@ -41,7 +41,7 @@ export default class extends Command { const { guild, t } = context; if (this.client.type !== 'custom') { - this.sendReply( + await this.sendReply( message, t('cmd.botConfig.customOnly', { prefix: context.settings.prefix, @@ -142,7 +142,7 @@ export default class extends Command { updateOnDuplicate: ['value', 'updatedAt'] } ); - this.client.setActivity(); + await this.client.setActivity(); if (value === oldVal) { embed.description = t('cmd.botConfig.sameValue'); @@ -156,7 +156,7 @@ export default class extends Command { embed.description = t('cmd.botConfig.changed.text', { prefix, key }); // Log the settings change - this.client.logAction(guild, message, LogAction.config, { + await this.client.logAction(guild, message, LogAction.config, { key, oldValue: oldVal, newValue: value diff --git a/src/modules/general/commands/config/config.ts b/src/modules/general/commands/config/config.ts index 4a30b3ca..b13e680e 100644 --- a/src/modules/general/commands/config/config.ts +++ b/src/modules/general/commands/config/config.ts @@ -37,6 +37,11 @@ export default class extends Command { } ], group: CommandGroup.Config, + /*botPermissions: [ + GuildPermission.ADD_REACTIONS, + GuildPermission.MANAGE_MESSAGES, + GuildPermission.READ_MESSAGE_HISTORY + ],*/ guildOnly: true, defaultAdminOnly: true }); @@ -126,7 +131,7 @@ export default class extends Command { embed.description = t('cmd.config.changed.text', { prefix, key }); // Log the settings change - this.client.logAction(guild, message, LogAction.config, { + await this.client.logAction(guild, message, LogAction.config, { key, oldValue: oldVal, newValue: value @@ -332,7 +337,7 @@ export default class extends Command { member.voiceState.channelID ) as VoiceChannel; const conn = await this.client.music.getMusicConnection(guild); - conn.playAnnouncement(value, `Hi, my name is ${value}`, channel); + await conn.playAnnouncement(value, `Hi, my name is ${value}`, channel); } } } diff --git a/src/modules/general/commands/config/interactiveConfig.ts b/src/modules/general/commands/config/interactiveConfig.ts index 0624259a..3e4f13e0 100644 --- a/src/modules/general/commands/config/interactiveConfig.ts +++ b/src/modules/general/commands/config/interactiveConfig.ts @@ -69,7 +69,7 @@ export default class extends Command { .permissionsOf(this.client.user.id) .has(GuildPermission.MANAGE_MESSAGES) ) { - message.delete(); + await message.delete(); } const msg = await this.sendReply(message, embed); @@ -78,13 +78,13 @@ export default class extends Command { } for (let i = 0; i < this.choices.length; i++) { - msg.addReaction(this.choices[i]); + await msg.addReaction(this.choices[i]); } - msg.addReaction(this.prev); - msg.addReaction(this.next); - msg.addReaction(this.up); - msg.addReaction(this.cancel); + await msg.addReaction(this.prev); + await msg.addReaction(this.next); + await msg.addReaction(this.up); + await msg.addReaction(this.cancel); while ( (await this.showConfigMenu(context, message.author.id, msg, [])) === 'up' @@ -580,10 +580,10 @@ export default class extends Command { this.client.on('messageReactionAdd', func); - const timeOutFunc = () => { + const timeOutFunc = async () => { this.client.removeListener('messageReactionAdd', func); - msg.delete(); + await msg.delete(); resolve(undefined); }; diff --git a/src/modules/general/commands/config/inviteCodeConfig.ts b/src/modules/general/commands/config/inviteCodeConfig.ts index 3c206d4c..e8922f84 100644 --- a/src/modules/general/commands/config/inviteCodeConfig.ts +++ b/src/modules/general/commands/config/inviteCodeConfig.ts @@ -163,7 +163,7 @@ export default class extends Command { embed.description = t('cmd.inviteCodeConfig.changed.text', { prefix, key }); // Log the settings change - this.client.logAction(guild, message, LogAction.config, { + await this.client.logAction(guild, message, LogAction.config, { key, oldValue: oldVal, newValue: value diff --git a/src/modules/general/commands/config/memberConfig.ts b/src/modules/general/commands/config/memberConfig.ts index eb5ad082..3b4de6f3 100644 --- a/src/modules/general/commands/config/memberConfig.ts +++ b/src/modules/general/commands/config/memberConfig.ts @@ -117,7 +117,7 @@ export default class extends Command { if (value === null) { if (!info.clearable) { - this.sendReply( + await this.sendReply( message, t('cmd.memberConfig.canNotClear', { prefix, key }) ); @@ -151,7 +151,7 @@ export default class extends Command { embed.description = t('cmd.memberConfig.changed.text', { prefix, key }); // Log the settings change - this.client.logAction(guild, message, LogAction.config, { + await this.client.logAction(guild, message, LogAction.config, { key, oldValue: oldVal, newValue: value diff --git a/src/modules/general/commands/config/permissions.ts b/src/modules/general/commands/config/permissions.ts index 26124660..b48477fe 100644 --- a/src/modules/general/commands/config/permissions.ts +++ b/src/modules/general/commands/config/permissions.ts @@ -216,7 +216,7 @@ export default class extends Command { if (oldPerms.length > 0) { oldPerms.forEach(op => op.destroy()); - this.sendReply( + await this.sendReply( message, t('cmd.permissions.removed', { role: `<@&${role.id}>`, @@ -239,7 +239,7 @@ export default class extends Command { })) ); - this.sendReply( + await this.sendReply( message, t('cmd.permissions.added', { role: `<@&${role.id}>`, diff --git a/src/modules/general/commands/info/help.ts b/src/modules/general/commands/info/help.ts index ba00b000..0a101f32 100644 --- a/src/modules/general/commands/info/help.ts +++ b/src/modules/general/commands/info/help.ts @@ -140,6 +140,6 @@ export default class extends Command { value: linksArray.join(` | `) }); - this.sendReply(message, embed); + await this.sendReply(message, embed); } } diff --git a/src/modules/general/commands/roles/mentionRole.ts b/src/modules/general/commands/roles/mentionRole.ts index 09fec3bb..4645dcb1 100644 --- a/src/modules/general/commands/roles/mentionRole.ts +++ b/src/modules/general/commands/roles/mentionRole.ts @@ -3,9 +3,7 @@ import { Message, Role } from 'eris'; import { IMClient } from '../../../../client'; import { Command, Context } from '../../../../framework/commands/Command'; import { RoleResolver } from '../../../../framework/resolvers'; -import { BotCommand, CommandGroup } from '../../../../types'; - -const MISSING_PERMISSIONS = 50013; +import { BotCommand, CommandGroup, GuildPermission } from '../../../../types'; export default class extends Command { public constructor(client: IMClient) { @@ -20,6 +18,7 @@ export default class extends Command { } ], group: CommandGroup.Other, + botPermissions: [GuildPermission.MANAGE_ROLES], guildOnly: true, defaultAdminOnly: true, extraExamples: ['!mentionRole @Role', '!mentionRole "Role with space"'] @@ -56,14 +55,7 @@ export default class extends Command { ); } - const res = await role - .edit({ mentionable: true }, 'Pinging role') - .catch(e => { - if (e.code === MISSING_PERMISSIONS) { - this.sendReply(message, 'cmd.mentionRole.missingPermissions'); - } - return null as Role; - }); + const res = await role.edit({ mentionable: true }, 'Pinging role'); if (!res) { return; } diff --git a/src/modules/invites/commands/invites/clearInvites.ts b/src/modules/invites/commands/invites/clearInvites.ts index e491d334..4f4cd6bc 100644 --- a/src/modules/invites/commands/invites/clearInvites.ts +++ b/src/modules/invites/commands/invites/clearInvites.ts @@ -120,7 +120,7 @@ export default class extends Command { this.client.cache.invites.flush(guild.id); } - this.client.logAction(guild, message, LogAction.clearInvites, { + await this.client.logAction(guild, message, LogAction.clearInvites, { clearBonus, ...(memberId && { targetId: memberId }) }); diff --git a/src/modules/invites/commands/invites/fake.ts b/src/modules/invites/commands/invites/fake.ts index fab3130a..31c61d60 100644 --- a/src/modules/invites/commands/invites/fake.ts +++ b/src/modules/invites/commands/invites/fake.ts @@ -105,7 +105,7 @@ export default class extends Command { const maxPage = Math.ceil(suspiciousJoins.length / USERS_PER_PAGE); const p = Math.max(Math.min(_page ? _page - 1 : 0, maxPage - 1), 0); - this.showPaginated(message, p, maxPage, page => { + await this.showPaginated(message, p, maxPage, page => { let description = ''; suspiciousJoins diff --git a/src/modules/invites/commands/invites/info.ts b/src/modules/invites/commands/invites/info.ts index 78381548..93315a4d 100644 --- a/src/modules/invites/commands/invites/info.ts +++ b/src/modules/invites/commands/invites/info.ts @@ -532,6 +532,6 @@ export default class extends Command { }); } - this.sendReply(message, embed); + await this.sendReply(message, embed); } } diff --git a/src/modules/invites/commands/invites/inviteCodes.ts b/src/modules/invites/commands/invites/inviteCodes.ts index 7a426d83..a877dae2 100644 --- a/src/modules/invites/commands/invites/inviteCodes.ts +++ b/src/modules/invites/commands/invites/inviteCodes.ts @@ -199,8 +199,8 @@ export default class extends Command { }); } - this.sendEmbed(await message.author.getDMChannel(), embed); - this.sendReply( + await this.sendEmbed(await message.author.getDMChannel(), embed); + await this.sendReply( message, `<@!${message.author.id}>, ${t('cmd.inviteCodes.dmSent')}` ); diff --git a/src/modules/invites/commands/invites/inviteDetails.ts b/src/modules/invites/commands/invites/inviteDetails.ts index e2ed868f..6313ac6f 100644 --- a/src/modules/invites/commands/invites/inviteDetails.ts +++ b/src/modules/invites/commands/invites/inviteDetails.ts @@ -46,7 +46,7 @@ export default class extends Command { }); if (invs.length === 0) { - this.sendReply(message, t('cmd.inviteDetails.noInviteCodes')); + await this.sendReply(message, t('cmd.inviteDetails.noInviteCodes')); return; } @@ -71,6 +71,6 @@ export default class extends Command { }) + '\n'; } - this.sendReply(message, invText); + await this.sendReply(message, invText); } } diff --git a/src/modules/invites/commands/invites/leaderboard.ts b/src/modules/invites/commands/invites/leaderboard.ts index 77e8ba00..5aa019fb 100644 --- a/src/modules/invites/commands/invites/leaderboard.ts +++ b/src/modules/invites/commands/invites/leaderboard.ts @@ -100,7 +100,7 @@ export default class extends Command { const style: LeaderboardStyle = settings.leaderboardStyle; // Show the leaderboard as a paginated list - this.showPaginated(message, p, maxPage, page => { + await this.showPaginated(message, p, maxPage, page => { const compText = t('cmd.leaderboard.comparedTo', { to: `**${comp.locale(settings.lang).fromNow()}**` }); diff --git a/src/modules/invites/commands/invites/restoreInvites.ts b/src/modules/invites/commands/invites/restoreInvites.ts index 080841db..743f7ec8 100644 --- a/src/modules/invites/commands/invites/restoreInvites.ts +++ b/src/modules/invites/commands/invites/restoreInvites.ts @@ -87,7 +87,7 @@ export default class extends Command { this.client.cache.invites.flush(guild.id); } - this.client.logAction(guild, message, LogAction.restoreInvites, { + await this.client.logAction(guild, message, LogAction.restoreInvites, { ...(memberId && { targetId: memberId }) }); diff --git a/src/modules/invites/commands/ranks/addRank.ts b/src/modules/invites/commands/ranks/addRank.ts index 093dbf7c..cb34c5f9 100644 --- a/src/modules/invites/commands/ranks/addRank.ts +++ b/src/modules/invites/commands/ranks/addRank.ts @@ -106,7 +106,7 @@ export default class extends Command { isNew = true; } - this.client.logAction( + await this.client.logAction( guild, message, isNew ? LogAction.addRank : LogAction.updateRank, diff --git a/src/modules/invites/commands/ranks/ranks.ts b/src/modules/invites/commands/ranks/ranks.ts index 1312603f..b3aa0059 100644 --- a/src/modules/invites/commands/ranks/ranks.ts +++ b/src/modules/invites/commands/ranks/ranks.ts @@ -45,7 +45,7 @@ export default class extends Command { const maxPage = Math.ceil(rs.length / RANKS_PER_PAGE); const startPage = Math.max(Math.min(_page ? _page - 1 : 0, maxPage - 1), 0); - this.showPaginated(message, startPage, maxPage, page => { + await this.showPaginated(message, startPage, maxPage, page => { let description = ''; rs.slice(page * RANKS_PER_PAGE, (page + 1) * RANKS_PER_PAGE).forEach( diff --git a/src/modules/invites/commands/ranks/removeRank.ts b/src/modules/invites/commands/ranks/removeRank.ts index 31087151..e5f05b02 100644 --- a/src/modules/invites/commands/ranks/removeRank.ts +++ b/src/modules/invites/commands/ranks/removeRank.ts @@ -41,7 +41,7 @@ export default class extends Command { if (rank) { await rank.destroy(); - this.client.logAction(guild, message, LogAction.removeRank, { + await this.client.logAction(guild, message, LogAction.removeRank, { rankId: rank.id, roleId: role.id }); diff --git a/src/modules/invites/services/Captcha.ts b/src/modules/invites/services/Captcha.ts index eafb3ce9..9c0c90ff 100644 --- a/src/modules/invites/services/Captcha.ts +++ b/src/modules/invites/services/Captcha.ts @@ -104,7 +104,7 @@ export class CaptchaService { } if (response === text) { - dmChannel.createMessage( + await dmChannel.createMessage( sets.captchaVerificationSuccessMessage.replace( /\{serverName\}/g, member.guild.name @@ -113,7 +113,7 @@ export class CaptchaService { return; } - dmChannel.createMessage( + await dmChannel.createMessage( i18n.__({ locale: sets.lang, phrase: 'captcha.invalid' }) ); } diff --git a/src/modules/invites/services/Invites.ts b/src/modules/invites/services/Invites.ts index 1677512f..0b4d5be9 100644 --- a/src/modules/invites/services/Invites.ts +++ b/src/modules/invites/services/Invites.ts @@ -643,7 +643,7 @@ export class InvitesService { `Guild ${guild.id} has invalid ` + `rank announcement channel ${rankChannelId}` ); - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.rankAnnouncementChannel, null diff --git a/src/modules/invites/services/Tracking.ts b/src/modules/invites/services/Tracking.ts index 87d8bc9b..4dfe607f 100644 --- a/src/modules/invites/services/Tracking.ts +++ b/src/modules/invites/services/Tracking.ts @@ -108,7 +108,7 @@ export class TrackingService { console.log( `Pending: ${this.pendingGuilds.size}/${this.initialPendingGuilds}` ); - this.client.rabbitmq.sendStatusToManager(); + await this.client.rabbitmq.sendStatusToManager(); } setTimeout(func, 0); @@ -416,7 +416,7 @@ export class TrackingService { ); // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.joinMessageChannel, null @@ -428,7 +428,7 @@ export class TrackingService { ); // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.joinMessageChannel, null @@ -446,7 +446,7 @@ export class TrackingService { ); // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.joinMessageChannel, null @@ -485,7 +485,7 @@ export class TrackingService { { id: member.id } ) ) - .catch(err => { + .catch(async err => { // Missing permissions if ( err.code === 50001 || @@ -493,7 +493,7 @@ export class TrackingService { err.code === 50013 ) { // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.joinMessageChannel, null @@ -511,7 +511,7 @@ export class TrackingService { { id: member.id } ) ) - .catch(err => { + .catch(async err => { // Missing permissions if ( err.code === 50001 || @@ -519,7 +519,7 @@ export class TrackingService { err.code === 50013 ) { // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.joinMessageChannel, null @@ -560,7 +560,7 @@ export class TrackingService { { id: member.id } ) ) - .catch(err => { + .catch(async err => { // Missing permissions if ( err.code === 50001 || @@ -568,7 +568,7 @@ export class TrackingService { err.code === 50013 ) { // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.joinMessageChannel, null @@ -647,11 +647,11 @@ export class TrackingService { await joinChannel .createMessage(typeof msg === 'string' ? msg : { embed: msg }) - .catch(err => { + .catch(async err => { // Missing permissions if (err.code === 50001 || err.code === 50020 || err.code === 50013) { // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.joinMessageChannel, null @@ -745,7 +745,7 @@ export class TrackingService { `message channel ${leaveChannelId}` ); // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.leaveMessageChannel, null @@ -768,7 +768,7 @@ export class TrackingService { } ) ) - .catch(err => { + .catch(async err => { // Missing permissions if ( err.code === 50001 || @@ -776,7 +776,7 @@ export class TrackingService { err.code === 50013 ) { // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.joinMessageChannel, null @@ -861,11 +861,11 @@ export class TrackingService { leaveChannel .createMessage(typeof msg === 'string' ? msg : { embed: msg }) - .catch(err => { + .catch(async err => { // Missing permissions if (err.code === 50001 || err.code === 50020 || err.code === 50013) { // Reset the channel - this.client.cache.settings.setOne( + await this.client.cache.settings.setOne( guild.id, SettingsKey.joinMessageChannel, null diff --git a/src/modules/mod/cache/StrikesCache.ts b/src/modules/mod/cache/StrikesCache.ts index 4748393c..37563ff1 100644 --- a/src/modules/mod/cache/StrikesCache.ts +++ b/src/modules/mod/cache/StrikesCache.ts @@ -1,6 +1,5 @@ -import { StrikeConfigInstance, strikeConfigs } from '../../../sequelize'; - import { Cache } from '../../../framework/cache/Cache'; +import { StrikeConfigInstance, strikeConfigs } from '../../../sequelize'; export class StrikesCache extends Cache { public async init() { diff --git a/src/modules/mod/commands/automod/punishment-config.ts b/src/modules/mod/commands/automod/punishment-config.ts index 3f90baf9..bb59760d 100644 --- a/src/modules/mod/commands/automod/punishment-config.ts +++ b/src/modules/mod/commands/automod/punishment-config.ts @@ -97,6 +97,6 @@ export default class extends Command { } this.client.cache.punishments.flush(guild.id); - this.sendReply(message, embed); + await this.sendReply(message, embed); } } diff --git a/src/modules/mod/commands/automod/strike-config.ts b/src/modules/mod/commands/automod/strike-config.ts index 43e50bf7..d2182a29 100644 --- a/src/modules/mod/commands/automod/strike-config.ts +++ b/src/modules/mod/commands/automod/strike-config.ts @@ -88,6 +88,6 @@ export default class extends Command { } this.client.cache.strikes.flush(guild.id); - this.sendReply(message, embed); + await this.sendReply(message, embed); } } diff --git a/src/modules/mod/commands/info/caseDelete.ts b/src/modules/mod/commands/info/caseDelete.ts index 6a291eae..005ab408 100644 --- a/src/modules/mod/commands/info/caseDelete.ts +++ b/src/modules/mod/commands/info/caseDelete.ts @@ -61,6 +61,6 @@ export default class extends Command { embed.description = t('cmd.caseDelete.notFound'); } - this.sendReply(message, embed); + await this.sendReply(message, embed); } } diff --git a/src/modules/mod/commands/info/caseView.ts b/src/modules/mod/commands/info/caseView.ts index 55b28cc8..a251a09d 100644 --- a/src/modules/mod/commands/info/caseView.ts +++ b/src/modules/mod/commands/info/caseView.ts @@ -89,6 +89,6 @@ export default class extends Command { embed.description = t('cmd.caseView.notFound'); } - this.sendReply(message, embed); + await this.sendReply(message, embed); } } diff --git a/src/modules/mod/commands/info/check.ts b/src/modules/mod/commands/info/check.ts index 8ed4a007..6e31d5e2 100644 --- a/src/modules/mod/commands/info/check.ts +++ b/src/modules/mod/commands/info/check.ts @@ -112,6 +112,6 @@ export default class extends Command { embed.description = t('cmd.check.noHistory'); } - this.sendReply(message, embed); + await this.sendReply(message, embed); } } diff --git a/src/modules/mod/commands/mod/ban.ts b/src/modules/mod/commands/mod/ban.ts index a7d590aa..b94dc1ea 100644 --- a/src/modules/mod/commands/mod/ban.ts +++ b/src/modules/mod/commands/mod/ban.ts @@ -101,7 +101,7 @@ export default class extends Command { creatorId: message.author.id }); - this.client.mod.logPunishmentModAction( + await this.client.mod.logPunishmentModAction( guild, targetUser, punishment.type, diff --git a/src/modules/mod/commands/mod/kick.ts b/src/modules/mod/commands/mod/kick.ts index 3bd8d2e8..b12d1a00 100644 --- a/src/modules/mod/commands/mod/kick.ts +++ b/src/modules/mod/commands/mod/kick.ts @@ -77,7 +77,7 @@ export default class extends Command { creatorId: message.author.id }); - this.client.mod.logPunishmentModAction( + await this.client.mod.logPunishmentModAction( guild, targetMember.user, punishment.type, diff --git a/src/modules/mod/commands/mod/mute.ts b/src/modules/mod/commands/mod/mute.ts index 98853b3a..d9658224 100644 --- a/src/modules/mod/commands/mod/mute.ts +++ b/src/modules/mod/commands/mod/mute.ts @@ -89,7 +89,7 @@ export default class extends Command { creatorId: message.author.id }); - this.client.mod.logPunishmentModAction( + await this.client.mod.logPunishmentModAction( guild, targetMember.user, punishment.type, @@ -99,7 +99,7 @@ export default class extends Command { ); if (duration) { - this.client.scheduler.addScheduledAction( + await this.client.scheduler.addScheduledAction( guild.id, ScheduledActionType.unmute, { memberId: targetMember.id }, diff --git a/src/modules/mod/commands/mod/softban.ts b/src/modules/mod/commands/mod/softban.ts index f035362d..b69bd92b 100644 --- a/src/modules/mod/commands/mod/softban.ts +++ b/src/modules/mod/commands/mod/softban.ts @@ -91,7 +91,7 @@ export default class extends Command { creatorId: message.author.id }); - this.client.mod.logPunishmentModAction( + await this.client.mod.logPunishmentModAction( guild, targetMember.user, punishment.type, diff --git a/src/modules/mod/commands/mod/strike.ts b/src/modules/mod/commands/mod/strike.ts index 4141d83a..21ecc4c8 100644 --- a/src/modules/mod/commands/mod/strike.ts +++ b/src/modules/mod/commands/mod/strike.ts @@ -47,9 +47,13 @@ export default class extends Command { const source = `${message.author.username}#${message.author.discriminator} ` + `(ID: ${message.author.id})`; - this.client.mod.logViolationModAction(guild, member.user, type, amount, [ - { name: 'Issued by', value: source } - ]); + await this.client.mod.logViolationModAction( + guild, + member.user, + type, + amount, + [{ name: 'Issued by', value: source }] + ); await this.client.mod.addStrikesAndPunish(member, type, amount, { guild, diff --git a/src/modules/mod/commands/mod/unban.ts b/src/modules/mod/commands/mod/unban.ts index f0f9f764..cd5d7fdf 100644 --- a/src/modules/mod/commands/mod/unban.ts +++ b/src/modules/mod/commands/mod/unban.ts @@ -60,7 +60,7 @@ export default class extends Command { name: 'Reason', value: reason }); - this.client.logModAction(guild, logEmbed); + await this.client.logModAction(guild, logEmbed); embed.description = t('cmd.unban.done'); } diff --git a/src/modules/mod/commands/mod/unmute.ts b/src/modules/mod/commands/mod/unmute.ts index 4e813638..da05913d 100644 --- a/src/modules/mod/commands/mod/unmute.ts +++ b/src/modules/mod/commands/mod/unmute.ts @@ -54,7 +54,7 @@ export default class extends Command { name: 'Mod', value: `<@${message.author.id}>` }); - this.client.logModAction(guild, logEmbed); + await this.client.logModAction(guild, logEmbed); embed.description = t('cmd.unmute.done'); } diff --git a/src/modules/mod/commands/mod/warn.ts b/src/modules/mod/commands/mod/warn.ts index dc5404c8..ecc6c8f1 100644 --- a/src/modules/mod/commands/mod/warn.ts +++ b/src/modules/mod/commands/mod/warn.ts @@ -67,7 +67,7 @@ export default class extends Command { creatorId: message.author.id }); - this.client.mod.logPunishmentModAction( + await this.client.mod.logPunishmentModAction( guild, targetMember.user, punishment.type, diff --git a/src/modules/mod/services/Moderation.ts b/src/modules/mod/services/Moderation.ts index 9c34b664..868ff273 100644 --- a/src/modules/mod/services/Moderation.ts +++ b/src/modules/mod/services/Moderation.ts @@ -101,11 +101,11 @@ export class ModerationService { } private async onGuildMemberAdd(guild: Guild, member: Member) { - this.hoist(guild, member); + await this.hoist(guild, member); } private async onGuildMemberUpdate(guild: Guild, member: Member) { - this.hoist(guild, member); + await this.hoist(guild, member); } private async onMessage(message: Message) { @@ -218,9 +218,9 @@ export class ModerationService { continue; } - message.delete(); + await message.delete(); - this.logViolationModAction( + await this.logViolationModAction( guild, message.author, strike.type, @@ -237,8 +237,8 @@ export class ModerationService { embed.description = `Message by ${usr} was removed because it violated the ${viol} rule.\n`; embed.description += `\n\nUser got ${strike.amount} strikes.`; - this.sendReplyAndDelete(message, embed, settings); - this.addStrikesAndPunish(member, strike.type, strike.amount, { + await this.sendReplyAndDelete(message, embed, settings); + await this.addStrikesAndPunish(member, strike.type, strike.amount, { guild, settings }); @@ -258,7 +258,7 @@ export class ModerationService { message.delete().catch(() => undefined); - this.logViolationModAction(guild, message.author, violation, 0, [ + await this.logViolationModAction(guild, message.author, violation, 0, [ { name: 'Channel', value: channel.name }, { name: 'Message', value: message.content } ]); @@ -267,7 +267,7 @@ export class ModerationService { const usr = `<@${message.author.id}>`; embed.description = `Message by ${usr} was removed because it violated the \`${violation}\` rule.\n`; - this.sendReplyAndDelete(message, embed, settings); + await this.sendReplyAndDelete(message, embed, settings); return; } } @@ -283,7 +283,7 @@ export class ModerationService { return embed; } - public logViolationModAction( + public async logViolationModAction( guild: Guild, user: BasicUser, type: ViolationType, @@ -307,10 +307,10 @@ export class ModerationService { if (extra) { extra.filter(e => !!e.value).forEach(e => logEmbed.fields.push(e)); } - this.client.logModAction(guild, logEmbed); + await this.client.logModAction(guild, logEmbed); } - public logPunishmentModAction( + public async logPunishmentModAction( guild: Guild, user: BasicUser, type: PunishmentType, @@ -329,7 +329,7 @@ export class ModerationService { if (extra) { extra.forEach(e => logEmbed.fields.push(e)); } - this.client.logModAction(guild, logEmbed); + await this.client.logModAction(guild, logEmbed); } private getMiniMessage(message: Message): MiniMessage { @@ -413,7 +413,7 @@ export class ModerationService { creatorId: null }); - this.logPunishmentModAction( + await this.logPunishmentModAction( args.guild, member.user, punishmentConfig.type, @@ -696,12 +696,12 @@ export class ModerationService { const newName = (NAME_DEHOIST_PREFIX + ' ' + name).substr(0, 32); member.edit({ nick: newName }, 'Auto dehoist').catch(() => undefined); - this.logViolationModAction(guild, member.user, type, amount, [ + await this.logViolationModAction(guild, member.user, type, amount, [ { name: 'New name', value: newName }, { name: 'Previous name', value: name } ]); - this.addStrikesAndPunish(member, type, amount, { + await this.addStrikesAndPunish(member, type, amount, { guild, settings }); @@ -801,7 +801,7 @@ export class ModerationService { } ); - return dmChannel.createMessage(message).catch(() => { + return dmChannel.createMessage(message).catch(async () => { if (settings.modLogChannel) { const channel = member.guild.channels.get(settings.modLogChannel); if (channel && channel instanceof TextChannel) { @@ -820,7 +820,7 @@ export class ModerationService { } ] }); - channel.createMessage({ embed }); + await channel.createMessage({ embed }); } } }); @@ -851,7 +851,7 @@ export class ModerationService { ); } - return dmChannel.createMessage(message).catch(() => { + return dmChannel.createMessage(message).catch(async () => { if (settings.modLogChannel) { const channel = member.guild.channels.get(settings.modLogChannel); if (channel && channel instanceof TextChannel) { @@ -870,7 +870,7 @@ export class ModerationService { } ] }); - channel.createMessage({ embed }); + await channel.createMessage({ embed }); } } }); diff --git a/src/modules/music/commands/music/disconnect.ts b/src/modules/music/commands/music/disconnect.ts index cc65b23a..6cd0131d 100644 --- a/src/modules/music/commands/music/disconnect.ts +++ b/src/modules/music/commands/music/disconnect.ts @@ -24,10 +24,10 @@ export default class extends Command { ): Promise { const conn = await this.client.music.getMusicConnection(guild); if (!conn.isConnected()) { - this.sendReply(message, t('music.notConnected')); + await this.sendReply(message, t('music.notConnected')); return; } - conn.disconnect(); + await conn.disconnect(); } } diff --git a/src/modules/music/commands/music/lyrics.ts b/src/modules/music/commands/music/lyrics.ts index 44b4a61e..650eb5fd 100644 --- a/src/modules/music/commands/music/lyrics.ts +++ b/src/modules/music/commands/music/lyrics.ts @@ -5,7 +5,6 @@ import { Command, Context } from '../../../../framework/commands/Command'; import { BooleanResolver } from '../../../../framework/resolvers'; import { CommandGroup, MusicCommand } from '../../../../types'; import { MusicConnection } from '../../models/MusicConnection'; -import { MusicPlatform } from '../../models/MusicPlatform'; export default class extends Command { public constructor(client: IMClient) { @@ -35,14 +34,14 @@ export default class extends Command { ): Promise { const conn = await this.client.music.getMusicConnection(guild); if (!conn.isPlaying()) { - this.sendReply(message, t('music.notPlaying')); + await this.sendReply(message, t('music.notPlaying')); return; } const musicPlatform = conn.getNowPlaying().getPlatform(); if (!musicPlatform.supportsLyrics) { - this.sendReply( + await this.sendReply( message, t('cmd.lyrics.notSupported', { platform: musicPlatform.getType() @@ -55,12 +54,12 @@ export default class extends Command { const lyrics = await this.client.music.getLyrics(item); if (lyrics.length === 0) { - this.sendReply(message, t('cmd.lyrics.notFound')); + await this.sendReply(message, t('cmd.lyrics.notFound')); return; } if (!live) { - this.sendReply( + await this.sendReply( message, lyrics .map(l => `${this.client.music.formatTime(l.start)}: ${l.text}`) @@ -75,10 +74,10 @@ export default class extends Command { ); const msg = await this.sendReply(message, 'Loading...'); - this.scheduleNext(msg, conn, lyrics, index); + await this.scheduleNext(msg, conn, lyrics, index); } - private scheduleNext( + private async scheduleNext( msg: Message, conn: MusicConnection, lyrics: { start: number; dur: number; text: string }[], @@ -104,7 +103,7 @@ export default class extends Command { setTimeout(() => msg.delete(), now.dur * 1000 + 500); } - msg.edit({ + await msg.edit({ embed: this.createEmbed({ description: text }) }); } diff --git a/src/modules/music/commands/music/mashup.ts b/src/modules/music/commands/music/mashup.ts index 6333751c..2dfbab9e 100644 --- a/src/modules/music/commands/music/mashup.ts +++ b/src/modules/music/commands/music/mashup.ts @@ -39,13 +39,13 @@ export default class extends Command { // TODO const voiceChannelId = message.member.voiceState.channelID; if (!voiceChannelId) { - this.sendReply(message, t('music.voiceChannelRequired')); + await this.sendReply(message, t('music.voiceChannelRequired')); return; } const [link1, link2] = videos.split(' '); if (!link2) { - this.sendReply(message, t('cmd.mashup.missingSecondVideo')); + await this.sendReply(message, t('cmd.mashup.missingSecondVideo')); return; } const platform1 = this.client.music.platforms.getForLink(link1); @@ -78,6 +78,6 @@ export default class extends Command { mashupId = await musicPlatform.mix(result1.id, result2.id); } - this.sendReply(message, `RaveDJ Link: https://rave.dj/${mashupId}`); + await this.sendReply(message, `RaveDJ Link: https://rave.dj/${mashupId}`); } } diff --git a/src/modules/music/commands/music/now-playing.ts b/src/modules/music/commands/music/now-playing.ts index 3a6cd771..c1c412e7 100644 --- a/src/modules/music/commands/music/now-playing.ts +++ b/src/modules/music/commands/music/now-playing.ts @@ -85,7 +85,7 @@ export default class extends Command { ? PREMIUM_PIN_UPDATE_INTERVAL : PIN_UPDATE_INTERVAL; - func(); + await func(); this.timerMap.set(guild.id, setInterval(func, interval)); } } diff --git a/src/modules/music/commands/music/pause.ts b/src/modules/music/commands/music/pause.ts index e4682291..c02f5f75 100644 --- a/src/modules/music/commands/music/pause.ts +++ b/src/modules/music/commands/music/pause.ts @@ -24,7 +24,7 @@ export default class extends Command { ): Promise { const conn = await this.client.music.getMusicConnection(guild); if (!conn.isConnected()) { - this.sendReply(message, t('music.notConnected')); + await this.sendReply(message, t('music.notConnected')); return; } diff --git a/src/modules/music/commands/music/play.ts b/src/modules/music/commands/music/play.ts index cb6cc758..ae811eab 100644 --- a/src/modules/music/commands/music/play.ts +++ b/src/modules/music/commands/music/play.ts @@ -55,7 +55,7 @@ export default class extends Command { ): Promise { const voiceChannelId = message.member.voiceState.channelID; if (!voiceChannelId) { - this.sendReply(message, t('music.voiceChannelRequired')); + await this.sendReply(message, t('music.voiceChannelRequired')); return; } @@ -92,9 +92,9 @@ export default class extends Command { item.setAuthor(message.author); const voiceChannel = guild.channels.get(voiceChannelId) as VoiceChannel; - conn.play(item, voiceChannel, next); + await conn.play(item, voiceChannel, next); - this.sendEmbed( + await this.sendEmbed( message.channel, this.client.music.createPlayingEmbed(item) ); diff --git a/src/modules/music/commands/music/repeat.ts b/src/modules/music/commands/music/repeat.ts index b694fb09..4cbd47f0 100644 --- a/src/modules/music/commands/music/repeat.ts +++ b/src/modules/music/commands/music/repeat.ts @@ -24,16 +24,16 @@ export default class extends Command { ): Promise { const conn = await this.client.music.getMusicConnection(guild); if (!conn.isConnected()) { - this.sendReply(message, t('music.notConnected')); + await this.sendReply(message, t('music.notConnected')); return; } conn.setRepeat(!conn.isRepeating()); if (conn.isRepeating()) { - this.sendReply(message, t('cmd.repeat.enabled')); + await this.sendReply(message, t('cmd.repeat.enabled')); } else { - this.sendReply(message, t('cmd.repeat.disabled')); + await this.sendReply(message, t('cmd.repeat.disabled')); } } } diff --git a/src/modules/music/commands/music/resume.ts b/src/modules/music/commands/music/resume.ts index 31c45b06..7f479b4d 100644 --- a/src/modules/music/commands/music/resume.ts +++ b/src/modules/music/commands/music/resume.ts @@ -24,7 +24,7 @@ export default class extends Command { ): Promise { const conn = await this.client.music.getMusicConnection(guild); if (!conn.isConnected()) { - this.sendReply(message, t('music.notConnected')); + await this.sendReply(message, t('music.notConnected')); return; } diff --git a/src/modules/music/commands/music/rewind.ts b/src/modules/music/commands/music/rewind.ts index 2abfff59..413a5c8a 100644 --- a/src/modules/music/commands/music/rewind.ts +++ b/src/modules/music/commands/music/rewind.ts @@ -25,14 +25,14 @@ export default class extends Command { ): Promise { const conn = await this.client.music.getMusicConnection(guild); if (!conn.isPlaying()) { - this.sendReply(message, t('music.notPlaying')); + await this.sendReply(message, t('music.notPlaying')); return; } const musicPlatform: MusicPlatform = conn.getNowPlaying().getPlatform(); if (!musicPlatform.supportsRewind) { - this.sendReply( + await this.sendReply( message, t('cmd.rewind.notSupported', { platform: musicPlatform.getType() @@ -41,6 +41,6 @@ export default class extends Command { return; } - conn.rewind(); + await conn.rewind(); } } diff --git a/src/modules/music/commands/music/search.ts b/src/modules/music/commands/music/search.ts index 78f77035..29935e87 100644 --- a/src/modules/music/commands/music/search.ts +++ b/src/modules/music/commands/music/search.ts @@ -45,7 +45,7 @@ export default class extends Command { ): Promise { const voiceChannelId = message.member.voiceState.channelID; if (!voiceChannelId) { - this.sendReply(message, t('music.voiceChannelRequired')); + await this.sendReply(message, t('music.voiceChannelRequired')); return; } @@ -59,7 +59,7 @@ export default class extends Command { } if (!musicPlatform.supportsSearch) { - this.sendReply( + await this.sendReply( message, t('cmd.search.notSupported', { platform: musicPlatform.getType() @@ -71,7 +71,7 @@ export default class extends Command { const items = await musicPlatform.search(searchTerm); if (items.length === 0) { - this.sendReply(message, t('cmd.search.noResults')); + await this.sendReply(message, t('cmd.search.noResults')); return; } @@ -105,9 +105,9 @@ export default class extends Command { const voiceChannel = guild.channels.get(voiceChannelId) as VoiceChannel; - conn.play(musicItem, voiceChannel); + await conn.play(musicItem, voiceChannel); - this.sendEmbed( + await this.sendEmbed( message.channel, this.client.music.createPlayingEmbed(musicItem) ); diff --git a/src/modules/music/commands/music/seek.ts b/src/modules/music/commands/music/seek.ts index 8d742589..66abeaca 100644 --- a/src/modules/music/commands/music/seek.ts +++ b/src/modules/music/commands/music/seek.ts @@ -33,14 +33,14 @@ export default class extends Command { ): Promise { const conn = await this.client.music.getMusicConnection(guild); if (!conn.isPlaying()) { - this.sendReply(message, t('music.notPlaying')); + await this.sendReply(message, t('music.notPlaying')); return; } const musicPlatform: MusicPlatform = conn.getNowPlaying().getPlatform(); if (!musicPlatform.supportsSeek) { - this.sendReply( + await this.sendReply( message, t('cmd.seek.notSupported', { platform: musicPlatform.getType() @@ -49,6 +49,6 @@ export default class extends Command { return; } - conn.seek(duration); + await conn.seek(duration); } } diff --git a/src/modules/music/commands/music/skip.ts b/src/modules/music/commands/music/skip.ts index 81b9f67b..0f446867 100644 --- a/src/modules/music/commands/music/skip.ts +++ b/src/modules/music/commands/music/skip.ts @@ -32,10 +32,10 @@ export default class extends Command { ): Promise { const conn = await this.client.music.getMusicConnection(guild); if (!conn.isPlaying()) { - this.sendReply(message, t('music.notPlaying')); + await this.sendReply(message, t('music.notPlaying')); return; } - conn.skip(amount || 0); + await conn.skip(amount || 0); } } diff --git a/src/modules/music/commands/music/volume.ts b/src/modules/music/commands/music/volume.ts index 1e352058..abb5b859 100644 --- a/src/modules/music/commands/music/volume.ts +++ b/src/modules/music/commands/music/volume.ts @@ -31,15 +31,15 @@ export default class extends Command { ): Promise { const conn = await this.client.music.getMusicConnection(guild); if (!conn.isPlaying()) { - this.sendReply(message, t('music.notPlaying')); + await this.sendReply(message, t('music.notPlaying')); return; } if (volume) { conn.setVolume(volume); - this.sendReply(message, `Changed volume to ${volume}`); + await this.sendReply(message, `Changed volume to ${volume}`); } else { - this.sendReply(message, `Volume is set to ${conn.getVolume()}`); + await this.sendReply(message, `Volume is set to ${conn.getVolume()}`); } } } diff --git a/src/modules/music/models/MusicConnection.ts b/src/modules/music/models/MusicConnection.ts index 3aff717d..e0c66100 100644 --- a/src/modules/music/models/MusicConnection.ts +++ b/src/modules/music/models/MusicConnection.ts @@ -90,7 +90,7 @@ export class MusicConnection { } if (!this.isPlaying()) { - this.playNext(); + await this.playNext(); } } @@ -107,11 +107,11 @@ export class MusicConnection { } public async rewind() { - this.seek(0); + await this.seek(0); } public async skip(amount: number = 1) { - this.playNext(amount - 1); + await this.playNext(amount - 1); } public isRepeating() { @@ -168,11 +168,11 @@ export class MusicConnection { this.player.on('reconnect', () => { console.error(`Reconnected lavalink player for guild ${this.guild.id}`); }); - this.player.on('disconnect', () => { + this.player.on('disconnect', async () => { console.error(`Player disconnected for guild ${this.guild.id}`); this.player = null; this.voiceChannel.leave(); - this.service.removeConnection(this.guild); + await this.service.removeConnection(this.guild); }); } } @@ -210,7 +210,7 @@ export class MusicConnection { } } - private onStreamEnd(data: any) { + private async onStreamEnd(data: any) { console.log(data); if (data.reason && data.reason === 'REPLACED') { @@ -229,7 +229,7 @@ export class MusicConnection { } if (this.doPlayNext) { - this.playNext(); + await this.playNext(); } } @@ -287,7 +287,7 @@ export class MusicConnection { if (tracks.length === 0) { this.preparingNext = false; - this.playNext(); + await this.playNext(); return; } @@ -310,10 +310,10 @@ export class MusicConnection { this.player.seek(offsetSeconds * 1000); } - public disconnect() { + public async disconnect() { this.player.stop(); this.voiceChannel.leave(); this.player = null; - this.service.removeConnection(this.guild); + await this.service.removeConnection(this.guild); } } diff --git a/src/modules/music/models/ravedj/RaveDJ.ts b/src/modules/music/models/ravedj/RaveDJ.ts index e8168a82..fc89c01d 100644 --- a/src/modules/music/models/ravedj/RaveDJ.ts +++ b/src/modules/music/models/ravedj/RaveDJ.ts @@ -20,7 +20,8 @@ export class RaveDJ extends MusicPlatform { public constructor(client: IMClient) { super(client); - this.getIdToken(); + // TODO: Deactivate service if not available + this.getIdToken().catch(() => undefined); } public isPlatformUrl(url: string): boolean { diff --git a/src/sequelize.ts b/src/sequelize.ts index e7e68b7f..a8c43ad9 100644 --- a/src/sequelize.ts +++ b/src/sequelize.ts @@ -6,6 +6,7 @@ import { MemberSettingsObject, SettingsObject } from './settings'; +import { BotType } from './types'; const config = require('../config.json'); @@ -781,6 +782,7 @@ export interface CommandUsageAttributes extends BaseAttributes { memberId: string; command: string; args: string; + errored: boolean; time: number; } export interface CommandUsageInstance @@ -799,6 +801,7 @@ export const commandUsage = sequelize.define< id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, command: Sequelize.STRING, args: Sequelize.TEXT, + errored: Sequelize.BOOLEAN, time: Sequelize.FLOAT, guildId: Sequelize.STRING(32), memberId: Sequelize.STRING(32) @@ -1302,6 +1305,7 @@ export const musicNodes = sequelize.define< // Servers // ------------------------------------ export interface ServerAttributes extends BaseAttributes { + id: number; name: string; cpu: number; ram: number; @@ -1314,10 +1318,14 @@ export interface ServerInstance export const servers = sequelize.define( 'servers', { - name: { type: Sequelize.STRING(255), primaryKey: true }, + id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, + name: Sequelize.STRING(255), cpu: Sequelize.DOUBLE, ram: Sequelize.DOUBLE, fs: Sequelize.DOUBLE + }, + { + indexes: [{ fields: ['name'], unique: true }] } ); @@ -1326,7 +1334,7 @@ export const servers = sequelize.define( // ------------------------------------ export interface ServerStatAttributes extends BaseAttributes { id: number; - serverName: string; + serverId: number; cpu: number; ram: number; fs: number; @@ -1340,7 +1348,7 @@ export const serverStats = sequelize.define< ServerStatAttributes >('serverStats', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, - serverName: Sequelize.STRING(255), + serverId: Sequelize.INTEGER, cpu: Sequelize.DOUBLE, ram: Sequelize.DOUBLE, fs: Sequelize.DOUBLE @@ -1349,38 +1357,76 @@ export const serverStats = sequelize.define< serverStats.belongsTo(servers); servers.hasMany(serverStats); +// ------------------------------------ +// Instances +// ------------------------------------ +export interface InstanceAttributes extends BaseAttributes { + id: number; + type: BotType; + name: string; + shardCount: number; +} +export interface InstanceInstance + extends Sequelize.Instance, + InstanceAttributes {} + +export const instances = sequelize.define( + 'instances', + { + id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, + type: Sequelize.ENUM(Object.values(BotType)), + name: Sequelize.STRING(255), + shardCount: Sequelize.INTEGER + }, + { + indexes: [{ fields: ['name'], unique: true }] + } +); + // ------------------------------------ // Shards // ------------------------------------ export interface ShardAttributes extends BaseAttributes { - id: string; - serverName: string; + id: number; + instanceId: number; + shardId: number; + serverId: number; version: string; revision: string; } export interface ShardInstance extends Sequelize.Instance, - ShardAttributes {} + ShardAttributes { + getInstance: Sequelize.BelongsToGetAssociationMixin; +} export const shards = sequelize.define( 'shards', { - id: { type: Sequelize.STRING(255), primaryKey: true }, - serverName: Sequelize.STRING(255), + id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, + instanceId: Sequelize.INTEGER, + shardId: Sequelize.INTEGER, + serverId: Sequelize.INTEGER, version: Sequelize.STRING(255), revision: Sequelize.STRING(255) + }, + { + indexes: [{ fields: ['instanceId', 'shardId'], unique: true }] } ); shards.belongsTo(servers); servers.hasMany(shards); +shards.belongsTo(instances); +instances.hasMany(shards); + // ------------------------------------ // ShardStats // ------------------------------------ export interface ShardStatAttributes extends BaseAttributes { id: number; - shardId: string; + shardId: number; status: string; cpu: number; @@ -1394,14 +1440,16 @@ export interface ShardStatAttributes extends BaseAttributes { } export interface ShardStatInstance extends Sequelize.Instance, - ShardStatAttributes {} + ShardStatAttributes { + getShard: Sequelize.BelongsToGetAssociationMixin; +} export const shardStats = sequelize.define< ShardStatInstance, ShardStatAttributes >('shardStats', { id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, - shardId: Sequelize.STRING(255), + shardId: Sequelize.INTEGER, status: Sequelize.STRING(255), cpu: Sequelize.DOUBLE, ram: Sequelize.DOUBLE, @@ -1414,3 +1462,29 @@ export const shardStats = sequelize.define< shardStats.belongsTo(shards); shards.hasMany(shardStats); + +// ------------------------------------ +// Incidents +// ------------------------------------ +export interface IncidentAttributes extends BaseAttributes { + id: number; + guildId: string; + error: string; + details: any; +} +export interface IncidentInstance + extends Sequelize.Instance, + IncidentAttributes {} + +export const incidents = sequelize.define( + 'incidents', + { + id: { type: Sequelize.INTEGER, primaryKey: true, autoIncrement: true }, + guildId: Sequelize.STRING(32), + error: Sequelize.TEXT, + details: Sequelize.JSON + } +); + +incidents.belongsTo(guilds); +guilds.hasMany(incidents); diff --git a/tslint.json b/tslint.json index f5a6ff71..1a724fdb 100644 --- a/tslint.json +++ b/tslint.json @@ -34,7 +34,7 @@ "no-duplicate-variable": true, "no-empty": true, "no-eval": true, - "no-floating-promises": false, + "no-floating-promises": true, "no-inferrable-types": [false], "no-parameter-properties": true, "no-shadowed-variable": true,