Use GuildSavedMessages events in logs. Fix code blocks in edit logs.
This commit is contained in:
parent
1a6e680d81
commit
2c8603ca70
4 changed files with 95 additions and 37 deletions
|
@ -11,6 +11,8 @@ const RETENTION_PERIOD = 7 * 24 * 60 * 60 * 1000; // 1 week
|
||||||
|
|
||||||
export class GuildSavedMessages extends BaseRepository {
|
export class GuildSavedMessages extends BaseRepository {
|
||||||
private messages: Repository<SavedMessage>;
|
private messages: Repository<SavedMessage>;
|
||||||
|
protected toBePermanent: Set<string>;
|
||||||
|
|
||||||
public events: QueuedEventEmitter;
|
public events: QueuedEventEmitter;
|
||||||
|
|
||||||
constructor(guildId) {
|
constructor(guildId) {
|
||||||
|
@ -18,6 +20,8 @@ export class GuildSavedMessages extends BaseRepository {
|
||||||
this.messages = getRepository(SavedMessage);
|
this.messages = getRepository(SavedMessage);
|
||||||
this.events = new QueuedEventEmitter();
|
this.events = new QueuedEventEmitter();
|
||||||
|
|
||||||
|
this.toBePermanent = new Set();
|
||||||
|
|
||||||
this.cleanup();
|
this.cleanup();
|
||||||
setInterval(() => this.cleanup(), CLEANUP_INTERVAL);
|
setInterval(() => this.cleanup(), CLEANUP_INTERVAL);
|
||||||
}
|
}
|
||||||
|
@ -84,6 +88,12 @@ export class GuildSavedMessages extends BaseRepository {
|
||||||
}
|
}
|
||||||
|
|
||||||
async create(data) {
|
async create(data) {
|
||||||
|
const isPermanent = this.toBePermanent.has(data.id);
|
||||||
|
if (isPermanent) {
|
||||||
|
data.is_permanent = true;
|
||||||
|
this.toBePermanent.delete(data.id);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.messages.insert(data);
|
await this.messages.insert(data);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -145,4 +155,18 @@ export class GuildSavedMessages extends BaseRepository {
|
||||||
const newData = this.msgToSavedMessageData(msg);
|
const newData = this.msgToSavedMessageData(msg);
|
||||||
return this.saveEdit(msg.id, newData);
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,20 @@ import { decorators as d, Plugin } from "knub";
|
||||||
import { GuildLogs } from "../data/GuildLogs";
|
import { GuildLogs } from "../data/GuildLogs";
|
||||||
import { LogType } from "../data/LogType";
|
import { LogType } from "../data/LogType";
|
||||||
import { Channel, Constants as ErisConstants, Member, Message, TextChannel, User } from "eris";
|
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 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 isEqual from "lodash.isequal";
|
||||||
import diff from "lodash.difference";
|
import diff from "lodash.difference";
|
||||||
|
import { GuildSavedMessages } from "../data/GuildSavedMessages";
|
||||||
|
import { SavedMessage } from "../data/entities/SavedMessage";
|
||||||
|
|
||||||
interface ILogChannel {
|
interface ILogChannel {
|
||||||
include?: string[];
|
include?: string[];
|
||||||
|
@ -26,8 +34,14 @@ const unknownUser = {
|
||||||
|
|
||||||
export class LogsPlugin extends Plugin {
|
export class LogsPlugin extends Plugin {
|
||||||
protected serverLogs: GuildLogs;
|
protected serverLogs: GuildLogs;
|
||||||
|
protected savedMessages: GuildSavedMessages;
|
||||||
|
|
||||||
protected logListener;
|
protected logListener;
|
||||||
|
|
||||||
|
private onMessageDeleteFn;
|
||||||
|
private onMessageDeleteBulkFn;
|
||||||
|
private onMessageUpdateFn;
|
||||||
|
|
||||||
getDefaultOptions() {
|
getDefaultOptions() {
|
||||||
return {
|
return {
|
||||||
config: {
|
config: {
|
||||||
|
@ -42,13 +56,22 @@ export class LogsPlugin extends Plugin {
|
||||||
|
|
||||||
onLoad() {
|
onLoad() {
|
||||||
this.serverLogs = new GuildLogs(this.guildId);
|
this.serverLogs = new GuildLogs(this.guildId);
|
||||||
|
this.savedMessages = GuildSavedMessages.getInstance(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);
|
||||||
|
|
||||||
|
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() {
|
onUnload() {
|
||||||
this.serverLogs.removeListener("log", this.logListener);
|
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) {
|
async log(type, data) {
|
||||||
|
@ -59,10 +82,7 @@ export class LogsPlugin extends Plugin {
|
||||||
const channel = this.guild.channels.get(channelId);
|
const channel = this.guild.channels.get(channelId);
|
||||||
if (!channel || !(channel instanceof TextChannel)) continue;
|
if (!channel || !(channel instanceof TextChannel)) continue;
|
||||||
|
|
||||||
if (
|
if ((opts.include && opts.include.includes(typeStr)) || (opts.exclude && !opts.exclude.includes(typeStr))) {
|
||||||
(opts.include && opts.include.includes(typeStr)) ||
|
|
||||||
(opts.exclude && !opts.exclude.includes(typeStr))
|
|
||||||
) {
|
|
||||||
const message = this.getLogMessage(type, data);
|
const message = this.getLogMessage(type, data);
|
||||||
// TODO: Split log messages that are too long
|
// TODO: Split log messages that are too long
|
||||||
if (message) await channel.createMessage(message).catch(() => {});
|
if (message) await channel.createMessage(message).catch(() => {});
|
||||||
|
@ -234,55 +254,64 @@ export class LogsPlugin extends Plugin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@d.event("messageUpdate")
|
// Uses events from savesMessages
|
||||||
onMessageUpdate(msg: Message, oldMsg: Message) {
|
onMessageUpdate(savedMessage: SavedMessage, oldSavedMessage: SavedMessage) {
|
||||||
if (!msg.author) return;
|
if (oldSavedMessage && JSON.stringify(savedMessage.data) === JSON.stringify(oldSavedMessage.data)) return;
|
||||||
if (oldMsg && msg.content === oldMsg.content) return;
|
|
||||||
if (msg.type !== 0) 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, {
|
this.serverLogs.log(LogType.MESSAGE_EDIT, {
|
||||||
member: stripObjectToScalars(msg.member, ["user"]),
|
member: stripObjectToScalars(member, ["user"]),
|
||||||
channel: stripObjectToScalars(msg.channel),
|
channel: stripObjectToScalars(channel),
|
||||||
before: oldMsg ? oldMsg.cleanContent || oldMsg.content || "" : "Unavailable due to restart",
|
before,
|
||||||
after: msg.cleanContent || msg.content || ""
|
after
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@d.event("messageDelete")
|
// Uses events from savesMessages
|
||||||
onMessageDelete(msg: Message) {
|
onMessageDelete(savedMessage: SavedMessage) {
|
||||||
if (msg.type != null && msg.type !== 0) return;
|
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(
|
this.serverLogs.log(
|
||||||
LogType.MESSAGE_DELETE,
|
LogType.MESSAGE_DELETE,
|
||||||
{
|
{
|
||||||
member: stripObjectToScalars(msg.member, ["user"]),
|
member: stripObjectToScalars(member, ["user"]),
|
||||||
channel: stripObjectToScalars(msg.channel),
|
channel: stripObjectToScalars(channel),
|
||||||
messageText: msg.cleanContent || msg.content || ""
|
messageText: disableCodeBlocks(deactivateMentions(savedMessage.data.content || ""))
|
||||||
},
|
},
|
||||||
msg.id
|
savedMessage.id
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
this.serverLogs.log(
|
this.serverLogs.log(
|
||||||
LogType.MESSAGE_DELETE_BARE,
|
LogType.MESSAGE_DELETE_BARE,
|
||||||
{
|
{
|
||||||
messageId: msg.id,
|
messageId: savedMessage.id,
|
||||||
channel: stripObjectToScalars(msg.channel)
|
channel: stripObjectToScalars(channel)
|
||||||
},
|
},
|
||||||
msg.id
|
savedMessage.id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@d.event("messageDeleteBulk")
|
// Uses events from savesMessages
|
||||||
onMessageDeleteBulk(messages: Message[]) {
|
onMessageDeleteBulk(savedMessages: SavedMessage[]) {
|
||||||
|
const channel = this.guild.channels.get(savedMessages[0].channel_id);
|
||||||
|
|
||||||
this.serverLogs.log(
|
this.serverLogs.log(
|
||||||
LogType.MESSAGE_DELETE_BULK,
|
LogType.MESSAGE_DELETE_BULK,
|
||||||
{
|
{
|
||||||
count: messages.length,
|
count: savedMessages.length,
|
||||||
channel: messages[0] ? messages[0].channel : null
|
channel
|
||||||
},
|
},
|
||||||
messages[0] && messages[0].id
|
savedMessages[0].id
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ export class PostPlugin extends Plugin {
|
||||||
}
|
}
|
||||||
|
|
||||||
const createdMsg = await args.channel.createMessage(args.content);
|
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);
|
const edited = await this.bot.editMessage(savedMessage.channel_id, savedMessage.id, args.content);
|
||||||
await this.savedMessages.saveEditFromMsg(edited);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
src/utils.ts
16
src/utils.ts
|
@ -1,8 +1,9 @@
|
||||||
import at = require("lodash.at");
|
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 url from "url";
|
||||||
import tlds from "tlds";
|
import tlds from "tlds";
|
||||||
import emojiRegex from "emoji-regex";
|
import emojiRegex from "emoji-regex";
|
||||||
|
import { GuildSavedMessages } from "./data/GuildSavedMessages";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turns a "delay string" such as "1h30m" to milliseconds
|
* 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 unicodeEmojiRegex = emojiRegex();
|
||||||
export const customEmojiRegex = /<:(?:.*?):(\d+)>/g;
|
export const customEmojiRegex = /<:(?:.*?):(\d+)>/g;
|
||||||
export const anyEmojiRegex = new RegExp(
|
export const anyEmojiRegex = new RegExp(`(?:(?:${unicodeEmojiRegex.source})|(?:${customEmojiRegex.source}))`, "g");
|
||||||
`(?:(?:${unicodeEmojiRegex.source})|(?:${customEmojiRegex.source}))`,
|
|
||||||
"g"
|
|
||||||
);
|
|
||||||
|
|
||||||
export function getEmojiInString(str: string): string[] {
|
export function getEmojiInString(str: string): string[] {
|
||||||
return str.match(anyEmojiRegex) || [];
|
return str.match(anyEmojiRegex) || [];
|
||||||
|
@ -275,4 +273,12 @@ export function disableLinkPreviews(str: string): string {
|
||||||
return str.replace(/(?<!\<)(https?:\/\/\S+)/gi, "<$1>");
|
return str.replace(/(?<!\<)(https?:\/\/\S+)/gi, "<$1>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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";
|
export const DBDateFormat = "YYYY-MM-DD HH:mm:ss";
|
||||||
|
|
Loading…
Add table
Reference in a new issue