2 # IrcHooks.pl: IRC Hooks stuff.
6 # NOTE: Based on code by Kevin Lenzo & Patrick Cole (c) 1997
9 #######################################################################
10 ####### IRC HOOK HELPERS IRC HOOK HELPERS IRC HOOK HELPERS ########
11 #######################################################################
14 # Usage: &hookMode($nick, $modes, @targets);
16 my ($nick, $modes, @targets) = @_;
19 if ($chan =~ tr/A-Z/a-z/) {
20 &VERB("hookMode: cased $chan.",2);
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;
49 # lets do some custom stuff.
50 if ($mode eq "o" and $parity) {
51 if ($nick eq "ChanServ" and $target =~ /^\Q$ident\E$/i) {
52 &VERB("hookmode: chanserv deopped us! asking",2);
53 &chanServCheck($chan);
56 &chanLimitVerify($chan);
61 $channels{$chan}{$mode} = $target if $parity;
62 delete $channels{$chan}{$mode} if !$parity;
66 # important channel modes, targetless.
67 if ($mode =~ /[mt]/) {
68 $channels{$chan}{$mode}++ if $parity;
69 delete $channels{$chan}{$mode} if !$parity;
75 ($msgType, $chan, $who, $message) = @_;
79 $orig{message} = $message;
83 $message =~ s/[\cA-\c_]//ig; # strip control characters
84 $message =~ s/^\s+//; # initial whitespaces.
85 $who =~ tr/A-Z/a-z/; # lowercase.
86 my $mynick = $conn->nick();
91 if ($msgType =~ /private/) {
94 if (&IsChanConf('addressCharacter') > 0) {
95 $addressCharacter = getChanConf('addressCharacter');
96 if ($message =~ s/^\Q$addressCharacter\E//) {
97 &msg($who, "The addressCharacter \"$addressCharacter\" is to get my attention in a normal channel. Please leave it off when messaging me directly.");
102 # addressing revamped by the xk.
103 ### below needs to be fixed...
104 if (&IsChanConf('addressCharacter') > 0) {
105 $addressCharacter = getChanConf('addressCharacter');
106 if ($message =~ s/^\Q$addressCharacter\E//) {
112 if ($message =~ /^($mask{nick})([\;\:\>\, ]+) */) {
114 if ($1 =~ /^\Q$mynick\E$/i) {
115 $message = $newmessage;
118 # ignore messages addressed to other people or unaddressed.
119 $skipmessage++ if ($2 ne "" and $2 !~ /^ /);
124 # Determine floodwho.
126 if ($msgType =~ /public/i) {
128 $floodwho = $c = lc $chan;
129 } elsif ($msgType =~ /private/i) {
134 &FIXME("floodwho = ???");
137 my $val = &getChanConfDefault("floodRepeat", "2:5", $c);
138 my ($count, $interval) = split /:/, $val;
140 # flood repeat protection.
142 my $time = $flood{$floodwho}{$message} || 0;
144 if (!&IsFlag('o') and $msgType eq "public" and (time() - $time < $interval)) {
145 ### public != personal who so the below is kind of pointless.
147 foreach (keys %flood) {
148 next if (/^\Q$floodwho\E$/);
149 next if (defined $chan and /^\Q$chan\E$/);
151 push(@who, grep /^\Q$message\E$/i, keys %{ $flood{$_} });
154 return if ($lobotomized);
157 push(@who,"Someone");
159 &msg($who,join(' ', @who)." already said that ". (time - $time) ." seconds ago" );
161 ### TODO: delete old floodwarn{} keys.
163 if (!exists $floodwarn{$floodwho}) {
166 $floodwarn++ if (time() - $floodwarn{$floodwho} > $interval);
170 &status("FLOOD repetition detected from $floodwho.");
171 $floodwarn{$floodwho} = time();
178 &status("$b_cyan$who$ob is short-addressing $mynick");
179 } elsif ($msgType eq "private") { # private.
180 &status("$b_cyan$who$ob is /msg'ing $mynick");
182 &status("$b_cyan$who$ob is addressing $mynick");
185 $flood{$floodwho}{$message} = time();
186 } elsif ($msgType eq "public" and &IsChanConf("kickOnRepeat") > 0) {
187 # unaddressed, public only.
189 ### TODO: use a separate "short-time" hash.
191 @data = keys %{ $flood{$floodwho} } if (exists $flood{$floodwho});
194 $val = &getChanConfDefault("floodMessages", "5:30", $c);
195 ($count, $interval) = split /:/, $val;
197 # flood overflow protection.
199 foreach (keys %{ $flood{$floodwho} }) {
200 next unless (time() - $flood{$floodwho}{$_} > $interval);
201 delete $flood{$floodwho}{$_};
204 my $i = scalar keys %{ $flood{$floodwho} };
206 my $expire = $param{'ignoreAutoExpire'} || 5;
208 # &msg($who,"overflow of messages ($i > $count)");
209 &msg($who,"Too many queries from you, ignoring for $expire minutes.");
210 &status("FLOOD overflow detected from $floodwho; ignoring");
212 &ignoreAdd("*!$uh", $chan, $expire, "flood overflow auto-detected.");
216 $flood{$floodwho}{$message} = time();
220 if ($msgType =~ /public/i) { # public.
221 $talkchannel = $chan;
222 &status("<$orig{who}/$chan> $orig{message}");
223 push(@ignore, keys %{ $ignore{$chan} }) if (exists $ignore{$chan});
224 } elsif ($msgType =~ /private/i) { # private.
225 &status("[$orig{who}] $orig{message}");
226 $talkchannel = undef;
229 &DEBUG("unknown msgType => $msgType.");
231 push(@ignore, keys %{ $ignore{'*'} }) if (exists $ignore{'*'});
233 if ((!$skipmessage or &IsChanConf('seenStoreAll') > 0) and
234 &IsChanConf('sed') > 0 and &IsChanConf('seen') > 0 and
235 $msgType =~ /public/ and
236 $orig{message} =~ /^s\/([^;\/]*)\/([^;\/]*)\/([g]*)$/) {
237 my $sedmsg = $seencache{$who}{'msg'};
238 eval "\$sedmsg =~ s/\Q$1\E/\Q$2\E/$3;";
239 if ($sedmsg ne $seencache{$who}{'msg'}) {
240 &DEBUG("sed \"" . $orig{message} . "\" \"" .
241 $seencache{$who}{'msg'} . "\" \"" . $sedmsg. "\"");
242 &msg($talkchannel, "$orig{who} meant: $sedmsg");
244 } elsif ((!$skipmessage or &IsChanConf('seenStoreAll') > 0) and
245 &IsChanConf('seen') > 0 and $msgType =~ /public/) {
246 $seencache{$who}{'time'} = time();
247 $seencache{$who}{'nick'} = $orig{who};
248 $seencache{$who}{'host'} = $uh;
249 $seencache{$who}{'chan'} = $talkchannel;
250 $seencache{$who}{'msg'} = $orig{message};
251 $seencache{$who}{'msgcount'}++;
253 if (&IsChanConf("minVolunteerLength") > 0) {
254 # FIXME hack to treat unaddressed as if using addrchar
257 return if ($skipmessage);
258 return unless ($addrchar or $addressed);
263 next unless (eval { $nuh =~ /^$_$/i } );
265 # better to ignore an extra message than to allow one to get
266 # through, although it would be better to go through ignore
268 if (time() - ($cache{ignoreCheckTime} || 0) > 60) {
272 &status("IGNORE <$who> $message");
277 if (!defined $userHandle) {
278 &DEBUG("line 1074: need verifyUser?");
279 &verifyUser($who, $nuh);
282 &DEBUG("hookMsg: 'nuh' not defined?");
285 ### For extra debugging purposes...
286 if ($_ = &process()) {
287 # &DEBUG("IrcHooks: process returned '$_'.");
290 # hack to remove +o from ppl with +O flag.
291 if (exists $users{$userHandle} && exists $users{$userHandle}{FLAGS} &&
292 $users{$userHandle}{FLAGS} =~ /O/
294 $users{$userHandle}{FLAGS} =~ s/o//g;
300 # this is basically run on on_join or on_quit
301 sub chanLimitVerify {
304 my $l = $channels{$chan}{'l'};
306 return unless (&IsChanConf("chanlimitcheck") > 0);
308 if (scalar keys %netsplit) {
309 &WARN("clV: netsplit active (1, chan = $chan); skipping.");
314 &DEBUG("$chan: running chanlimitCheck from chanLimitVerify.");
319 # only change it if it's not set.
320 my $plus = &getChanConfDefault("chanlimitcheckPlus", 5, $chan);
321 my $count = scalar(keys %{ $channels{$chan}{''} });
322 my $int = &getChanConfDefault("chanlimitcheckInterval", 10, $chan);
324 my $delta = $count + $plus - $l;
328 &WARN("clc: stupid to have plus at $plus, fix it!");
331 if (exists $cache{chanlimitChange}{$chan}) {
332 if (time() - $cache{chanlimitChange}{$chan} < $int*60) {
337 &chanServCheck($chan);
339 ### TODO: unify code with chanlimitcheck()
340 return if ($delta > 5);
342 &status("clc: big change in limit for $chan ($delta);".
343 "going for it. (was: $l; now: ".($count+$plus).")");
345 $conn->mode($chan, "+l", $count+$plus);
346 $cache{chanlimitChange}{$chan} = time();
352 if (!defined $chan or $chan =~ /^\s*$/) {
353 &WARN("chanServCheck: chan == NULL.");
357 if ($chan =~ tr/A-Z/a-z/) {
358 &DEBUG("chanServCheck: lowercased chan ($chan)");
361 if (! &IsChanConf("chanServ_ops") > 0) {
365 &VERB("chanServCheck($chan) called.",2);
367 if ( &IsParam("nickServ_pass") and !$nickserv) {
368 $conn->who("NickServ");
372 # check for first hash then for next hash.
373 # TODO: a function for &ischanop()? &isvoice()?
374 if (exists $channels{$chan} and exists $channels{$chan}{'o'}{$ident}) {
378 &status("ChanServ ==> Requesting ops for $chan. (chanServCheck)");
379 &rawout("PRIVMSG ChanServ :OP $chan $ident");