]> git.donarmstrong.com Git - infobot.git/blob - src/IRC/IrcHelpers.pl
9d654f90e9c63d36274f186e0436354fe06451c8
[infobot.git] / src / IRC / IrcHelpers.pl
1 #
2 # IrcHooks.pl: IRC Hooks stuff.
3 #      Author: dms
4 #     Version: 20010413
5 #     Created: 20010413
6 #        NOTE: Based on code by Kevin Lenzo & Patrick Cole  (c) 1997
7 #
8
9 if (&IsParam("useStrict")) { use strict; }
10
11 #######################################################################
12 ####### IRC HOOK HELPERS   IRC HOOK HELPERS   IRC HOOK HELPERS ########
13 #######################################################################
14
15 #####
16 # Usage: &hookMode($chan, $modes, @targets);
17 sub hookMode {
18     my ($chan, $modes, @targets) = @_;
19     my $parity  = 0;
20
21     $chan = lc $chan;           # !!!.
22
23     my $mode;
24     foreach $mode (split(//, $modes)) {
25         # sign.
26         if ($mode =~ /[-+]/) {
27             $parity = 1         if ($mode eq "+");
28             $parity = 0         if ($mode eq "-");
29             next;
30         }
31
32         # mode with target.
33         if ($mode =~ /[bklov]/) {
34             my $target = shift @targets;
35
36             if ($parity) {
37                 $chanstats{$chan}{'Op'}++    if ($mode eq "o");
38                 $chanstats{$chan}{'Ban'}++   if ($mode eq "b");
39             } else {
40                 $chanstats{$chan}{'Deop'}++  if ($mode eq "o");
41                 $chanstats{$chan}{'Unban'}++ if ($mode eq "b");
42             }
43
44             # modes w/ target affecting nick => cache it.
45             if ($mode =~ /[bov]/) {
46                 $channels{$chan}{$mode}{$target}++      if  $parity;
47                 delete $channels{$chan}{$mode}{$target} if !$parity;
48             }
49
50             if ($mode =~ /[l]/) {
51                 $channels{$chan}{$mode} = $target       if  $parity;
52                 delete $channels{$chan}{$mode}          if !$parity;
53             }
54         }
55
56         # important channel modes, targetless.
57         if ($mode =~ /[mt]/) {
58             $channels{$chan}{$mode}++                   if  $parity;
59             delete $channels{$chan}{$mode}              if !$parity;
60         }
61     }
62 }
63
64 sub hookMsg {
65     ($msgType, $chan, $who, $message) = @_;
66     my $skipmessage     = 0;
67     $addressed          = 0;
68     $addressedother     = 0;
69     $orig{message}      = $message;
70     $orig{who}          = $who;
71     $addrchar           = 0;
72
73     $message    =~ s/[\cA-\c_]//ig;     # strip control characters
74     $message    =~ s/^\s+//;            # initial whitespaces.
75     $who        =~ tr/A-Z/a-z/;         # lowercase.
76
77     &showProc();
78
79     # addressing.
80     if ($msgType =~ /private/) {
81         # private messages.
82         $addressed = 1;
83     } else {
84         # public messages.
85         # addressing revamped by the xk.
86         ### below needs to be fixed...
87         if (&IsParam("addressCharacter")) {
88             if ($message =~ s/^\Q$param{'addressCharacter'}\E//) {
89                 $addrchar  = 1;
90                 $addressed = 1;
91             }
92         }
93
94         if ($message =~ /^($mask{nick})([\;\:\>\, ]+) */) {
95             my $newmessage = $';
96             if ($1 =~ /^\Q$ident\E$/i) {
97                 $message   = $newmessage;
98                 $addressed = 1;
99             } else {
100                 # ignore messages addressed to other people or unaddressed.
101                 $skipmessage++ if ($2 ne "" and $2 !~ /^ /);
102             }
103         }
104     }
105
106     # Determine floodwho.
107     my $c       = "_default";
108     if ($msgType =~ /public/i) {                # public.
109         $floodwho = $c = lc $chan;
110     } elsif ($msgType =~ /private/i) {  # private.
111         $floodwho = lc $who;
112     } else {                            # dcc?
113         &DEBUG("FIXME: floodwho = ???");
114     }
115
116     my $val = &getChanConfDefault("floodRepeat", "2:10", $c);
117     my ($count, $interval) = split /:/, $val;
118
119     # flood repeat protection.
120     if ($addressed) {
121         my $time = $flood{$floodwho}{$message} || 0;
122
123         if ($msgType eq "public" and (time() - $time < $interval)) {
124             ### public != personal who so the below is kind of pointless.
125             my @who;
126             foreach (keys %flood) {
127                 next if (/^\Q$floodwho\E$/);
128                 next if (defined $chan and /^\Q$chan\E$/);
129
130                 push(@who, grep /^\Q$message\E$/i, keys %{ $flood{$_} });
131             }
132
133             return if ($lobotomized);
134
135             if (scalar @who) {
136                 &msg($who, "you already said what ".
137                                 join(' ', @who)." have said.");
138             } else {
139                 &msg($who,"Someone already said that ". (time - $time) ." seconds ago" );
140             }
141
142             ### TODO: delete old floodwarn{} keys.
143             my $floodwarn = 0;
144             if (!exists $floodwarn{$floodwho}) {
145                 $floodwarn++;
146             } else {
147                 $floodwarn++ if (time() - $floodwarn{$floodwho} > $interval);
148             }
149
150             if ($floodwarn) {
151                 &status("FLOOD repetition detected from $floodwho.");
152                 $floodwarn{$floodwho} = time();
153             }
154
155             return;
156         }
157
158         if ($addrchar) {
159             &status("$b_cyan$who$ob is short-addressing me");
160         } elsif ($msgType eq "private") {       # private.
161             &status("$b_cyan$who$ob is /msg'ing me");
162         } else {                                # public?
163             &status("$b_cyan$who$ob is addressing me");
164         }
165
166         $flood{$floodwho}{$message} = time();
167     } elsif ($msgType eq "public" and &IsChanConf("kickOnRepeat")) {
168         # unaddressed, public only.
169
170         ### TODO: use a separate "short-time" hash.
171         my @data;
172         @data   = keys %{ $flood{$floodwho} } if (exists $flood{$floodwho});
173     }
174
175     $val = &getChanConfDefault("floodMessages", "5:30", $c);
176     ($count, $interval) = split /:/, $val;
177
178     # flood overflow protection.
179     if ($addressed) {
180         foreach (keys %{ $flood{$floodwho} }) {
181             next unless (time() - $flood{$floodwho}{$_} > $interval);
182             delete $flood{$floodwho}{$_};
183         }
184
185         my $i = scalar keys %{ $flood{$floodwho} };
186         if ($i > $count) {
187             &msg($who,"overflow of messages ($i > $count)");
188             &status("FLOOD overflow detected from $floodwho; ignoring");
189
190             my $expire = $param{'ignoreAutoExpire'} || 5;
191             &ignoreAdd("*!$uh", $chan, $expire, "flood overflow auto-detected.");
192             return;
193         }
194
195         $flood{$floodwho}{$message} = time();
196     }
197
198     my @ignore;
199     if ($msgType =~ /public/i) {                    # public.
200         $talkchannel    = $chan;
201         &status("<$orig{who}/$chan> $orig{message}");
202         push(@ignore, keys %{ $ignore{$chan} }) if (exists $ignore{$chan});
203     } elsif ($msgType =~ /private/i) {             # private.
204         &status("[$orig{who}] $orig{message}");
205         $talkchannel    = undef;
206         $chan           = "_default";
207     } else {
208         &DEBUG("unknown msgType => $msgType.");
209     }
210     push(@ignore, keys %{ $ignore{"*"} }) if (exists $ignore{"*"});
211
212     if ((!$skipmessage or &IsChanConf("seenStoreAll")) and
213         &IsChanConf("seen") and
214         $msgType =~ /public/
215     ) {
216         $seencache{$who}{'time'} = time();
217         $seencache{$who}{'nick'} = $orig{who};
218         $seencache{$who}{'host'} = $uh;
219         $seencache{$who}{'chan'} = $talkchannel;
220         $seencache{$who}{'msg'}  = $orig{message};
221         $seencache{$who}{'msgcount'}++;
222     }
223
224     return if ($skipmessage);
225     return unless (&IsParam("minVolunteerLength") or $addressed);
226
227     foreach (@ignore) {
228         s/\*/\\S*/g;
229
230         next unless (eval { $nuh =~ /^$_$/i });
231
232         &status("IGNORE <$who> $message");
233         return;
234     }
235
236     if (defined $nuh) {
237         if (!defined $userHandle) {
238             &DEBUG("line 1074: need verifyUser?");
239             &verifyUser($who, $nuh);
240         }
241     } else {
242         &DEBUG("hookMsg: 'nuh' not defined?");
243     }
244
245 ### For extra debugging purposes...
246     if ($_ = &process()) {
247 #       &DEBUG("IrcHooks: process returned '$_'.");
248     }
249
250     return;
251 }
252
253 sub chanLimitVerify {
254     my($chan)   = @_;
255     my $l       = $channels{$chan}{'l'};
256
257     # only change it if it's not set.
258     if (defined $l and &IsChanConf("chanlimitcheck")) {
259         my $plus  = &getChanConfDefault("chanlimitcheckPlus", 5, $chan);
260         my $count = scalar(keys %{ $channels{$chan}{''} });
261         my $int   = &getChanConfDefault("chanlimitcheckInterval", 10, $chan);
262
263         my $delta = $count + $plus - $l;
264         $delta    =~ s/^\-//;
265
266         if ($plus <= 3) {
267             &WARN("clc: stupid to have plus at $plus, fix it!");
268         }
269
270         if (exists $cache{chanlimitChange}{$chan}) {
271             if (time() - $cache{chanlimitChange}{$chan} < $int*60) {
272                 return;
273             }
274         }
275
276         &chanServCheck($chan);
277
278         ### todo: unify code with chanlimitcheck()
279         if ($delta > 5) {
280             &status("clc: big change in limit; changing.");
281             &rawout("MODE $chan +l ".($count+$plus) );
282             $cache{chanlimitChange}{$chan} = time();
283         }
284     }
285 }
286
287 sub chanServCheck {
288     ($chan) = @_;
289
290     if (!defined $chan or $chan =~ /^$/) {
291         &WARN("chanServCheck: chan == NULL.");
292         return;
293     }
294
295     if ($chan =~ tr/A-Z/a-z/) {
296         &DEBUG("chanServCheck: lowercased chan ($chan)");
297     }
298
299     if (! &IsChanConf("chanServ_ops") ) {
300         return;
301     }
302
303     &DEBUG("chanServCheck($chan) called.");
304
305     if ( &IsParam("nickServ_pass") and !$nickserv) {
306         &DEBUG("chanServ_ops($chan): nickserv enabled but not alive? (ircCheck)");
307         return;
308     }
309     return if (exists $channels{$chan}{'o'}{$ident});
310
311     &status("ChanServ ==> Requesting ops for $chan. (chanServCheck)");
312     &rawout("PRIVMSG ChanServ :OP $chan $ident");
313 }
314
315 1;