/** * Copyright (C) 2012 t7seven7t */ package net.t7seven7t.swornguard.detectors; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import net.dmulloy2.util.FormatUtil; import net.dmulloy2.util.Util; import net.t7seven7t.swornguard.SwornGuard; import net.t7seven7t.swornguard.events.CheatEvent; import net.t7seven7t.swornguard.types.CheatType; import net.t7seven7t.swornguard.types.PlayerData; import org.bukkit.entity.Player; import org.bukkit.scheduler.BukkitRunnable; /** * @author t7seven7t */ public class SpamDetector { private final SwornGuard plugin; private final String player; private final Map<String, Long> messages; private final Map<String, Long> commands; public SpamDetector(final SwornGuard plugin, final Player player) { this.plugin = plugin; this.messages = new HashMap<String, Long>(16, 0.75f); this.commands = new HashMap<String, Long>(16, 0.75f); this.player = player.getName(); } public Map<String, Long> getMessages(final ChatType type) { if (type == ChatType.COMMAND) return commands; return messages; } public boolean checkSpam(final String message, final ChatType type) { final long now = System.currentTimeMillis(); boolean cancelled = false; Map<String, Long> messages = getMessages(type); for (Iterator<Entry<String, Long>> i = messages.entrySet().iterator(); i.hasNext(); ) { Entry<String, Long> entry = i.next(); if (now - entry.getValue() > SpamOptions.MESSAGE_DECAY_TIME * 1000L) i.remove(); } if (messages.size() >= SpamOptions.SPAM_THRESHOLD * SpamOptions.MESSAGE_DECAY_TIME) { cancelled = true; new BukkitRunnable() { @Override public void run() { Player pl = Util.matchPlayer(player); PlayerData data = plugin.getPlayerDataCache().getData(pl); if (data == null) return; if (System.currentTimeMillis() - data.getLastSpamWarn() > 2000L) { data.setLastSpamWarn(System.currentTimeMillis()); CheatEvent event = new CheatEvent(pl, CheatType.SPAM, FormatUtil.format("[SPAMMER] {0} is trying to spam {1}!", pl.getName(), type.toString())); plugin.getCheatHandler().announceCheat(event); } } }.runTask(plugin); messages.clear(); } if (!cancelled && compareMessages(message, messages.keySet())) cancelled = true; messages.put(message, now); return cancelled; } public boolean compareMessages(String message, Set<String> messages) { message = message.toLowerCase(); for (String line : messages) { line = line.toLowerCase(); if (SpamOptions.COMPARISON_LEVEL > 0 && message.length() <= 2 && line.length() <= 2) return true; if (SpamOptions.COMPARISON_LEVEL > 1 && message.equals(line)) return true; if (SpamOptions.COMPARISON_LEVEL > 2 && line.length() >= 2 && message.startsWith(line.substring(0, 2))) return true; if (SpamOptions.COMPARISON_LEVEL > 3 && message.length() >= 3 && line.length() >= 3) { if (message.length() >= 6 && line.length() >= 6) { for (int i = 0; i < 3; i++) { if (compareStrings(message, line, i)) return true; } } else { if (compareStrings(message, line, 2)) return true; } } } return false; } public boolean compareStrings(String a, String b, int offset) { int sublen = (b.length() / 3) * offset; return (a.regionMatches(sublen, b, sublen, b.length() / 3)); } public enum ChatType { CHAT, COMMAND; } public static class SpamOptions { public static int SPAM_THRESHOLD; public static int MESSAGE_DECAY_TIME; public static int COMPARISON_LEVEL; public SpamOptions(final SwornGuard plugin) { MESSAGE_DECAY_TIME = plugin.getConfig().getInt("spamDetectorMessageDecayTime"); SPAM_THRESHOLD = plugin.getConfig().getInt("spamDetectorThresholdPerSecond"); COMPARISON_LEVEL = plugin.getConfig().getInt("spamDetectorComparisonLevel"); } } }