diff --git a/src/data/DefaultLogMessages.json b/src/data/DefaultLogMessages.json index 43e386e8..52c90d51 100644 --- a/src/data/DefaultLogMessages.json +++ b/src/data/DefaultLogMessages.json @@ -1,16 +1,16 @@ { - "MEMBER_WARN": "⚠️ **{member.user.username}#{member.user.discriminator}** (`{member.id}`) was warned by {mod.user.username}#{mod.user.discriminator}", - "MEMBER_MUTE": "🔇 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) was muted by {mod.user.username}#{mod.user.discriminator}", - "MEMBER_UNMUTE": "🔉 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) was unmuted by {mod.user.username}#{mod.user.discriminator}", + "MEMBER_WARN": "⚠️ **{member.user.username}#{member.user.discriminator}** (`{member.id}`) was warned by {mod.username}#{mod.discriminator}", + "MEMBER_MUTE": "🔇 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) was muted by {mod.username}#{mod.discriminator}", + "MEMBER_UNMUTE": "🔉 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) was unmuted by {mod.username}#{mod.discriminator}", "MEMBER_MUTE_EXPIRED": "🔉 **{member.user.username}#{member.user.discriminator}**'s mute expired", - "MEMBER_KICK": "👢 **{user.username}#{user.discriminator}** (`{user.id}`) was kicked by {mod.user.username}#{mod.user.discriminator}", - "MEMBER_BAN": "🔨 **{user.username}#{user.discriminator}** (`{user.id}`) was banned by {mod.user.username}#{mod.user.discriminator}", - "MEMBER_UNBAN": "🔓 **{user.username}#{user.discriminator}** (`{user.id}`) was unbanned by {mod.user.username}#{mod.user.discriminator}", - "MEMBER_FORCEBAN": "🔨 User `{userId}` was forcebanned by {mod.user.username}#{mod.user.discriminator}", + "MEMBER_KICK": "👢 **{user.username}#{user.discriminator}** (`{user.id}`) was kicked by {mod.username}#{mod.discriminator}", + "MEMBER_BAN": "🔨 **{user.username}#{user.discriminator}** (`{user.id}`) was banned by {mod.username}#{mod.discriminator}", + "MEMBER_UNBAN": "🔓 **{user.username}#{user.discriminator}** (`{user.id}`) was unbanned by {mod.username}#{mod.discriminator}", + "MEMBER_FORCEBAN": "🔨 User `{userId}` was forcebanned by {mod.username}#{mod.discriminator}", "MEMBER_JOIN": "📥 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) joined{new} (created {account_age} ago)", "MEMBER_LEAVE": "📤 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) left the server", - "MEMBER_ROLE_ADD": "🔑 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) role added **{role.name}** by {mod.user.username}#{mod.user.discriminator}", - "MEMBER_ROLE_REMOVE": "🔑 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) role removed **{role.name}** by {mod.user.username}#{mod.user.discriminator}", + "MEMBER_ROLE_ADD": "🔑 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) role added **{role.name}** by {mod.username}#{mod.discriminator}", + "MEMBER_ROLE_REMOVE": "🔑 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) role removed **{role.name}** by {mod.username}#{mod.discriminator}", "MEMBER_NICK_CHANGE": "✏ **{member.user.username}#{member.user.discriminator}** (`{member.id}`) changed their nickname from **{oldNick}** to **{newNick}**", "MEMBER_USERNAME_CHANGE": "✏ **{member.user.username}#{member.user.discriminator}** (`{member.id}`) changed their username from **{oldName}** to **{newName}**", "MEMBER_ROLES_RESTORE": "💿 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) roles were restored", @@ -28,8 +28,8 @@ "MESSAGE_DELETE_BULK": "🗑 **{count}** messages deleted in **{channel.name}**", "VOICE_CHANNEL_JOIN": "🔸 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) joined **{channel.name}**", - "VOICE_CHANNEL_MOVE": "🔸 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) moved **{oldChannel.name}** ➞ **{newChannel.name}**", - "VOICE_CHANNEL_LEAVE": "🔸 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) left **{channel.name}**", + "VOICE_CHANNEL_MOVE": "🔹 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) moved **{oldChannel.name}** ➞ **{newChannel.name}**", + "VOICE_CHANNEL_LEAVE": "♦ **{member.user.username}#{member.user.discriminator}** (`{member.id}`) left **{channel.name}**", "COMMAND": "🤖 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) used command in **{channel.name}**:\n`{command}`", diff --git a/src/plugins/Logs.ts b/src/plugins/Logs.ts index 3e801741..974b78cb 100644 --- a/src/plugins/Logs.ts +++ b/src/plugins/Logs.ts @@ -26,13 +26,10 @@ interface ILogChannelMap { [channelId: string]: ILogChannel; } -const unknownMember = { +const unknownUser = { id: 0, - user: { - id: 0, - username: "Unknown", - discriminator: "0000" - } + username: "Unknown", + discriminator: "0000" }; export class LogsPlugin extends Plugin { @@ -62,7 +59,7 @@ export class LogsPlugin extends Plugin { this.serverLogs.removeListener("log", this.logListener); } - log(type, data) { + async log(type, data) { const logChannels: ILogChannelMap = this.configValue("channels"); for (const [channelId, opts] of Object.entries(logChannels)) { const channel = this.guild.channels.get(channelId); @@ -73,7 +70,7 @@ export class LogsPlugin extends Plugin { (opts.exclude && !opts.exclude.includes(type)) ) { const message = this.getLogMessage(type, data); - if (message) channel.createMessage(message); + if (message) await channel.createMessage(message); } } } @@ -118,47 +115,35 @@ export class LogsPlugin extends Plugin { @d.event("guildBanAdd") async onMemberBan(_, user) { const relevantAuditLogEntry = await findRelevantAuditLogEntry( - this.bot, + this.guild, ErisConstants.AuditLogActions.MEMBER_BAN_ADD, user.id ); + const mod = relevantAuditLogEntry ? relevantAuditLogEntry.user : unknownUser; - if (relevantAuditLogEntry) { - this.log(LogType.MEMBER_BAN, { - user: stripObjectToScalars(user), - mod: relevantAuditLogEntry.member - }); - } else { - this.log(LogType.MEMBER_BAN, { - user: stripObjectToScalars(user), - mod: unknownMember - }); - } + this.log(LogType.MEMBER_BAN, { + user: stripObjectToScalars(user), + mod: stripObjectToScalars(mod) + }); } @d.event("guildBanRemove") async onMemberUnban(_, user) { const relevantAuditLogEntry = await findRelevantAuditLogEntry( - this.bot, + this.guild, ErisConstants.AuditLogActions.MEMBER_BAN_REMOVE, user.id ); + const mod = relevantAuditLogEntry ? relevantAuditLogEntry.user : unknownUser; - if (relevantAuditLogEntry) { - this.log(LogType.MEMBER_UNBAN, { - user: stripObjectToScalars(user), - mod: relevantAuditLogEntry.member - }); - } else { - this.log(LogType.MEMBER_UNBAN, { - user: stripObjectToScalars(user), - mod: unknownMember - }); - } + this.log(LogType.MEMBER_UNBAN, { + user: stripObjectToScalars(user), + mod: stripObjectToScalars(mod) + }); } @d.event("guildMemberUpdate") - onMemberUpdate(_, member: Member, oldMember: Member) { + async onMemberUpdate(_, member: Member, oldMember: Member) { if (!oldMember) return; if (member.nick !== oldMember.nick) { @@ -170,18 +155,27 @@ export class LogsPlugin extends Plugin { } if (!isEqual(oldMember.roles, member.roles)) { - const addedRoles = diff(oldMember.roles, member.roles); - const removedRoles = diff(member.roles, oldMember.roles); + const addedRoles = diff(member.roles, oldMember.roles); + const removedRoles = diff(oldMember.roles, member.roles); + + const relevantAuditLogEntry = await findRelevantAuditLogEntry( + this.guild, + ErisConstants.AuditLogActions.MEMBER_ROLE_UPDATE, + member.id + ); + const mod = relevantAuditLogEntry ? relevantAuditLogEntry.user : unknownUser; if (addedRoles.length) { this.log(LogType.MEMBER_ROLE_ADD, { member, - role: this.guild.roles.get(addedRoles[0]) + role: this.guild.roles.get(addedRoles[0]), + mod: stripObjectToScalars(mod) }); } else if (removedRoles.length) { this.log(LogType.MEMBER_ROLE_REMOVE, { member, - role: this.guild.roles.get(removedRoles[0]) + role: this.guild.roles.get(removedRoles[0]), + mod: stripObjectToScalars(mod) }); } } @@ -216,14 +210,14 @@ export class LogsPlugin extends Plugin { } @d.event("guildRoleCreate") - onRoleCreate(role) { + onRoleCreate(_, role) { this.log(LogType.ROLE_CREATE, { role: stripObjectToScalars(role) }); } @d.event("guildRoleDelete") - onRoleDelete(role) { + onRoleDelete(_, role) { this.log(LogType.ROLE_DELETE, { role: stripObjectToScalars(role) }); @@ -236,8 +230,8 @@ export class LogsPlugin extends Plugin { this.log(LogType.MESSAGE_EDIT, { member: stripObjectToScalars(msg.member, ["user"]), channel: stripObjectToScalars(msg.channel), - before: oldMsg ? oldMsg.cleanContent || "" : "Unavailable due to restart", - after: msg.cleanContent || "" + before: oldMsg ? oldMsg.content || "" : "Unavailable due to restart", + after: msg.content || "" }); } @@ -275,7 +269,7 @@ export class LogsPlugin extends Plugin { } @d.event("voiceChannelSwitch") - onVoiceChannelSwitch(member: Member, oldChannel: Channel, newChannel: Channel) { + onVoiceChannelSwitch(member: Member, newChannel: Channel, oldChannel: Channel) { this.log(LogType.VOICE_CHANNEL_MOVE, { member: stripObjectToScalars(member, ["user"]), oldChannel: stripObjectToScalars(oldChannel), diff --git a/src/plugins/ModActions.ts b/src/plugins/ModActions.ts index 926677ec..b3fc8aaf 100644 --- a/src/plugins/ModActions.ts +++ b/src/plugins/ModActions.ts @@ -1,5 +1,5 @@ -import { Plugin, decorators as d, waitForReaction } from "knub"; -import { Guild, GuildAuditLogEntry, Member, Message, TextChannel, User } from "eris"; +import { decorators as d, Plugin, waitForReaction } from "knub"; +import { Constants as ErisConstants, Guild, Member, Message, TextChannel, User } from "eris"; import moment from "moment-timezone"; import humanizeDuration from "humanize-duration"; import { GuildCases } from "../data/GuildCases"; @@ -8,21 +8,16 @@ import { errorMessage, findRelevantAuditLogEntry, formatTemplateString, + sleep, stripObjectToScalars, successMessage } from "../utils"; import { GuildMutes } from "../data/GuildMutes"; -import Timer = NodeJS.Timer; import Case from "../models/Case"; import { CaseType } from "../data/CaseType"; import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; - -const sleep = (ms: number): Promise => { - return new Promise(resolve => { - setTimeout(resolve, ms); - }); -}; +import Timer = NodeJS.Timer; export class ModActionsPlugin extends Plugin { protected cases: GuildCases; @@ -102,8 +97,8 @@ export class ModActionsPlugin extends Plugin { async onGuildBanAdd(guild: Guild, user: User) { await sleep(1000); // Wait a moment for the audit log to update const relevantAuditLogEntry = await findRelevantAuditLogEntry( - this.bot, - "MEMBER_BAN_ADD", + this.guild, + ErisConstants.AuditLogActions.MEMBER_BAN_ADD, user.id ); @@ -131,8 +126,8 @@ export class ModActionsPlugin extends Plugin { @d.event("guildBanRemove") async onGuildBanRemove(guild: Guild, user: User) { const relevantAuditLogEntry = await findRelevantAuditLogEntry( - this.bot, - "MEMBER_BAN_REMOVE", + this.guild, + ErisConstants.AuditLogActions.MEMBER_BAN_REMOVE, user.id ); @@ -150,7 +145,7 @@ export class ModActionsPlugin extends Plugin { * Show an alert if a member with prior notes joins the server */ @d.event("guildMemberAdd") - async onGuildMemberAdd(member: Member) { + async onGuildMemberAdd(_, member: Member) { if (!this.configValue("alert_on_rejoin")) return; const alertChannelId = this.configValue("alert_channel"); @@ -168,6 +163,30 @@ export class ModActionsPlugin extends Plugin { } } + @d.event("guildMemberRemove") + async onGuildMemberRemove(_, member: Member) { + const kickAuditLogEntry = await findRelevantAuditLogEntry( + this.guild, + ErisConstants.AuditLogActions.MEMBER_KICK, + member.id + ); + + if (kickAuditLogEntry) { + this.createCase( + member.id, + kickAuditLogEntry.user.id, + CaseType.Kick, + kickAuditLogEntry.id, + kickAuditLogEntry.reason, + true + ); + this.serverLogs.log(LogType.MEMBER_KICK, { + user: stripObjectToScalars(member.user), + mod: stripObjectToScalars(kickAuditLogEntry.user) + }); + } + } + /** * Update the specified case by adding more notes/details to it */ @@ -228,7 +247,7 @@ export class ModActionsPlugin extends Plugin { msg.channel.createMessage(successMessage("Member warned")); this.serverLogs.log(LogType.MEMBER_WARN, { - mod: stripObjectToScalars(msg.member, ["user"]), + mod: stripObjectToScalars(msg.member.user), member: stripObjectToScalars(args.member, ["user"]) }); } @@ -298,7 +317,7 @@ export class ModActionsPlugin extends Plugin { // Log the action this.serverLogs.log(LogType.MEMBER_MUTE, { - mod: stripObjectToScalars(msg.member, ["user"]), + mod: stripObjectToScalars(msg.member.user), member: stripObjectToScalars(args.member, ["user"]) }); } @@ -337,7 +356,7 @@ export class ModActionsPlugin extends Plugin { // Log the action this.serverLogs.log(LogType.MEMBER_UNMUTE, { - mod: stripObjectToScalars(msg.member, ["user"]), + mod: stripObjectToScalars(msg.member.user), member: stripObjectToScalars(args.member, ["user"]) }); } @@ -381,7 +400,7 @@ export class ModActionsPlugin extends Plugin { // Log the action this.serverLogs.log(LogType.MEMBER_KICK, { - mod: stripObjectToScalars(msg.member, ["user"]), + mod: stripObjectToScalars(msg.member.user), member: stripObjectToScalars(args.member, ["user"]) }); } @@ -425,7 +444,7 @@ export class ModActionsPlugin extends Plugin { // Log the action this.serverLogs.log(LogType.MEMBER_BAN, { - mod: stripObjectToScalars(msg.member, ["user"]), + mod: stripObjectToScalars(msg.member.user), member: stripObjectToScalars(args.member, ["user"]) }); } @@ -450,7 +469,7 @@ export class ModActionsPlugin extends Plugin { // Log the action this.serverLogs.log(LogType.MEMBER_UNBAN, { - mod: stripObjectToScalars(msg.member, ["user"]), + mod: stripObjectToScalars(msg.member.user), userId: args.userId }); } @@ -484,7 +503,7 @@ export class ModActionsPlugin extends Plugin { // Log the action this.serverLogs.log(LogType.MEMBER_FORCEBAN, { - mod: stripObjectToScalars(msg.member, ["user"]), + mod: stripObjectToScalars(msg.member.user), userId: args.userId }); } @@ -527,7 +546,7 @@ export class ModActionsPlugin extends Plugin { // Log the action msg.channel.createMessage(successMessage("Case created!")); this.serverLogs.log(LogType.CASE_CREATE, { - mod: stripObjectToScalars(msg.member, ["user"]), + mod: stripObjectToScalars(msg.member.user), userId: args.userId, caseNum: theCase.case_number, caseType: type.toUpperCase() diff --git a/src/utils.ts b/src/utils.ts index 315794a5..b1209792 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,5 +1,5 @@ import at = require("lodash.at"); -import { GuildAuditLogEntry } from "eris"; +import { Guild, GuildAuditLogEntry } from "eris"; /** * Turns a "delay string" such as "1h30m" to milliseconds @@ -80,17 +80,23 @@ export function isSnowflake(v: string): boolean { return /^\d{17,20}$/.test(v); } +export function sleep(ms: number): Promise { + return new Promise(resolve => { + setTimeout(resolve, ms); + }); +} + /** * Attempts to find a relevant audit log entry for the given user and action */ export async function findRelevantAuditLogEntry( - bot, + guild: Guild, actionType: number, userId: string, attempts: number = 3, - attemptDelay: number = 1500 + attemptDelay: number = 3000 ): Promise { - const auditLogEntries = await this.bot.getGuildAuditLogs(this.guildId, 5, null, actionType); + const auditLogEntries = await guild.getAuditLogs(5, null, actionType); auditLogEntries.entries.sort((a, b) => { if (a.createdAt > b.createdAt) return -1; @@ -101,13 +107,14 @@ export async function findRelevantAuditLogEntry( const cutoffTS = Date.now() - 1000 * 60 * 2; const relevantEntry = auditLogEntries.entries.find(entry => { - return entry.target.id === userId && entry.createdAt >= cutoffTS; + return entry.targetID === userId && entry.createdAt >= cutoffTS; }); if (relevantEntry) { return relevantEntry; } else if (attempts > 0) { - return findRelevantAuditLogEntry(bot, actionType, userId, attempts - 1, attemptDelay); + await sleep(attemptDelay); + return findRelevantAuditLogEntry(guild, actionType, userId, attempts - 1, attemptDelay); } else { return null; }