added simple spam/flood scanner to bot_NeonSpam
[NeonServV5.git] / src / event_neonspam_chanmsg.c
diff --git a/src/event_neonspam_chanmsg.c b/src/event_neonspam_chanmsg.c
new file mode 100644 (file)
index 0000000..87ecdc6
--- /dev/null
@@ -0,0 +1,115 @@
+/* event_neonspam_chanmsg.c - NeonServ v5.1
+ * Copyright (C) 2011  Philipp Kreil (pk910)
+ * 
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ * 
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License 
+ * along with this program. If not, see <http://www.gnu.org/licenses/>. 
+ */
+
+#define SPAMSERV_CHECK_IGNORE 0
+#define SPAMSERV_CHECK_WARN   1
+#define SPAMSERV_CHECK_PUNISH 2
+
+static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message);
+static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser);
+
+static void neonspam_event_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) {
+    struct ClientSocket *client = getChannelBot(chan, BOTID);
+    if(!client) return; //we can't "see" this event
+    loadNeonSpamSettings(chan);
+    struct NeonSpamSettings *settings = chan->spam_settings;
+    struct ChanUser *chanuser = getChanUser(user, chan);
+    if(!settings || !chanuser) return;
+    
+    //ignore messages from ops/voices if we ignore them
+    if(!(settings->flags & SPAMSETTINGS_SCANOPS) && (chanuser->flags & CHANUSERFLAG_OPPED)) return;
+    if(!(settings->flags & SPAMSETTINGS_SCANVOICE) && (chanuser->flags & CHANUSERFLAG_VOICED)) return;
+    
+    //scan the message
+    int result = 0;
+    if(settings->flags & SPAMSETTINGS_SPAMSCAN) {
+        result = neonspam_spamscan(settings, chanuser, message);
+        switch(result) {
+            case SPAMSERV_CHECK_IGNORE:
+                break;
+            case SPAMSERV_CHECK_WARN:
+                reply(client, user, "SPAM WARNING!");
+                break;
+            case SPAMSERV_CHECK_PUNISH:
+                reply(client, user, "SPAM PUNISHMENT!");
+                break;
+        }
+    }
+    if(settings->flags & SPAMSETTINGS_FLOODSCAN) {
+        result = neonspam_floodscan(settings, chanuser);
+        switch(result) {
+            case SPAMSERV_CHECK_IGNORE:
+                break;
+            case SPAMSERV_CHECK_WARN:
+                reply(client, user, "FLOOD WARNING!");
+                break;
+            case SPAMSERV_CHECK_PUNISH:
+                reply(client, user, "FLOOD PUNISHMENT!");
+                break;
+        }
+    }
+    
+}
+
+static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
+    //crc32 hash of the message
+    unsigned long msghash = crc32(message);
+    if(chanuser->spamnode) {
+        if(chanuser->spamnode->lastmsg == msghash) {
+            chanuser->spamnode->spamcount++;
+            if(chanuser->spamnode->spamcount == settings->spam_amount)
+                return SPAMSERV_CHECK_WARN;
+            else if(chanuser->spamnode->spamcount > settings->spam_amount)
+                return SPAMSERV_CHECK_PUNISH;
+            else
+                return SPAMSERV_CHECK_IGNORE;
+        }
+    } else
+        createSpamNode(chanuser);
+    chanuser->spamnode->lastmsg = msghash;
+    chanuser->spamnode->spamcount = 1;
+    return SPAMSERV_CHECK_IGNORE;
+}
+
+static int neonspam_update_penalty(struct NeonSpamSettings *settings, struct ChanUser *chanuser, int addmsg) {
+    int last_update = time(0) - chanuser->spamnode->last_penalty_update;
+    if(last_update) {
+        if(last_update < MAX_FLOOD_TIME && chanuser->spamnode->floodpenalty) {
+            chanuser->spamnode->floodpenalty -= last_update * (MAX_FLOOD_TIME / settings->flood_time);
+            if(chanuser->spamnode->floodpenalty < 0)
+                chanuser->spamnode->floodpenalty = 0;
+        } else
+            chanuser->spamnode->floodpenalty = 0;
+        chanuser->spamnode->last_penalty_update = time(0);
+    }
+    chanuser->spamnode->floodpenalty += MAX_FLOOD_TIME * addmsg;
+    return chanuser->spamnode->floodpenalty / MAX_FLOOD_TIME + ((chanuser->spamnode->floodpenalty % MAX_FLOOD_TIME) ? 1 : 0);
+}
+
+static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser) {
+    if(!chanuser->spamnode)
+        createSpamNode(chanuser);
+    int messages_pending = neonspam_update_penalty(settings, chanuser, 1);
+    if(messages_pending == settings->flood_amount)
+        return SPAMSERV_CHECK_WARN;
+    else if(messages_pending > settings->flood_amount)
+        return SPAMSERV_CHECK_PUNISH;
+    else
+        return SPAMSERV_CHECK_IGNORE;
+}
+
+