3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-03-15 05:41:51 +00:00

feat: add logging for most events

This commit is contained in:
Dragory 2018-07-29 18:46:49 +03:00
parent 6625e9ffb0
commit 724c30703f
9 changed files with 361 additions and 69 deletions

5
package-lock.json generated
View file

@ -2490,6 +2490,11 @@
"resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
"integrity": "sha1-nMtOUF1Ia5FlE0V3KIWi3yf9AXw=" "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": { "lodash.merge": {
"version": "4.6.1", "version": "4.6.1",
"resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.1.tgz",

View file

@ -33,6 +33,8 @@
"knex": "^0.14.6", "knex": "^0.14.6",
"knub": "^9.4.11", "knub": "^9.4.11",
"lodash.at": "^4.6.0", "lodash.at": "^4.6.0",
"lodash.difference": "^4.5.0",
"lodash.isequal": "^4.5.0",
"mariasql": "^0.2.6", "mariasql": "^0.2.6",
"moment-timezone": "^0.5.21", "moment-timezone": "^0.5.21",
"ts-node": "^3.3.0", "ts-node": "^3.3.0",

View file

@ -3,10 +3,38 @@
"MEMBER_MUTE": "🔇 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) was muted 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_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_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_KICK": "👢 **{user.username}#{user.discriminator}** (`{user.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_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_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_LEAVE": "📤 **{member.user.username}#{member.user.discriminator}** (`{member.id}`) 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_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}** role removed **{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
View 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
);
}
}

View file

@ -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 });
}
}

View file

@ -17,7 +17,6 @@ export enum LogType {
CHANNEL_CREATE, CHANNEL_CREATE,
CHANNEL_DELETE, CHANNEL_DELETE,
CHANNEL_EDIT,
ROLE_CREATE, ROLE_CREATE,
ROLE_DELETE, ROLE_DELETE,

View file

@ -1,11 +1,21 @@
import { decorators as d, Plugin } from "knub"; import { decorators as d, Plugin } from "knub";
import { GuildServerLogs } from "../data/GuildServerLogs"; import { GuildLogs } from "../data/GuildLogs";
import { LogType } from "../data/LogType"; import { LogType } from "../data/LogType";
import { TextChannel } from "eris"; import {
import { formatTemplateString, stripObjectToScalars } from "../utils"; Channel,
Constants as ErisConstants,
Member,
Message,
PrivateChannel,
TextChannel,
User
} from "eris";
import { findRelevantAuditLogEntry, formatTemplateString, stripObjectToScalars } from "../utils";
import DefaultLogMessages from "../data/DefaultLogMessages.json"; import DefaultLogMessages from "../data/DefaultLogMessages.json";
import moment from "moment-timezone"; import moment from "moment-timezone";
import humanizeDuration from "humanize-duration"; import humanizeDuration from "humanize-duration";
import isEqual from "lodash.isequal";
import diff from "lodash.difference";
interface ILogChannel { interface ILogChannel {
include?: LogType[]; include?: LogType[];
@ -16,8 +26,17 @@ interface ILogChannelMap {
[channelId: string]: ILogChannel; [channelId: string]: ILogChannel;
} }
const unknownMember = {
id: 0,
user: {
id: 0,
username: "Unknown",
discriminator: "0000"
}
};
export class LogsPlugin extends Plugin { export class LogsPlugin extends Plugin {
protected serverLogs: GuildServerLogs; protected serverLogs: GuildLogs;
protected logListener; protected logListener;
getDefaultOptions() { getDefaultOptions() {
@ -33,7 +52,7 @@ export class LogsPlugin extends Plugin {
} }
onLoad() { onLoad() {
this.serverLogs = new GuildServerLogs(this.guildId); this.serverLogs = new GuildLogs(this.guildId);
this.logListener = ({ type, data }) => this.log(type, data); this.logListener = ({ type, data }) => this.log(type, data);
this.serverLogs.on("log", this.logListener); this.serverLogs.on("log", this.logListener);
@ -88,4 +107,179 @@ export class LogsPlugin extends Plugin {
account_age: accountAge 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)
});
}
} }

View file

@ -6,6 +6,7 @@ import { GuildCases } from "../data/GuildCases";
import { import {
convertDelayStringToMS, convertDelayStringToMS,
errorMessage, errorMessage,
findRelevantAuditLogEntry,
formatTemplateString, formatTemplateString,
stripObjectToScalars, stripObjectToScalars,
successMessage successMessage
@ -14,7 +15,7 @@ import { GuildMutes } from "../data/GuildMutes";
import Timer = NodeJS.Timer; import Timer = NodeJS.Timer;
import Case from "../models/Case"; import Case from "../models/Case";
import { CaseType } from "../data/CaseType"; import { CaseType } from "../data/CaseType";
import { GuildServerLogs } from "../data/GuildServerLogs"; import { GuildLogs } from "../data/GuildLogs";
import { LogType } from "../data/LogType"; import { LogType } from "../data/LogType";
const sleep = (ms: number): Promise<void> => { const sleep = (ms: number): Promise<void> => {
@ -26,14 +27,14 @@ const sleep = (ms: number): Promise<void> => {
export class ModActionsPlugin extends Plugin { export class ModActionsPlugin extends Plugin {
protected cases: GuildCases; protected cases: GuildCases;
protected mutes: GuildMutes; protected mutes: GuildMutes;
protected serverLogs: GuildServerLogs; protected serverLogs: GuildLogs;
protected muteClearIntervalId: Timer; protected muteClearIntervalId: Timer;
async onLoad() { async onLoad() {
this.cases = new GuildCases(this.guildId); this.cases = new GuildCases(this.guildId);
this.mutes = new GuildMutes(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 // Check for expired mutes every 5s
this.clearExpiredMutes(); this.clearExpiredMutes();
@ -100,7 +101,11 @@ export class ModActionsPlugin extends Plugin {
@d.event("guildBanAdd") @d.event("guildBanAdd")
async onGuildBanAdd(guild: Guild, user: User) { async onGuildBanAdd(guild: Guild, user: User) {
await sleep(1000); // Wait a moment for the audit log to update 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) { if (relevantAuditLogEntry) {
const modId = relevantAuditLogEntry.user.id; const modId = relevantAuditLogEntry.user.id;
@ -125,7 +130,8 @@ export class ModActionsPlugin extends Plugin {
*/ */
@d.event("guildBanRemove") @d.event("guildBanRemove")
async onGuildBanRemove(guild: Guild, user: User) { async onGuildBanRemove(guild: Guild, user: User) {
const relevantAuditLogEntry = await this.findRelevantAuditLogEntry( const relevantAuditLogEntry = await findRelevantAuditLogEntry(
this.bot,
"MEMBER_BAN_REMOVE", "MEMBER_BAN_REMOVE",
user.id user.id
); );
@ -251,6 +257,7 @@ export class ModActionsPlugin extends Plugin {
} }
// Apply "muted" role // Apply "muted" role
this.serverLogs.ignoreLog(LogType.MEMBER_ROLE_ADD, args.member.id);
await args.member.addRole(this.configValue("mute_role"), args.reason); await args.member.addRole(this.configValue("mute_role"), args.reason);
await this.mutes.addOrUpdateMute(args.member.id, muteTime); await this.mutes.addOrUpdateMute(args.member.id, muteTime);
@ -318,6 +325,7 @@ export class ModActionsPlugin extends Plugin {
} }
// Remove "muted" role // Remove "muted" role
this.serverLogs.ignoreLog(LogType.MEMBER_ROLE_REMOVE, args.member.id);
await args.member.removeRole(this.configValue("mute_role"), args.reason); await args.member.removeRole(this.configValue("mute_role"), args.reason);
await this.mutes.clear(args.member.id); await this.mutes.clear(args.member.id);
@ -360,6 +368,7 @@ export class ModActionsPlugin extends Plugin {
} }
// Kick the user // Kick the user
this.serverLogs.ignoreLog(LogType.MEMBER_KICK, args.member.id);
args.member.kick(args.reason); args.member.kick(args.reason);
// Create a case for this action // Create a case for this action
@ -403,6 +412,7 @@ export class ModActionsPlugin extends Plugin {
} }
// Ban the user // Ban the user
this.serverLogs.ignoreLog(LogType.MEMBER_BAN, args.member.id);
args.member.ban(1, args.reason); args.member.ban(1, args.reason);
// Create a case for this action // Create a case for this action
@ -423,6 +433,8 @@ export class ModActionsPlugin extends Plugin {
@d.command("unban", "<userId:string> [reason:string$]") @d.command("unban", "<userId:string> [reason:string$]")
@d.permission("ban") @d.permission("ban")
async unbanCmd(msg: Message, args: any) { async unbanCmd(msg: Message, args: any) {
this.serverLogs.ignoreLog(LogType.MEMBER_UNBAN, args.member.id);
try { try {
await this.guild.unbanMember(args.userId, args.reason); await this.guild.unbanMember(args.userId, args.reason);
} catch (e) { } catch (e) {
@ -455,6 +467,8 @@ export class ModActionsPlugin extends Plugin {
return; return;
} }
this.serverLogs.ignoreLog(LogType.MEMBER_FORCEBAN, args.member.id);
try { try {
await this.guild.banMember(args.userId, 1, args.reason); await this.guild.banMember(args.userId, 1, args.reason);
} catch (e) { } catch (e) {
@ -501,13 +515,22 @@ export class ModActionsPlugin extends Plugin {
} }
// Create the case // 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 // Log the action
msg.channel.createMessage(successMessage("Case created!")); msg.channel.createMessage(successMessage("Case created!"));
this.serverLogs.log(LogType.CASE_CREATE, { this.serverLogs.log(LogType.CASE_CREATE, {
mod: stripObjectToScalars(msg.member, ["user"]), 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); 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( protected async createCase(
userId: string, userId: string,
modId: string, modId: string,

View file

@ -1,4 +1,5 @@
import at = require("lodash.at"); import at = require("lodash.at");
import { GuildAuditLogEntry } from "eris";
/** /**
* Turns a "delay string" such as "1h30m" to milliseconds * Turns a "delay string" such as "1h30m" to milliseconds
@ -42,7 +43,7 @@ export function uclower(str) {
return str[0].toLowerCase() + str.slice(1); return str[0].toLowerCase() + str.slice(1);
} }
export function stripObjectToScalars(obj, includedNested: string[]) { export function stripObjectToScalars(obj, includedNested: string[] = []) {
const result = {}; const result = {};
for (const key in obj) { for (const key in obj) {
@ -78,3 +79,36 @@ export function formatTemplateString(str: string, values) {
export function isSnowflake(v: string): boolean { export function isSnowflake(v: string): boolean {
return /^\d{17,20}$/.test(v); 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;
}
}