feat: add logging for most events
This commit is contained in:
parent
6625e9ffb0
commit
724c30703f
9 changed files with 361 additions and 69 deletions
5
package-lock.json
generated
5
package-lock.json
generated
|
@ -2490,6 +2490,11 @@
|
|||
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
|
||||
"integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw="
|
||||
},
|
||||
"lodash.isequal": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz",
|
||||
"integrity": "sha1-QVxEePK8wwEgwizhDtMib30+GOA="
|
||||
},
|
||||
"lodash.merge": {
|
||||
"version": "4.6.1",
|
||||
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",
|
||||
|
|
|
@ -33,6 +33,8 @@
|
|||
"knex": "^0.14.6",
|
||||
"knub": "^9.4.11",
|
||||
"lodash.at": "^4.6.0",
|
||||
"lodash.difference": "^4.5.0",
|
||||
"lodash.isequal": "^4.5.0",
|
||||
"mariasql": "^0.2.6",
|
||||
"moment-timezone": "^0.5.21",
|
||||
"ts-node": "^3.3.0",
|
||||
|
|
|
@ -3,10 +3,38 @@
|
|||
"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_MUTE_EXPIRED": "🔉 **{member.user.username}#{member.user.discriminator}**'s mute expired",
|
||||
"MEMBER_KICK": "👢 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) was kicked by {mod.user.username}#{mod.user.discriminator}",
|
||||
"MEMBER_BAN": "🔨 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) was banned by {mod.user.username}#{mod.user.discriminator}",
|
||||
"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_JOIN": "📥 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) joined{new} (created {account_age} ago)",
|
||||
"MEMBER_LEAVE": "📤 **{member.user.username}#{member.user.discriminator}** left the server",
|
||||
"MEMBER_ROLE_ADD": "🔑 **{member.user.username}#{member.user.discriminator}** role added **{role.name}** by {mod.user.username}#{mod.user.discriminator}",
|
||||
"MEMBER_ROLE_REMOVE": "🔑 **{member.user.username}#{member.user.discriminator}** role removed **{role.name}** by {mod.user.username}#{mod.user.discriminator}"
|
||||
"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_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",
|
||||
|
||||
"CHANNEL_CREATE": "🖊 Channel **#{channel.name}** was created",
|
||||
"CHANNEL_DELETE": "🗑 Channel **#{channel.name}** was deleted",
|
||||
"CHANNEL_EDIT": "✏ Channel **#{channel.name}** was edited",
|
||||
|
||||
"ROLE_CREATE": "🖊 Role **{role.name}** was created",
|
||||
"ROLE_DELETE": "🖊 Role **{role.name}** was deleted",
|
||||
"ROLE_EDIT": "🖊 Role **{role.name}** was edited",
|
||||
|
||||
"MESSAGE_EDIT": "✏ **{member.user.username}#{member.user.discriminator}** (`{member.id}`) message edited in **{channel.name}**:\n`B:` {before}\n`A:` {after}",
|
||||
"MESSAGE_DELETE": "🗑 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) message deleted in **{channel.name}**:\n{messageText}{attachments}",
|
||||
"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}**",
|
||||
|
||||
"COMMAND": "🤖 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) used command in **{channel.name}**:\n`{command}`",
|
||||
|
||||
"SPAM_DELETE": "🛑 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) triggered spam filter: **{filterName}**",
|
||||
"CENSOR": "🛑 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) censored message in **{channel.name}**:\n`{messageText}`",
|
||||
|
||||
"CASE_CREATE": "✏ **{member.user.username}#{member.user.discriminator}** (`{member.id}`) manually created new **{caseType}** case (#{caseNum})"
|
||||
}
|
||||
|
|
57
src/data/GuildLogs.ts
Normal file
57
src/data/GuildLogs.ts
Normal file
|
@ -0,0 +1,57 @@
|
|||
import EventEmitter from "events";
|
||||
import { LogType } from "./LogType";
|
||||
|
||||
// Use the same instance for the same guild, even if a new instance is created
|
||||
const guildInstances: Map<string, GuildLogs> = new Map();
|
||||
|
||||
interface IIgnoredLog {
|
||||
type: LogType;
|
||||
ignoreId: any;
|
||||
}
|
||||
|
||||
export class GuildLogs extends EventEmitter {
|
||||
protected guildId: string;
|
||||
protected ignoredLogs: IIgnoredLog[];
|
||||
|
||||
constructor(guildId) {
|
||||
if (guildInstances.has(guildId)) {
|
||||
// Return existing instance for this guild if one exists
|
||||
return guildInstances.get(guildId);
|
||||
}
|
||||
|
||||
super();
|
||||
this.guildId = guildId;
|
||||
|
||||
// Store the instance for this guild so it can be returned later if a new instance for this guild is requested
|
||||
guildInstances.set(guildId, this);
|
||||
}
|
||||
|
||||
log(type: LogType, data: any, ignoreId = null) {
|
||||
if (ignoreId && this.isLogIgnored(type, ignoreId)) {
|
||||
this.clearIgnoredLog(type, ignoreId);
|
||||
return;
|
||||
}
|
||||
|
||||
this.emit("log", { type, data });
|
||||
}
|
||||
|
||||
ignoreLog(type: LogType, ignoreId: any, expiryTime: number = null) {
|
||||
this.ignoredLogs.push({ type, ignoreId });
|
||||
|
||||
// Clear after expiry (15sec by default)
|
||||
setTimeout(() => {
|
||||
this.clearIgnoredLog(type, ignoreId);
|
||||
}, expiryTime || 1000 * 15);
|
||||
}
|
||||
|
||||
isLogIgnored(type: LogType, ignoreId: any) {
|
||||
return this.ignoredLogs.some(info => type === info.type && ignoreId === info.ignoreId);
|
||||
}
|
||||
|
||||
clearIgnoredLog(type: LogType, ignoreId: any) {
|
||||
this.ignoredLogs.splice(
|
||||
this.ignoredLogs.findIndex(info => type === info.type && ignoreId === info.ignoreId),
|
||||
1
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1,26 +0,0 @@
|
|||
import EventEmitter from "events";
|
||||
import { LogType } from "./LogType";
|
||||
|
||||
// Use the same instance for the same guild, even if a new instance is created
|
||||
const guildInstances: Map<string, GuildServerLogs> = new Map();
|
||||
|
||||
export class GuildServerLogs extends EventEmitter {
|
||||
protected guildId: string;
|
||||
|
||||
constructor(guildId) {
|
||||
if (guildInstances.has(guildId)) {
|
||||
// Return existing instance for this guild if one exists
|
||||
return guildInstances.get(guildId);
|
||||
}
|
||||
|
||||
super();
|
||||
this.guildId = guildId;
|
||||
|
||||
// Store the instance for this guild so it can be returned later if a new instance for this guild is requested
|
||||
guildInstances.set(guildId, this);
|
||||
}
|
||||
|
||||
log(type: LogType, data: any) {
|
||||
this.emit("log", { type, data });
|
||||
}
|
||||
}
|
|
@ -17,7 +17,6 @@ export enum LogType {
|
|||
|
||||
CHANNEL_CREATE,
|
||||
CHANNEL_DELETE,
|
||||
CHANNEL_EDIT,
|
||||
|
||||
ROLE_CREATE,
|
||||
ROLE_DELETE,
|
||||
|
|
|
@ -1,11 +1,21 @@
|
|||
import { decorators as d, Plugin } from "knub";
|
||||
import { GuildServerLogs } from "../data/GuildServerLogs";
|
||||
import { GuildLogs } from "../data/GuildLogs";
|
||||
import { LogType } from "../data/LogType";
|
||||
import { TextChannel } from "eris";
|
||||
import { formatTemplateString, stripObjectToScalars } from "../utils";
|
||||
import {
|
||||
Channel,
|
||||
Constants as ErisConstants,
|
||||
Member,
|
||||
Message,
|
||||
PrivateChannel,
|
||||
TextChannel,
|
||||
User
|
||||
} from "eris";
|
||||
import { findRelevantAuditLogEntry, formatTemplateString, stripObjectToScalars } from "../utils";
|
||||
import DefaultLogMessages from "../data/DefaultLogMessages.json";
|
||||
import moment from "moment-timezone";
|
||||
import humanizeDuration from "humanize-duration";
|
||||
import isEqual from "lodash.isequal";
|
||||
import diff from "lodash.difference";
|
||||
|
||||
interface ILogChannel {
|
||||
include?: LogType[];
|
||||
|
@ -16,8 +26,17 @@ interface ILogChannelMap {
|
|||
[channelId: string]: ILogChannel;
|
||||
}
|
||||
|
||||
const unknownMember = {
|
||||
id: 0,
|
||||
user: {
|
||||
id: 0,
|
||||
username: "Unknown",
|
||||
discriminator: "0000"
|
||||
}
|
||||
};
|
||||
|
||||
export class LogsPlugin extends Plugin {
|
||||
protected serverLogs: GuildServerLogs;
|
||||
protected serverLogs: GuildLogs;
|
||||
protected logListener;
|
||||
|
||||
getDefaultOptions() {
|
||||
|
@ -33,7 +52,7 @@ export class LogsPlugin extends Plugin {
|
|||
}
|
||||
|
||||
onLoad() {
|
||||
this.serverLogs = new GuildServerLogs(this.guildId);
|
||||
this.serverLogs = new GuildLogs(this.guildId);
|
||||
|
||||
this.logListener = ({ type, data }) => this.log(type, data);
|
||||
this.serverLogs.on("log", this.logListener);
|
||||
|
@ -88,4 +107,179 @@ export class LogsPlugin extends Plugin {
|
|||
account_age: accountAge
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("guildMemberRemove")
|
||||
onMemberLeave(_, member) {
|
||||
this.log(LogType.MEMBER_LEAVE, {
|
||||
member: stripObjectToScalars(member, ["user"])
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("guildBanAdd")
|
||||
async onMemberBan(_, user) {
|
||||
const relevantAuditLogEntry = await findRelevantAuditLogEntry(
|
||||
this.bot,
|
||||
ErisConstants.AuditLogActions.MEMBER_BAN_ADD,
|
||||
user.id
|
||||
);
|
||||
|
||||
if (relevantAuditLogEntry) {
|
||||
this.log(LogType.MEMBER_BAN, {
|
||||
user: stripObjectToScalars(user),
|
||||
mod: relevantAuditLogEntry.member
|
||||
});
|
||||
} else {
|
||||
this.log(LogType.MEMBER_BAN, {
|
||||
user: stripObjectToScalars(user),
|
||||
mod: unknownMember
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@d.event("guildBanRemove")
|
||||
async onMemberUnban(_, user) {
|
||||
const relevantAuditLogEntry = await findRelevantAuditLogEntry(
|
||||
this.bot,
|
||||
ErisConstants.AuditLogActions.MEMBER_BAN_REMOVE,
|
||||
user.id
|
||||
);
|
||||
|
||||
if (relevantAuditLogEntry) {
|
||||
this.log(LogType.MEMBER_UNBAN, {
|
||||
user: stripObjectToScalars(user),
|
||||
mod: relevantAuditLogEntry.member
|
||||
});
|
||||
} else {
|
||||
this.log(LogType.MEMBER_UNBAN, {
|
||||
user: stripObjectToScalars(user),
|
||||
mod: unknownMember
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@d.event("guildMemberUpdate")
|
||||
onMemberUpdate(_, member: Member, oldMember: Member) {
|
||||
if (!oldMember) return;
|
||||
|
||||
if (member.nick !== oldMember.nick) {
|
||||
this.log(LogType.MEMBER_NICK_CHANGE, {
|
||||
member,
|
||||
oldNick: oldMember.nick,
|
||||
newNick: member.nick
|
||||
});
|
||||
}
|
||||
|
||||
if (!isEqual(oldMember.roles, member.roles)) {
|
||||
const addedRoles = diff(oldMember.roles, member.roles);
|
||||
const removedRoles = diff(member.roles, oldMember.roles);
|
||||
|
||||
if (addedRoles.length) {
|
||||
this.log(LogType.MEMBER_ROLE_ADD, {
|
||||
member,
|
||||
role: this.guild.roles.get(addedRoles[0])
|
||||
});
|
||||
} else if (removedRoles.length) {
|
||||
this.log(LogType.MEMBER_ROLE_REMOVE, {
|
||||
member,
|
||||
role: this.guild.roles.get(removedRoles[0])
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@d.event("userUpdate")
|
||||
onUserUpdate(user: User, oldUser: User) {
|
||||
if (!oldUser) return;
|
||||
|
||||
if (user.username !== oldUser.username || user.discriminator !== oldUser.discriminator) {
|
||||
const member = this.guild.members.get(user.id) || { id: user.id, user };
|
||||
this.log(LogType.MEMBER_USERNAME_CHANGE, {
|
||||
member: stripObjectToScalars(member, ["user"]),
|
||||
oldName: `${oldUser.username}#${oldUser.discriminator}`,
|
||||
newName: `${user.username}#${user.discriminator}`
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@d.event("channelCreate")
|
||||
onChannelCreate(channel) {
|
||||
this.log(LogType.CHANNEL_CREATE, {
|
||||
channel: stripObjectToScalars(channel)
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("channelDelete")
|
||||
onChannelDelete(channel) {
|
||||
this.log(LogType.CHANNEL_DELETE, {
|
||||
channel: stripObjectToScalars(channel)
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("guildRoleCreate")
|
||||
onRoleCreate(role) {
|
||||
this.log(LogType.ROLE_CREATE, {
|
||||
role: stripObjectToScalars(role)
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("guildRoleDelete")
|
||||
onRoleDelete(role) {
|
||||
this.log(LogType.ROLE_DELETE, {
|
||||
role: stripObjectToScalars(role)
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("messageUpdate")
|
||||
onMessageUpdate(msg: Message, oldMsg: Message) {
|
||||
if (oldMsg && msg.content === oldMsg.content) return;
|
||||
|
||||
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 || ""
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("messageDelete")
|
||||
onMessageDelete(msg: Message) {
|
||||
this.log(LogType.MESSAGE_DELETE, {
|
||||
member: stripObjectToScalars(msg.member, ["user"]),
|
||||
channel: stripObjectToScalars(msg.channel),
|
||||
messageText: msg.cleanContent || ""
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("messageDeleteBulk")
|
||||
onMessageDeleteBulk(messages: Message[]) {
|
||||
this.log(LogType.MESSAGE_DELETE_BULK, {
|
||||
count: messages.length,
|
||||
channel: messages[0] ? messages[0].channel : null
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("voiceChannelJoin")
|
||||
onVoiceChannelJoin(member: Member, channel: Channel) {
|
||||
this.log(LogType.VOICE_CHANNEL_JOIN, {
|
||||
member: stripObjectToScalars(member, ["user"]),
|
||||
channel: stripObjectToScalars(channel)
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("voiceChannelLeave")
|
||||
onVoiceChannelLeave(member: Member, channel: Channel) {
|
||||
this.log(LogType.VOICE_CHANNEL_LEAVE, {
|
||||
member: stripObjectToScalars(member, ["user"]),
|
||||
channel: stripObjectToScalars(channel)
|
||||
});
|
||||
}
|
||||
|
||||
@d.event("voiceChannelSwitch")
|
||||
onVoiceChannelSwitch(member: Member, oldChannel: Channel, newChannel: Channel) {
|
||||
this.log(LogType.VOICE_CHANNEL_MOVE, {
|
||||
member: stripObjectToScalars(member, ["user"]),
|
||||
oldChannel: stripObjectToScalars(oldChannel),
|
||||
newChannel: stripObjectToScalars(newChannel)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ import { GuildCases } from "../data/GuildCases";
|
|||
import {
|
||||
convertDelayStringToMS,
|
||||
errorMessage,
|
||||
findRelevantAuditLogEntry,
|
||||
formatTemplateString,
|
||||
stripObjectToScalars,
|
||||
successMessage
|
||||
|
@ -14,7 +15,7 @@ import { GuildMutes } from "../data/GuildMutes";
|
|||
import Timer = NodeJS.Timer;
|
||||
import Case from "../models/Case";
|
||||
import { CaseType } from "../data/CaseType";
|
||||
import { GuildServerLogs } from "../data/GuildServerLogs";
|
||||
import { GuildLogs } from "../data/GuildLogs";
|
||||
import { LogType } from "../data/LogType";
|
||||
|
||||
const sleep = (ms: number): Promise<void> => {
|
||||
|
@ -26,14 +27,14 @@ const sleep = (ms: number): Promise<void> => {
|
|||
export class ModActionsPlugin extends Plugin {
|
||||
protected cases: GuildCases;
|
||||
protected mutes: GuildMutes;
|
||||
protected serverLogs: GuildServerLogs;
|
||||
protected serverLogs: GuildLogs;
|
||||
|
||||
protected muteClearIntervalId: Timer;
|
||||
|
||||
async onLoad() {
|
||||
this.cases = new GuildCases(this.guildId);
|
||||
this.mutes = new GuildMutes(this.guildId);
|
||||
this.serverLogs = new GuildServerLogs(this.guildId);
|
||||
this.serverLogs = new GuildLogs(this.guildId);
|
||||
|
||||
// Check for expired mutes every 5s
|
||||
this.clearExpiredMutes();
|
||||
|
@ -100,7 +101,11 @@ export class ModActionsPlugin extends Plugin {
|
|||
@d.event("guildBanAdd")
|
||||
async onGuildBanAdd(guild: Guild, user: User) {
|
||||
await sleep(1000); // Wait a moment for the audit log to update
|
||||
const relevantAuditLogEntry = await this.findRelevantAuditLogEntry("MEMBER_BAN_ADD", user.id);
|
||||
const relevantAuditLogEntry = await findRelevantAuditLogEntry(
|
||||
this.bot,
|
||||
"MEMBER_BAN_ADD",
|
||||
user.id
|
||||
);
|
||||
|
||||
if (relevantAuditLogEntry) {
|
||||
const modId = relevantAuditLogEntry.user.id;
|
||||
|
@ -125,7 +130,8 @@ export class ModActionsPlugin extends Plugin {
|
|||
*/
|
||||
@d.event("guildBanRemove")
|
||||
async onGuildBanRemove(guild: Guild, user: User) {
|
||||
const relevantAuditLogEntry = await this.findRelevantAuditLogEntry(
|
||||
const relevantAuditLogEntry = await findRelevantAuditLogEntry(
|
||||
this.bot,
|
||||
"MEMBER_BAN_REMOVE",
|
||||
user.id
|
||||
);
|
||||
|
@ -251,6 +257,7 @@ export class ModActionsPlugin extends Plugin {
|
|||
}
|
||||
|
||||
// Apply "muted" role
|
||||
this.serverLogs.ignoreLog(LogType.MEMBER_ROLE_ADD, args.member.id);
|
||||
await args.member.addRole(this.configValue("mute_role"), args.reason);
|
||||
await this.mutes.addOrUpdateMute(args.member.id, muteTime);
|
||||
|
||||
|
@ -318,6 +325,7 @@ export class ModActionsPlugin extends Plugin {
|
|||
}
|
||||
|
||||
// Remove "muted" role
|
||||
this.serverLogs.ignoreLog(LogType.MEMBER_ROLE_REMOVE, args.member.id);
|
||||
await args.member.removeRole(this.configValue("mute_role"), args.reason);
|
||||
await this.mutes.clear(args.member.id);
|
||||
|
||||
|
@ -360,6 +368,7 @@ export class ModActionsPlugin extends Plugin {
|
|||
}
|
||||
|
||||
// Kick the user
|
||||
this.serverLogs.ignoreLog(LogType.MEMBER_KICK, args.member.id);
|
||||
args.member.kick(args.reason);
|
||||
|
||||
// Create a case for this action
|
||||
|
@ -403,6 +412,7 @@ export class ModActionsPlugin extends Plugin {
|
|||
}
|
||||
|
||||
// Ban the user
|
||||
this.serverLogs.ignoreLog(LogType.MEMBER_BAN, args.member.id);
|
||||
args.member.ban(1, args.reason);
|
||||
|
||||
// Create a case for this action
|
||||
|
@ -423,6 +433,8 @@ export class ModActionsPlugin extends Plugin {
|
|||
@d.command("unban", "<userId:string> [reason:string$]")
|
||||
@d.permission("ban")
|
||||
async unbanCmd(msg: Message, args: any) {
|
||||
this.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, args.member.id);
|
||||
|
||||
try {
|
||||
await this.guild.unbanMember(args.userId, args.reason);
|
||||
} catch (e) {
|
||||
|
@ -455,6 +467,8 @@ export class ModActionsPlugin extends Plugin {
|
|||
return;
|
||||
}
|
||||
|
||||
this.serverLogs.ignoreLog(LogType.MEMBER_FORCEBAN, args.member.id);
|
||||
|
||||
try {
|
||||
await this.guild.banMember(args.userId, 1, args.reason);
|
||||
} catch (e) {
|
||||
|
@ -501,13 +515,22 @@ export class ModActionsPlugin extends Plugin {
|
|||
}
|
||||
|
||||
// Create the case
|
||||
await this.createCase(args.userId, msg.author.id, CaseType[type], null, args.reason);
|
||||
const caseId = await this.createCase(
|
||||
args.userId,
|
||||
msg.author.id,
|
||||
CaseType[type],
|
||||
null,
|
||||
args.reason
|
||||
);
|
||||
const theCase = await this.cases.find(caseId);
|
||||
|
||||
// Log the action
|
||||
msg.channel.createMessage(successMessage("Case created!"));
|
||||
this.serverLogs.log(LogType.CASE_CREATE, {
|
||||
mod: stripObjectToScalars(msg.member, ["user"]),
|
||||
userId: args.userId
|
||||
userId: args.userId,
|
||||
caseNum: theCase.case_number,
|
||||
caseType: type.toUpperCase()
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -665,30 +688,6 @@ export class ModActionsPlugin extends Plugin {
|
|||
return this.displayCase(caseOrCaseId, caseLogChannelId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find a relevant audit log entry for the given user and action. Only accepts audit log entries from the past 10 minutes.
|
||||
*/
|
||||
protected async findRelevantAuditLogEntry(
|
||||
actionType: string,
|
||||
userId: string
|
||||
): Promise<GuildAuditLogEntry> {
|
||||
const auditLogEntries = await this.bot.getGuildAuditLogs(this.guildId, 5, actionType);
|
||||
|
||||
auditLogEntries.entries.sort((a, b) => {
|
||||
if (a.createdAt > b.createdAt) return -1;
|
||||
if (a.createdAt > b.createdAt) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const cutoffDate = new Date();
|
||||
cutoffDate.setTime(cutoffDate.getTime() - 1000 * 15);
|
||||
const cutoffTS = cutoffDate.getTime();
|
||||
|
||||
return auditLogEntries.entries.find(entry => {
|
||||
return entry.target.id === userId && entry.createdAt >= cutoffTS;
|
||||
});
|
||||
}
|
||||
|
||||
protected async createCase(
|
||||
userId: string,
|
||||
modId: string,
|
||||
|
|
36
src/utils.ts
36
src/utils.ts
|
@ -1,4 +1,5 @@
|
|||
import at = require("lodash.at");
|
||||
import { GuildAuditLogEntry } from "eris";
|
||||
|
||||
/**
|
||||
* Turns a "delay string" such as "1h30m" to milliseconds
|
||||
|
@ -42,7 +43,7 @@ export function uclower(str) {
|
|||
return str[0].toLowerCase() + str.slice(1);
|
||||
}
|
||||
|
||||
export function stripObjectToScalars(obj, includedNested: string[]) {
|
||||
export function stripObjectToScalars(obj, includedNested: string[] = []) {
|
||||
const result = {};
|
||||
|
||||
for (const key in obj) {
|
||||
|
@ -78,3 +79,36 @@ export function formatTemplateString(str: string, values) {
|
|||
export function isSnowflake(v: string): boolean {
|
||||
return /^\d{17,20}$/.test(v);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempts to find a relevant audit log entry for the given user and action
|
||||
*/
|
||||
export async function findRelevantAuditLogEntry(
|
||||
bot,
|
||||
actionType: number,
|
||||
userId: string,
|
||||
attempts: number = 3,
|
||||
attemptDelay: number = 1500
|
||||
): Promise<GuildAuditLogEntry> {
|
||||
const auditLogEntries = await this.bot.getGuildAuditLogs(this.guildId, 5, null, actionType);
|
||||
|
||||
auditLogEntries.entries.sort((a, b) => {
|
||||
if (a.createdAt > b.createdAt) return -1;
|
||||
if (a.createdAt > b.createdAt) return 1;
|
||||
return 0;
|
||||
});
|
||||
|
||||
const cutoffTS = Date.now() - 1000 * 60 * 2;
|
||||
|
||||
const relevantEntry = auditLogEntries.entries.find(entry => {
|
||||
return entry.target.id === userId && entry.createdAt >= cutoffTS;
|
||||
});
|
||||
|
||||
if (relevantEntry) {
|
||||
return relevantEntry;
|
||||
} else if (attempts > 0) {
|
||||
return findRelevantAuditLogEntry(bot, actionType, userId, attempts - 1, attemptDelay);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue