3
0
Fork 0
mirror of https://github.com/ZeppelinBot/Zeppelin.git synced 2025-03-14 21:31:50 +00:00

fix(spam): count duplicate mentions; only clean offending messages

This commit is contained in:
Dragory 2018-08-01 19:13:32 +03:00
parent e8c021eea6
commit 847ee11195
2 changed files with 64 additions and 16 deletions

View file

@ -3,7 +3,9 @@ import { Message, TextChannel } from "eris";
import {
cleanMessagesInChannel,
getEmojiInString,
getRoleMentions,
getUrlsInString,
getUserMentions,
stripObjectToScalars
} from "../utils";
import { LogType } from "../data/LogType";
@ -24,10 +26,13 @@ interface IRecentAction {
type: RecentActionType;
userId: string;
channelId: string;
msg: Message;
timestamp: number;
count: number;
}
const MAX_INTERVAL = 300;
export class SpamPlugin extends Plugin {
protected logs: GuildLogs;
@ -63,6 +68,7 @@ export class SpamPlugin extends Plugin {
type: RecentActionType,
userId: string,
channelId: string,
msg: Message,
timestamp: number,
count = 1
) {
@ -70,11 +76,21 @@ export class SpamPlugin extends Plugin {
type,
userId,
channelId,
msg,
timestamp,
count
});
}
getRecentActions(type: RecentActionType, userId: string, channelId: string, since: number) {
return this.recentActions.filter(action => {
if (action.timestamp < since) return false;
if (action.type !== type) return false;
if (action.channelId !== channelId) return false;
return true;
});
}
getRecentActionCount(type: RecentActionType, userId: string, channelId: string, since: number) {
return this.recentActions.reduce((count, action) => {
if (action.timestamp < since) return count;
@ -92,7 +108,7 @@ export class SpamPlugin extends Plugin {
clearOldRecentActions() {
// TODO: Figure out expiry time from longest interval in the config?
const expiryTimestamp = Date.now() - 1000 * 60 * 5;
const expiryTimestamp = Date.now() - 1000 * MAX_INTERVAL;
this.recentActions = this.recentActions.filter(action => action.timestamp >= expiryTimestamp);
}
@ -105,26 +121,23 @@ export class SpamPlugin extends Plugin {
) {
if (actionCount === 0) return;
this.addRecentAction(type, msg.author.id, msg.channel.id, msg.timestamp, actionCount);
const recentMessagesCount = this.getRecentActionCount(
const since = msg.timestamp - 1000 * spamConfig.interval;
this.addRecentAction(type, msg.author.id, msg.channel.id, msg, msg.timestamp, actionCount);
const recentActionsCount = this.getRecentActionCount(
type,
msg.author.id,
msg.channel.id,
msg.timestamp - 1000 * spamConfig.interval
since
);
if (recentMessagesCount > spamConfig.count) {
if (recentActionsCount > spamConfig.count) {
if (spamConfig.clean !== false) {
const cleanCount =
type === RecentActionType.Message ? spamConfig.count : spamConfig.cleanCount || 20;
const recentActions = this.getRecentActions(type, msg.author.id, msg.channel.id, since);
const msgIds = recentActions.map(a => a.msg.id);
await this.bot.deleteMessages(msg.channel.id, msgIds);
await cleanMessagesInChannel(
this.bot,
msg.channel as TextChannel,
cleanCount,
msg.author.id,
"Spam detected"
);
this.logs.log(LogType.SPAM_DELETE, {
member: stripObjectToScalars(msg.member, ["user"]),
channel: stripObjectToScalars(msg.channel),
@ -135,6 +148,8 @@ export class SpamPlugin extends Plugin {
}
if (spamConfig.mute) {
// For muting the user, we use the ModActions plugin
// This means that spam mute functionality requires the ModActions plugin to be loaded
const guildData = this.knub.getGuildData(this.guildId);
const modActionsPlugin = guildData.loadedPlugins.get("mod_actions") as ModActionsPlugin;
if (!modActionsPlugin) return;
@ -176,12 +191,15 @@ export class SpamPlugin extends Plugin {
}
const maxMentions = this.configValueForMsg(msg, "max_mentions");
if (maxMentions && (msg.mentions.length || msg.roleMentions.length)) {
const mentions = msg.content
? [...getUserMentions(msg.content), ...getRoleMentions(msg.content)]
: [];
if (maxMentions && mentions.length) {
this.detectSpam(
msg,
RecentActionType.Mention,
maxMentions,
msg.mentions.length + msg.roleMentions.length,
mentions.length,
"too many mentions"
);
}

View file

@ -238,3 +238,33 @@ export function trimLines(str: string) {
export const emptyEmbedValue = "\u200b";
export const embedPadding = "\n" + emptyEmbedValue;
export const userMentionRegex = /<@!?([0-9]+)>/g;
export const roleMentionRegex = /<&([0-9]+)>/g;
export function getUserMentions(str: string) {
const regex = new RegExp(userMentionRegex.source, "g");
const userIds = [];
let match;
// tslint:disable-next-line
while ((match = regex.exec(str)) !== null) {
console.log("m", match);
userIds.push(match[1]);
}
return userIds;
}
export function getRoleMentions(str: string) {
const regex = new RegExp(roleMentionRegex.source, "g");
const roleIds = [];
let match;
// tslint:disable-next-line
while ((match = regex.exec(str)) !== null) {
roleIds.push(match[1]);
}
return roleIds;
}