import { decorators as d, Plugin } from "knub"; import { Invite, Message } from "eris"; import escapeStringRegexp from "escape-string-regexp"; import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; import { getInviteCodesInString, getUrlsInString, stripObjectToScalars } from "../utils"; import { ZalgoRegex } from "../data/Zalgo"; export class CensorPlugin extends Plugin { protected serverLogs: GuildLogs; getDefaultOptions() { return { config: { filter_zalgo: false, filter_invites: false, invite_guild_whitelist: null, invite_guild_blacklist: null, invite_code_whitelist: null, invite_code_blacklist: null, filter_domains: false, domain_whitelist: null, domain_blacklist: null, blocked_tokens: null, blocked_words: null, blocked_regex: null }, overrides: [ { level: ">=50", config: { filter_zalgo: false, filter_invites: false, filter_domains: false, blocked_tokens: null, blocked_words: null, blocked_regex: null } } ] }; } onLoad() { this.serverLogs = new GuildLogs(this.guildId); } async censorMessage(msg: Message, reason: string) { this.serverLogs.ignoreLog(LogType.MESSAGE_DELETE, msg.id); try { await msg.delete("Censored"); } catch (e) { return; } this.serverLogs.log(LogType.CENSOR, { member: stripObjectToScalars(msg.member, ["user"]), channel: stripObjectToScalars(msg.channel), reason, messageText: msg.cleanContent }); } async applyFiltersToMsg(msg: Message) { if (!msg.author || msg.author.bot) return; if (msg.type !== 0) return; if (!msg.content) return; // Filter zalgo if (this.configValueForMsg(msg, "filter_zalgo")) { const result = ZalgoRegex.exec(msg.content); if (result) { this.censorMessage(msg, `zalgo detected`); return; } } // Filter invites if (this.configValueForMsg(msg, "filter_invites")) { const inviteGuildWhitelist: string[] = this.configValueForMsg(msg, "invite_guild_whitelist"); const inviteGuildBlacklist: string[] = this.configValueForMsg(msg, "invite_guild_blacklist"); const inviteCodeWhitelist: string[] = this.configValueForMsg(msg, "invite_code_whitelist"); const inviteCodeBlacklist: string[] = this.configValueForMsg(msg, "invite_code_blacklist"); const inviteCodes = getInviteCodesInString(msg.content); let invites: Invite[] = await Promise.all( inviteCodes.map(code => { try { return this.bot.getInvite(code); } catch (e) { return null; } }) ); invites = invites.filter(v => !!v); for (const invite of invites) { if (inviteGuildWhitelist && !inviteGuildWhitelist.includes(invite.guild.id)) { this.censorMessage( msg, `invite guild (**${invite.guild.name}** \`${invite.guild.id}\`) not found in whitelist` ); return; } if (inviteGuildBlacklist && inviteGuildBlacklist.includes(invite.guild.id)) { this.censorMessage( msg, `invite guild (**${invite.guild.name}** \`${invite.guild.id}\`) found in blacklist` ); return; } if (inviteCodeWhitelist && !inviteCodeWhitelist.includes(invite.code)) { this.censorMessage(msg, `invite code (\`${invite.code}\`) not found in whitelist`); return; } if (inviteCodeBlacklist && inviteCodeBlacklist.includes(invite.code)) { this.censorMessage(msg, `invite code (\`${invite.code}\`) found in blacklist`); return; } } } // Filter domains if (this.configValueForMsg(msg, "filter_domains")) { const domainWhitelist: string[] = this.configValueForMsg(msg, "domain_whitelist"); const domainBlacklist: string[] = this.configValueForMsg(msg, "domain_blacklist"); const urls = getUrlsInString(msg.content); for (const thisUrl of urls) { if (domainWhitelist && !domainWhitelist.includes(thisUrl.hostname)) { this.censorMessage(msg, `domain (\`${thisUrl.hostname}\`) not found in whitelist`); return; } if (domainBlacklist && domainBlacklist.includes(thisUrl.hostname)) { this.censorMessage(msg, `domain (\`${thisUrl.hostname}\`) found in blacklist`); return; } } } // Filter tokens const blockedTokens = this.configValueForMsg(msg, "blocked_tokens") || []; for (const token of blockedTokens) { if (msg.content.includes(token)) { this.censorMessage(msg, `blocked token (\`${token}\`) found`); return; } } // Filter words const blockedWords = this.configValueForMsg(msg, "blocked_words") || []; for (const word of blockedWords) { const regex = new RegExp(`\\b${escapeStringRegexp(word)}\\b`, "i"); if (regex.test(msg.content)) { this.censorMessage(msg, `blocked word (\`${word}\`) found`); return; } } // Filter regex const blockedRegex = this.configValueForMsg(msg, "blocked_regex") || []; for (const regexStr of blockedRegex) { const regex = new RegExp(regexStr); if (regex.test(msg.content)) { this.censorMessage(msg, `blocked regex (\`${regexStr}\`) found`); return; } } } @d.event("messageCreate") async onMessageCreate(msg: Message) { this.applyFiltersToMsg(msg); } @d.event("messageUpdate") async onMessageUpdate(msg: Message) { this.applyFiltersToMsg(msg); } }