2 # IrcHooks.pl: IRC Hooks stuff.
6 # NOTE: Based on code by Kevin Lenzo & Patrick Cole (c) 1997
9 if (&IsParam("useStrict")) { use strict; }
11 #######################################################################
12 ####### IRC HOOK HELPERS IRC HOOK HELPERS IRC HOOK HELPERS ########
13 #######################################################################
16 # Usage: &hookMode($chan, $modes, @targets);
18 my ($chan, $modes, @targets) = @_;
21 $chan = lc $chan; # !!!.
24 foreach $mode (split(//, $modes)) {
26 if ($mode =~ /[-+]/) {
27 $parity = 1 if ($mode eq "+");
28 $parity = 0 if ($mode eq "-");
33 if ($mode =~ /[bklov]/) {
34 my $target = shift @targets;
37 $chanstats{$chan}{'Op'}++ if ($mode eq "o");
38 $chanstats{$chan}{'Ban'}++ if ($mode eq "b");
40 $chanstats{$chan}{'Deop'}++ if ($mode eq "o");
41 $chanstats{$chan}{'Unban'}++ if ($mode eq "b");
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;
51 $channels{$chan}{$mode} = $target if $parity;
52 delete $channels{$chan}{$mode} if !$parity;
56 # important channel modes, targetless.
57 if ($mode =~ /[mt]/) {
58 $channels{$chan}{$mode}++ if $parity;
59 delete $channels{$chan}{$mode} if !$parity;
65 ($msgType, $chan, $who, $message) = @_;
69 $orig{message} = $message;
73 $message =~ s/[\cA-\c_]//ig; # strip control characters
74 $message =~ s/^\s+//; # initial whitespaces.
75 $who =~ tr/A-Z/a-z/; # lowercase.
80 if ($msgType =~ /private/) {
85 # addressing revamped by the xk.
86 ### below needs to be fixed...
87 if (&IsParam("addressCharacter")) {
88 if ($message =~ s/^\Q$param{'addressCharacter'}\E//) {
94 if ($message =~ /^($mask{nick})([\;\:\>\, ]+) */) {
96 if ($1 =~ /^\Q$ident\E$/i) {
97 $message = $newmessage;
100 # ignore messages addressed to other people or unaddressed.
101 $skipmessage++ if ($2 ne "" and $2 !~ /^ /);
106 # Determine floodwho.
108 if ($msgType =~ /public/i) { # public.
109 $floodwho = $c = lc $chan;
110 } elsif ($msgType =~ /private/i) { # private.
113 &DEBUG("FIXME: floodwho = ???");
116 my $val = &getChanConfDefault("floodRepeat", "2:10", $c);
117 my ($count, $interval) = split /:/, $val;
119 # flood repeat protection.
121 my $time = $flood{$floodwho}{$message} || 0;
123 if ($msgType eq "public" and (time() - $time < $interval)) {
124 ### public != personal who so the below is kind of pointless.
126 foreach (keys %flood) {
127 next if (/^\Q$floodwho\E$/);
128 next if (defined $chan and /^\Q$chan\E$/);
130 push(@who, grep /^\Q$message\E$/i, keys %{ $flood{$_} });
133 return if ($lobotomized);
136 &msg($who, "you already said what ".
137 join(' ', @who)." have said.");
139 &msg($who,"Someone already said that ". (time - $time) ." seconds ago" );
142 ### TODO: delete old floodwarn{} keys.
144 if (!exists $floodwarn{$floodwho}) {
147 $floodwarn++ if (time() - $floodwarn{$floodwho} > $interval);
151 &status("FLOOD repetition detected from $floodwho.");
152 $floodwarn{$floodwho} = time();
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");
163 &status("$b_cyan$who$ob is addressing me");
166 $flood{$floodwho}{$message} = time();
167 } elsif ($msgType eq "public" and &IsChanConf("kickOnRepeat")) {
168 # unaddressed, public only.
170 ### TODO: use a separate "short-time" hash.
172 @data = keys %{ $flood{$floodwho} } if (exists $flood{$floodwho});
175 $val = &getChanConfDefault("floodMessages", "5:30", $c);
176 ($count, $interval) = split /:/, $val;
178 # flood overflow protection.
180 foreach (keys %{ $flood{$floodwho} }) {
181 next unless (time() - $flood{$floodwho}{$_} > $interval);
182 delete $flood{$floodwho}{$_};
185 my $i = scalar keys %{ $flood{$floodwho} };
187 &msg($who,"overflow of messages ($i > $count)");
188 &status("FLOOD overflow detected from $floodwho; ignoring");
190 my $expire = $param{'ignoreAutoExpire'} || 5;
191 &ignoreAdd("*!$uh", $chan, $expire, "flood overflow auto-detected.");
195 $flood{$floodwho}{$message} = time();
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;
208 &DEBUG("unknown msgType => $msgType.");
210 push(@ignore, keys %{ $ignore{"*"} }) if (exists $ignore{"*"});
212 if ((!$skipmessage or &IsChanConf("seenStoreAll")) and
213 &IsChanConf("seen") and
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'}++;
224 return if ($skipmessage);
225 return unless (&IsParam("minVolunteerLength") or $addressed);
230 next unless (eval { $nuh =~ /^$_$/i });
232 &status("IGNORE <$who> $message");
237 if (!defined $userHandle) {
238 &DEBUG("line 1074: need verifyUser?");
239 &verifyUser($who, $nuh);
242 &DEBUG("hookMsg: 'nuh' not defined?");
245 ### For extra debugging purposes...
246 if ($_ = &process()) {
247 # &DEBUG("IrcHooks: process returned '$_'.");
253 sub chanLimitVerify {
255 my $l = $channels{$chan}{'l'};
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);
263 my $delta = $count + $plus - $l;
267 &WARN("clc: stupid to have plus at $plus, fix it!");
270 if (exists $cache{chanlimitChange}{$chan}) {
271 if (time() - $cache{chanlimitChange}{$chan} < $int*60) {
276 &chanServCheck($chan);
278 ### todo: unify code with chanlimitcheck()
280 &status("clc: big change in limit; changing.");
281 &rawout("MODE $chan +l ".($count+$plus) );
282 $cache{chanlimitChange}{$chan} = time();
290 if (!defined $chan or $chan =~ /^$/) {
291 &WARN("chanServCheck: chan == NULL.");
295 if ($chan =~ tr/A-Z/a-z/) {
296 &DEBUG("chanServCheck: lowercased chan ($chan)");
299 if (! &IsChanConf("chanServ_ops") ) {
303 &DEBUG("chanServCheck($chan) called.");
305 if ( &IsParam("nickServ_pass") and !$nickserv) {
306 &DEBUG("chanServ_ops($chan): nickserv enabled but not alive? (ircCheck)");
309 return if (exists $channels{$chan}{'o'}{$ident});
311 &status("ChanServ ==> Requesting ops for $chan. (chanServCheck)");
312 &rawout("PRIVMSG ChanServ :OP $chan $ident");