added simple spam/flood scanner to bot_NeonSpam
[NeonServV5.git] / src / event_neonspam_chanmsg.c
1 /* event_neonspam_chanmsg.c - NeonServ v5.1
2  * Copyright (C) 2011  Philipp Kreil (pk910)
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License 
15  * along with this program. If not, see <http://www.gnu.org/licenses/>. 
16  */
17
18 #define SPAMSERV_CHECK_IGNORE 0
19 #define SPAMSERV_CHECK_WARN   1
20 #define SPAMSERV_CHECK_PUNISH 2
21
22 static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message);
23 static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser);
24
25 static void neonspam_event_chanmsg(struct UserNode *user, struct ChanNode *chan, char *message) {
26     struct ClientSocket *client = getChannelBot(chan, BOTID);
27     if(!client) return; //we can't "see" this event
28     loadNeonSpamSettings(chan);
29     struct NeonSpamSettings *settings = chan->spam_settings;
30     struct ChanUser *chanuser = getChanUser(user, chan);
31     if(!settings || !chanuser) return;
32     
33     //ignore messages from ops/voices if we ignore them
34     if(!(settings->flags & SPAMSETTINGS_SCANOPS) && (chanuser->flags & CHANUSERFLAG_OPPED)) return;
35     if(!(settings->flags & SPAMSETTINGS_SCANVOICE) && (chanuser->flags & CHANUSERFLAG_VOICED)) return;
36     
37     //scan the message
38     int result = 0;
39     if(settings->flags & SPAMSETTINGS_SPAMSCAN) {
40         result = neonspam_spamscan(settings, chanuser, message);
41         switch(result) {
42             case SPAMSERV_CHECK_IGNORE:
43                 break;
44             case SPAMSERV_CHECK_WARN:
45                 reply(client, user, "SPAM WARNING!");
46                 break;
47             case SPAMSERV_CHECK_PUNISH:
48                 reply(client, user, "SPAM PUNISHMENT!");
49                 break;
50         }
51     }
52     if(settings->flags & SPAMSETTINGS_FLOODSCAN) {
53         result = neonspam_floodscan(settings, chanuser);
54         switch(result) {
55             case SPAMSERV_CHECK_IGNORE:
56                 break;
57             case SPAMSERV_CHECK_WARN:
58                 reply(client, user, "FLOOD WARNING!");
59                 break;
60             case SPAMSERV_CHECK_PUNISH:
61                 reply(client, user, "FLOOD PUNISHMENT!");
62                 break;
63         }
64     }
65     
66 }
67
68 static int neonspam_spamscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser, char *message) {
69     //crc32 hash of the message
70     unsigned long msghash = crc32(message);
71     if(chanuser->spamnode) {
72         if(chanuser->spamnode->lastmsg == msghash) {
73             chanuser->spamnode->spamcount++;
74             if(chanuser->spamnode->spamcount == settings->spam_amount)
75                 return SPAMSERV_CHECK_WARN;
76             else if(chanuser->spamnode->spamcount > settings->spam_amount)
77                 return SPAMSERV_CHECK_PUNISH;
78             else
79                 return SPAMSERV_CHECK_IGNORE;
80         }
81     } else
82         createSpamNode(chanuser);
83     chanuser->spamnode->lastmsg = msghash;
84     chanuser->spamnode->spamcount = 1;
85     return SPAMSERV_CHECK_IGNORE;
86 }
87
88 static int neonspam_update_penalty(struct NeonSpamSettings *settings, struct ChanUser *chanuser, int addmsg) {
89     int last_update = time(0) - chanuser->spamnode->last_penalty_update;
90     if(last_update) {
91         if(last_update < MAX_FLOOD_TIME && chanuser->spamnode->floodpenalty) {
92             chanuser->spamnode->floodpenalty -= last_update * (MAX_FLOOD_TIME / settings->flood_time);
93             if(chanuser->spamnode->floodpenalty < 0)
94                 chanuser->spamnode->floodpenalty = 0;
95         } else
96             chanuser->spamnode->floodpenalty = 0;
97         chanuser->spamnode->last_penalty_update = time(0);
98     }
99     chanuser->spamnode->floodpenalty += MAX_FLOOD_TIME * addmsg;
100     return chanuser->spamnode->floodpenalty / MAX_FLOOD_TIME + ((chanuser->spamnode->floodpenalty % MAX_FLOOD_TIME) ? 1 : 0);
101 }
102
103 static int neonspam_floodscan(struct NeonSpamSettings *settings, struct ChanUser *chanuser) {
104     if(!chanuser->spamnode)
105         createSpamNode(chanuser);
106     int messages_pending = neonspam_update_penalty(settings, chanuser, 1);
107     if(messages_pending == settings->flood_amount)
108         return SPAMSERV_CHECK_WARN;
109     else if(messages_pending > settings->flood_amount)
110         return SPAMSERV_CHECK_PUNISH;
111     else
112         return SPAMSERV_CHECK_IGNORE;
113 }
114
115