From 2c8603ca700a75041342b53f3f42ad3a4a9fae96 Mon Sep 17 00:00:00 2001 From: Dragory Date: Sat, 24 Nov 2018 17:59:05 +0200 Subject: [PATCH] Use GuildSavedMessages events in logs. Fix code blocks in edit logs. --- src/data/GuildSavedMessages.ts | 24 +++++++++ src/plugins/Logs.ts | 89 ++++++++++++++++++++++------------ src/plugins/Post.ts | 3 +- src/utils.ts | 16 ++++-- 4 files changed, 95 insertions(+), 37 deletions(-) diff --git a/src/data/GuildSavedMessages.ts b/src/data/GuildSavedMessages.ts index 09b01cc9..fe378380 100644 --- a/src/data/GuildSavedMessages.ts +++ b/src/data/GuildSavedMessages.ts @@ -11,6 +11,8 @@ const RETENTION_PERIOD = 7 * 24 * 60 * 60 * 1000; // 1 week export class GuildSavedMessages extends BaseRepository { private messages: Repository; + protected toBePermanent: Set; + public events: QueuedEventEmitter; constructor(guildId) { @@ -18,6 +20,8 @@ export class GuildSavedMessages extends BaseRepository { this.messages = getRepository(SavedMessage); this.events = new QueuedEventEmitter(); + this.toBePermanent = new Set(); + this.cleanup(); setInterval(() => this.cleanup(), CLEANUP_INTERVAL); } @@ -84,6 +88,12 @@ export class GuildSavedMessages extends BaseRepository { } async create(data) { + const isPermanent = this.toBePermanent.has(data.id); + if (isPermanent) { + data.is_permanent = true; + this.toBePermanent.delete(data.id); + } + try { await this.messages.insert(data); } catch (e) { @@ -145,4 +155,18 @@ export class GuildSavedMessages extends BaseRepository { const newData = this.msgToSavedMessageData(msg); return this.saveEdit(msg.id, newData); } + + async setPermanent(id: string) { + const savedMsg = await this.find(id); + if (savedMsg) { + await this.messages.update( + { id }, + { + is_permanent: true + } + ); + } else { + this.toBePermanent.add(id); + } + } } diff --git a/src/plugins/Logs.ts b/src/plugins/Logs.ts index 38fc45cc..d0e46203 100644 --- a/src/plugins/Logs.ts +++ b/src/plugins/Logs.ts @@ -2,12 +2,20 @@ import { decorators as d, Plugin } from "knub"; import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; import { Channel, Constants as ErisConstants, Member, Message, TextChannel, User } from "eris"; -import { findRelevantAuditLogEntry, formatTemplateString, stripObjectToScalars } from "../utils"; +import { + deactivateMentions, + disableCodeBlocks, + 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"; +import { GuildSavedMessages } from "../data/GuildSavedMessages"; +import { SavedMessage } from "../data/entities/SavedMessage"; interface ILogChannel { include?: string[]; @@ -26,8 +34,14 @@ const unknownUser = { export class LogsPlugin extends Plugin { protected serverLogs: GuildLogs; + protected savedMessages: GuildSavedMessages; + protected logListener; + private onMessageDeleteFn; + private onMessageDeleteBulkFn; + private onMessageUpdateFn; + getDefaultOptions() { return { config: { @@ -42,13 +56,22 @@ export class LogsPlugin extends Plugin { onLoad() { this.serverLogs = new GuildLogs(this.guildId); + this.savedMessages = GuildSavedMessages.getInstance(this.guildId); this.logListener = ({ type, data }) => this.log(type, data); this.serverLogs.on("log", this.logListener); + + this.savedMessages.events.on("delete", this.onMessageDelete.bind(this)); + this.savedMessages.events.on("deleteBulk", this.onMessageDeleteBulk.bind(this)); + this.savedMessages.events.on("update", this.onMessageUpdate.bind(this)); } onUnload() { this.serverLogs.removeListener("log", this.logListener); + + this.savedMessages.events.off("delete", this.onMessageDelete.bind(this)); + this.savedMessages.events.off("deleteBulk", this.onMessageDeleteBulk.bind(this)); + this.savedMessages.events.off("update", this.onMessageUpdate.bind(this)); } async log(type, data) { @@ -59,10 +82,7 @@ export class LogsPlugin extends Plugin { const channel = this.guild.channels.get(channelId); if (!channel || !(channel instanceof TextChannel)) continue; - if ( - (opts.include && opts.include.includes(typeStr)) || - (opts.exclude && !opts.exclude.includes(typeStr)) - ) { + if ((opts.include && opts.include.includes(typeStr)) || (opts.exclude && !opts.exclude.includes(typeStr))) { const message = this.getLogMessage(type, data); // TODO: Split log messages that are too long if (message) await channel.createMessage(message).catch(() => {}); @@ -234,55 +254,64 @@ export class LogsPlugin extends Plugin { }); } - @d.event("messageUpdate") - onMessageUpdate(msg: Message, oldMsg: Message) { - if (!msg.author) return; - if (oldMsg && msg.content === oldMsg.content) return; - if (msg.type !== 0) return; + // Uses events from savesMessages + onMessageUpdate(savedMessage: SavedMessage, oldSavedMessage: SavedMessage) { + if (oldSavedMessage && JSON.stringify(savedMessage.data) === JSON.stringify(oldSavedMessage.data)) return; + + const member = this.guild.members.get(savedMessage.user_id); + const channel = this.guild.channels.get(savedMessage.channel_id); + + const before = oldSavedMessage + ? disableCodeBlocks(deactivateMentions(oldSavedMessage.data.content || "")) + : "Unknown pre-edit content"; + const after = disableCodeBlocks(deactivateMentions(savedMessage.data.content || "")); this.serverLogs.log(LogType.MESSAGE_EDIT, { - member: stripObjectToScalars(msg.member, ["user"]), - channel: stripObjectToScalars(msg.channel), - before: oldMsg ? oldMsg.cleanContent || oldMsg.content || "" : "Unavailable due to restart", - after: msg.cleanContent || msg.content || "" + member: stripObjectToScalars(member, ["user"]), + channel: stripObjectToScalars(channel), + before, + after }); } - @d.event("messageDelete") - onMessageDelete(msg: Message) { - if (msg.type != null && msg.type !== 0) return; + // Uses events from savesMessages + onMessageDelete(savedMessage: SavedMessage) { + const member = this.guild.members.get(savedMessage.user_id); + const channel = this.guild.channels.get(savedMessage.channel_id); - if (msg.member) { + if (member) { this.serverLogs.log( LogType.MESSAGE_DELETE, { - member: stripObjectToScalars(msg.member, ["user"]), - channel: stripObjectToScalars(msg.channel), - messageText: msg.cleanContent || msg.content || "" + member: stripObjectToScalars(member, ["user"]), + channel: stripObjectToScalars(channel), + messageText: disableCodeBlocks(deactivateMentions(savedMessage.data.content || "")) }, - msg.id + savedMessage.id ); } else { this.serverLogs.log( LogType.MESSAGE_DELETE_BARE, { - messageId: msg.id, - channel: stripObjectToScalars(msg.channel) + messageId: savedMessage.id, + channel: stripObjectToScalars(channel) }, - msg.id + savedMessage.id ); } } - @d.event("messageDeleteBulk") - onMessageDeleteBulk(messages: Message[]) { + // Uses events from savesMessages + onMessageDeleteBulk(savedMessages: SavedMessage[]) { + const channel = this.guild.channels.get(savedMessages[0].channel_id); + this.serverLogs.log( LogType.MESSAGE_DELETE_BULK, { - count: messages.length, - channel: messages[0] ? messages[0].channel : null + count: savedMessages.length, + channel }, - messages[0] && messages[0].id + savedMessages[0].id ); } diff --git a/src/plugins/Post.ts b/src/plugins/Post.ts index 888f40f2..e1f8c965 100644 --- a/src/plugins/Post.ts +++ b/src/plugins/Post.ts @@ -42,7 +42,7 @@ export class PostPlugin extends Plugin { } const createdMsg = await args.channel.createMessage(args.content); - await this.savedMessages.createFromMsg(createdMsg, { is_permanent: true }); + await this.savedMessages.setPermanent(createdMsg.id); } /** @@ -64,6 +64,5 @@ export class PostPlugin extends Plugin { } const edited = await this.bot.editMessage(savedMessage.channel_id, savedMessage.id, args.content); - await this.savedMessages.saveEditFromMsg(edited); } } diff --git a/src/utils.ts b/src/utils.ts index f641f9f5..75feb5ca 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,8 +1,9 @@ import at = require("lodash.at"); -import { Client, Guild, GuildAuditLogEntry, Message, TextChannel } from "eris"; +import { Client, Guild, GuildAuditLogEntry, GuildChannel, Message, TextChannel } from "eris"; import url from "url"; import tlds from "tlds"; import emojiRegex from "emoji-regex"; +import { GuildSavedMessages } from "./data/GuildSavedMessages"; /** * Turns a "delay string" such as "1h30m" to milliseconds @@ -166,10 +167,7 @@ export function getInviteCodesInString(str: string): string[] { export const unicodeEmojiRegex = emojiRegex(); export const customEmojiRegex = /<:(?:.*?):(\d+)>/g; -export const anyEmojiRegex = new RegExp( - `(?:(?:${unicodeEmojiRegex.source})|(?:${customEmojiRegex.source}))`, - "g" -); +export const anyEmojiRegex = new RegExp(`(?:(?:${unicodeEmojiRegex.source})|(?:${customEmojiRegex.source}))`, "g"); export function getEmojiInString(str: string): string[] { return str.match(anyEmojiRegex) || []; @@ -275,4 +273,12 @@ export function disableLinkPreviews(str: string): string { return str.replace(/(?"); } +export function deactivateMentions(content: string): string { + return content.replace(/@/g, "@\u200b"); +} + +export function disableCodeBlocks(content: string): string { + return content.replace(/`/g, "`\u200b"); +} + export const DBDateFormat = "YYYY-MM-DD HH:mm:ss";