3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-05-11 04:45:02 +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

View file

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

View file

@ -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,