--- /dev/null
+#
+# IrcHooks.pl: IRC Hooks stuff.
+# Author: dms
+# Version: 20010413
+# Created: 20010413
+# NOTE: Based on code by Kevin Lenzo & Patrick Cole (c) 1997
+#
+
+if (&IsParam("useStrict")) { use strict; }
+
+#######################################################################
+####### IRC HOOK HELPERS IRC HOOK HELPERS IRC HOOK HELPERS ########
+#######################################################################
+
+#####
+# Usage: &hookMode($chan, $modes, @targets);
+sub hookMode {
+ my ($chan, $modes, @targets) = @_;
+ my $parity = 0;
+
+ $chan = lc $chan; # !!!.
+
+ my $mode;
+ foreach $mode (split(//, $modes)) {
+ # sign.
+ if ($mode =~ /[-+]/) {
+ $parity = 1 if ($mode eq "+");
+ $parity = 0 if ($mode eq "-");
+ next;
+ }
+
+ # mode with target.
+ if ($mode =~ /[bklov]/) {
+ my $target = shift @targets;
+
+ if ($parity) {
+ $chanstats{$chan}{'Op'}++ if ($mode eq "o");
+ $chanstats{$chan}{'Ban'}++ if ($mode eq "b");
+ } else {
+ $chanstats{$chan}{'Deop'}++ if ($mode eq "o");
+ $chanstats{$chan}{'Unban'}++ if ($mode eq "b");
+ }
+
+ # modes w/ target affecting nick => cache it.
+ if ($mode =~ /[bov]/) {
+ $channels{$chan}{$mode}{$target}++ if $parity;
+ delete $channels{$chan}{$mode}{$target} if !$parity;
+ }
+
+ if ($mode =~ /[l]/) {
+ $channels{$chan}{$mode} = $target if $parity;
+ delete $channels{$chan}{$mode} if !$parity;
+ }
+ }
+
+ # important channel modes, targetless.
+ if ($mode =~ /[mt]/) {
+ $channels{$chan}{$mode}++ if $parity;
+ delete $channels{$chan}{$mode} if !$parity;
+ }
+ }
+}
+
+sub hookMsg {
+ ($msgType, $chan, $who, $message) = @_;
+ my $skipmessage = 0;
+ $addressed = 0;
+ $addressedother = 0;
+ $orig{message} = $message;
+ $orig{who} = $who;
+ $addrchar = 0;
+
+ $message =~ s/[\cA-\c_]//ig; # strip control characters
+ $message =~ s/^\s+//; # initial whitespaces.
+ $who =~ tr/A-Z/a-z/; # lowercase.
+
+ &showProc();
+
+ # addressing.
+ if ($msgType =~ /private/) {
+ # private messages.
+ $addressed = 1;
+ } else {
+ # public messages.
+ # addressing revamped by the xk.
+ ### below needs to be fixed...
+ if (&IsParam("addressCharacter")) {
+ if ($message =~ s/^$param{'addressCharacter'}//) {
+ $addrchar = 1;
+ $addressed = 1;
+ }
+ }
+
+ if ($message =~ /^($mask{nick})([\;\:\>\, ]+) */) {
+ my $newmessage = $';
+ if ($1 =~ /^\Q$ident\E$/i) {
+ $message = $newmessage;
+ $addressed = 1;
+ } else {
+ # ignore messages addressed to other people or unaddressed.
+ $skipmessage++ if ($2 ne "" and $2 !~ /^ /);
+ }
+ }
+ }
+
+ # Determine floodwho.
+ my $c = "_default";
+ if ($msgType =~ /public/i) { # public.
+ $floodwho = $c = lc $chan;
+ } elsif ($msgType =~ /private/i) { # private.
+ $floodwho = lc $who;
+ } else { # dcc?
+ &DEBUG("FIXME: floodwho = ???");
+ }
+
+ my $val = &getChanConfDefault("floodRepeat", "2:10", $c);
+ my ($count, $interval) = split /:/, $val;
+
+ # flood repeat protection.
+ if ($addressed) {
+ my $time = $flood{$floodwho}{$message} || 0;
+
+ if ($msgType eq "public" and (time() - $time < $interval)) {
+ ### public != personal who so the below is kind of pointless.
+ my @who;
+ foreach (keys %flood) {
+ next if (/^\Q$floodwho\E$/);
+ next if (defined $chan and /^\Q$chan\E$/);
+
+ push(@who, grep /^\Q$message\E$/i, keys %{ $flood{$_} });
+ }
+
+ return if ($lobotomized);
+
+ if (scalar @who) {
+ &msg($who, "you already said what ".
+ join(' ', @who)." have said.");
+ } else {
+ &msg($who,"Someone already said that ". (time - $time) ." seconds ago" );
+ }
+
+ ### TODO: delete old floodwarn{} keys.
+ my $floodwarn = 0;
+ if (!exists $floodwarn{$floodwho}) {
+ $floodwarn++;
+ } else {
+ $floodwarn++ if (time() - $floodwarn{$floodwho} > $interval);
+ }
+
+ if ($floodwarn) {
+ &status("FLOOD repetition detected from $floodwho.");
+ $floodwarn{$floodwho} = time();
+ }
+
+ return;
+ }
+
+ if ($addrchar) {
+ &status("$b_cyan$who$ob is short-addressing me");
+ } elsif ($msgType eq "private") { # private.
+ &status("$b_cyan$who$ob is /msg'ing me");
+ } else { # public?
+ &status("$b_cyan$who$ob is addressing me");
+ }
+
+ $flood{$floodwho}{$message} = time();
+ } elsif ($msgType eq "public" and &IsChanConf("kickOnRepeat")) {
+ # unaddressed, public only.
+
+ ### TODO: use a separate "short-time" hash.
+ my @data;
+ @data = keys %{ $flood{$floodwho} } if (exists $flood{$floodwho});
+ }
+
+ $val = &getChanConfDefault("floodMessages", "5:30", $c);
+ ($count, $interval) = split /:/, $val;
+
+ # flood overflow protection.
+ if ($addressed) {
+ foreach (keys %{$flood{$floodwho}}) {
+ next unless (time() - $flood{$floodwho}{$_} > $interval);
+ delete $flood{$floodwho}{$_};
+ }
+
+ my $i = scalar keys %{$flood{$floodwho}};
+ if ($i > $count) {
+ &msg($who,"overflow of messages ($i > $count)");
+ &status("FLOOD overflow detected from $floodwho; ignoring");
+
+ my $expire = $param{'ignoreAutoExpire'} || 5;
+ &ignoreAdd("*!$uh", $chan, $expire, "flood overflow auto-detected.");
+ return;
+ }
+
+ $flood{$floodwho}{$message} = time();
+ }
+
+ my @ignore;
+ if ($msgType =~ /public/i) { # public.
+ $talkchannel = $chan;
+ &status("<$orig{who}/$chan> $orig{message}");
+ push(@ignore, keys %{ $ignore{$chan} }) if (exists $ignore{$chan});
+ } elsif ($msgType =~ /private/i) { # private.
+ &status("[$orig{who}] $orig{message}");
+ $talkchannel = undef;
+ $chan = "_default";
+ } else {
+ &DEBUG("unknown msgType => $msgType.");
+ }
+ push(@ignore, keys %{ $ignore{"*"} }) if (exists $ignore{"*"});
+
+ if ((!$skipmessage or &IsChanConf("seenStoreAll")) and
+ &IsChanConf("seen") and
+ $msgType =~ /public/
+ ) {
+ $seencache{$who}{'time'} = time();
+ $seencache{$who}{'nick'} = $orig{who};
+ $seencache{$who}{'host'} = $uh;
+ $seencache{$who}{'chan'} = $talkchannel;
+ $seencache{$who}{'msg'} = $orig{message};
+ $seencache{$who}{'msgcount'}++;
+ }
+
+ return if ($skipmessage);
+ return unless (&IsParam("minVolunteerLength") or $addressed);
+
+ foreach (@ignore) {
+ s/\*/\\S*/g;
+
+ next unless (eval { $nuh =~ /^$_$/i });
+
+ &status("IGNORE <$who> $message");
+ return;
+ }
+
+ if (defined $nuh) {
+ if (!defined $userHandle) {
+ &DEBUG("line 1074: need verifyUser?");
+ &verifyUser($who, $nuh);
+ }
+ } else {
+ &DEBUG("hookMsg: 'nuh' not defined?");
+ }
+
+### For extra debugging purposes...
+ if ($_ = &process()) {
+# &DEBUG("IrcHooks: process returned '$_'.");
+ }
+
+ return;
+}
+
+sub chanLimitVerify {
+ my($chan) = @_;
+ my $l = $channels{$chan}{'l'};
+
+ # only change it if it's not set.
+ if (defined $l and &IsChanConf("chanlimitcheck")) {
+ my $plus = &getChanConfDefault("chanlimitcheckPlus", 5, $chan);
+ my $count = scalar(keys %{ $channels{$chan}{''} });
+ &DEBUG("plus = $plus, count = $count");
+ my $delta = $count + $plus - $l;
+ $delta =~ s/^\-//;
+ &DEBUG(" delta => $delta");
+
+ if ($plus <= 3) {
+ &WARN("clc: stupid to have plus at $plus, fix it!");
+ }
+
+ ### todo: check if we have ops.
+ ### todo: if not, check if nickserv/chanserv is avail.
+ ### todo: unify code with chanlimitcheck()
+ if ($delta > 5) {
+ &status("clc: big change in limit; changing.");
+ &rawout("MODE $chan +l ".($count+$plus) );
+ }
+ }
+}
+
+1;
$nuh{lc $args[1]} = $args[1]."!".$args[2]."\@".$args[3];
}
-#######################################################################
-####### IRC HOOK HELPERS IRC HOOK HELPERS IRC HOOK HELPERS ########
-#######################################################################
-
-#####
-# Usage: &hookMode($chan, $modes, @targets);
-sub hookMode {
- my ($chan, $modes, @targets) = @_;
- my $parity = 0;
-
- $chan = lc $chan; # !!!.
-
- my $mode;
- foreach $mode (split(//, $modes)) {
- # sign.
- if ($mode =~ /[-+]/) {
- $parity = 1 if ($mode eq "+");
- $parity = 0 if ($mode eq "-");
- next;
- }
-
- # mode with target.
- if ($mode =~ /[bklov]/) {
- my $target = shift @targets;
-
- if ($parity) {
- $chanstats{$chan}{'Op'}++ if ($mode eq "o");
- $chanstats{$chan}{'Ban'}++ if ($mode eq "b");
- } else {
- $chanstats{$chan}{'Deop'}++ if ($mode eq "o");
- $chanstats{$chan}{'Unban'}++ if ($mode eq "b");
- }
-
- # modes w/ target affecting nick => cache it.
- if ($mode =~ /[bov]/) {
- $channels{$chan}{$mode}{$target}++ if $parity;
- delete $channels{$chan}{$mode}{$target} if !$parity;
- }
-
- if ($mode =~ /[l]/) {
- $channels{$chan}{$mode} = $target if $parity;
- delete $channels{$chan}{$mode} if !$parity;
- }
- }
-
- # important channel modes, targetless.
- if ($mode =~ /[mt]/) {
- $channels{$chan}{$mode}++ if $parity;
- delete $channels{$chan}{$mode} if !$parity;
- }
- }
-}
-
-sub hookMsg {
- ($msgType, $chan, $who, $message) = @_;
- my $skipmessage = 0;
- $addressed = 0;
- $addressedother = 0;
- $orig{message} = $message;
- $orig{who} = $who;
- $addrchar = 0;
-
- $message =~ s/[\cA-\c_]//ig; # strip control characters
- $message =~ s/^\s+//; # initial whitespaces.
- $who =~ tr/A-Z/a-z/; # lowercase.
-
- &showProc();
-
- # addressing.
- if ($msgType =~ /private/) {
- # private messages.
- $addressed = 1;
- } else {
- # public messages.
- # addressing revamped by the xk.
- ### below needs to be fixed...
- if (&IsParam("addressCharacter")) {
- if ($message =~ s/^$param{'addressCharacter'}//) {
- $addrchar = 1;
- $addressed = 1;
- }
- }
-
- if ($message =~ /^($mask{nick})([\;\:\>\, ]+) */) {
- my $newmessage = $';
- if ($1 =~ /^\Q$ident\E$/i) {
- $message = $newmessage;
- $addressed = 1;
- } else {
- # ignore messages addressed to other people or unaddressed.
- $skipmessage++ if ($2 ne "" and $2 !~ /^ /);
- }
- }
- }
-
- # Determine floodwho.
- my $c = "_default";
- if ($msgType =~ /public/i) { # public.
- $floodwho = $c = lc $chan;
- } elsif ($msgType =~ /private/i) { # private.
- $floodwho = lc $who;
- } else { # dcc?
- &DEBUG("FIXME: floodwho = ???");
- }
-
- my $val = &getChanConfDefault("floodRepeat", "2:10", $c);
- my ($count, $interval) = split /:/, $val;
-
- # flood repeat protection.
- if ($addressed) {
- my $time = $flood{$floodwho}{$message} || 0;
-
- if ($msgType eq "public" and (time() - $time < $interval)) {
- ### public != personal who so the below is kind of pointless.
- my @who;
- foreach (keys %flood) {
- next if (/^\Q$floodwho\E$/);
- next if (defined $chan and /^\Q$chan\E$/);
-
- push(@who, grep /^\Q$message\E$/i, keys %{ $flood{$_} });
- }
-
- return if ($lobotomized);
-
- if (scalar @who) {
- &msg($who, "you already said what ".
- join(' ', @who)." have said.");
- } else {
- &msg($who,"Someone already said that ". (time - $time) ." seconds ago" );
- }
-
- ### TODO: delete old floodwarn{} keys.
- my $floodwarn = 0;
- if (!exists $floodwarn{$floodwho}) {
- $floodwarn++;
- } else {
- $floodwarn++ if (time() - $floodwarn{$floodwho} > $interval);
- }
-
- if ($floodwarn) {
- &status("FLOOD repetition detected from $floodwho.");
- $floodwarn{$floodwho} = time();
- }
-
- return;
- }
-
- if ($addrchar) {
- &status("$b_cyan$who$ob is short-addressing me");
- } elsif ($msgType eq "private") { # private.
- &status("$b_cyan$who$ob is /msg'ing me");
- } else { # public?
- &status("$b_cyan$who$ob is addressing me");
- }
-
- $flood{$floodwho}{$message} = time();
- } elsif ($msgType eq "public" and &IsChanConf("kickOnRepeat")) {
- # unaddressed, public only.
-
- ### TODO: use a separate "short-time" hash.
- my @data;
- @data = keys %{ $flood{$floodwho} } if (exists $flood{$floodwho});
- }
-
- $val = &getChanConfDefault("floodMessages", "5:30", $c);
- ($count, $interval) = split /:/, $val;
-
- # flood overflow protection.
- if ($addressed) {
- foreach (keys %{$flood{$floodwho}}) {
- next unless (time() - $flood{$floodwho}{$_} > $interval);
- delete $flood{$floodwho}{$_};
- }
-
- my $i = scalar keys %{$flood{$floodwho}};
- if ($i > $count) {
- &msg($who,"overflow of messages ($i > $count)");
- &status("FLOOD overflow detected from $floodwho; ignoring");
-
- my $expire = $param{'ignoreAutoExpire'} || 5;
- &ignoreAdd("*!$uh", $chan, $expire, "flood overflow auto-detected.");
- return;
- }
-
- $flood{$floodwho}{$message} = time();
- }
-
- my @ignore;
- if ($msgType =~ /public/i) { # public.
- $talkchannel = $chan;
- &status("<$orig{who}/$chan> $orig{message}");
- push(@ignore, keys %{ $ignore{$chan} }) if (exists $ignore{$chan});
- } elsif ($msgType =~ /private/i) { # private.
- &status("[$orig{who}] $orig{message}");
- $talkchannel = undef;
- $chan = "_default";
- } else {
- &DEBUG("unknown msgType => $msgType.");
- }
- push(@ignore, keys %{ $ignore{"*"} }) if (exists $ignore{"*"});
-
- if ((!$skipmessage or &IsChanConf("seenStoreAll")) and
- &IsChanConf("seen") and
- $msgType =~ /public/
- ) {
- $seencache{$who}{'time'} = time();
- $seencache{$who}{'nick'} = $orig{who};
- $seencache{$who}{'host'} = $uh;
- $seencache{$who}{'chan'} = $talkchannel;
- $seencache{$who}{'msg'} = $orig{message};
- $seencache{$who}{'msgcount'}++;
- }
-
- return if ($skipmessage);
- return unless (&IsParam("minVolunteerLength") or $addressed);
-
- foreach (@ignore) {
- s/\*/\\S*/g;
-
- next unless (eval { $nuh =~ /^$_$/i });
-
- &status("IGNORE <$who> $message");
- return;
- }
-
- if (defined $nuh) {
- if (!defined $userHandle) {
- &DEBUG("line 1074: need verifyUser?");
- &verifyUser($who, $nuh);
- }
- } else {
- &DEBUG("hookMsg: 'nuh' not defined?");
- }
-
-### For extra debugging purposes...
- if ($_ = &process()) {
-# &DEBUG("IrcHooks: process returned '$_'.");
- }
-
- return;
-}
-
-sub chanLimitVerify {
- my($chan) = @_;
- my $l = $channels{$chan}{'l'};
-
- # only change it if it's not set.
- if (defined $l and &IsChanConf("chanlimitcheck")) {
- my $plus = &getChanConfDefault("chanlimitcheckPlus", 5, $chan);
- my $nowl = scalar(keys %{ $channels{$chan}{''} });
- my $delta = $nowl - $l;
- $delta =~ s/^\-//;
-
- if ($plus <= 3) {
- &WARN("clc: stupid to have plus at $plus, fix it!");
- }
-
- ### todo: check if we have ops.
- ### todo: if not, check if nickserv/chanserv is avail.
- ### todo: unify code with chanlimitcheck()
- if ($delta > 5) {
- &status("clc: big change in limit; changing.");
- &rawout("MODE $chan +l ".($nowl+$plus) );
- }
- }
-}
-
1;
foreach $chan (keys %::newsuser) {
foreach (keys %{ $::newsuser{$chan} }) {
my $t = $::newsuser{$chan}{$_};
- if (!defined $t or $t < 1000) {
+ if (!defined $t or ($t > 2 and $t < 1000)) {
&DEBUG("something wrong with newsuser{$chan}{$_} => $t");
next;
}
$match++ if ($val =~ /\s{3,}/);
next unless ($match);
+ my $v = &getFactoid($val);
+ if (defined $v) {
+ &DEBUG("key $key => $val => $v");
+ }
+
$key =~ s/\,/\037\,\037/g;
push(@list, $key);
}
if ($what =~ /^add(\s+(.*))?$/i) {
&add($2);
+
} elsif ($what =~ /^del(\s+(.*))?$/i) {
&del($2);
+
} elsif ($what =~ /^mod(\s+(.*))?$/i) {
&mod($2);
+
} elsif ($what =~ /^set(\s+(.*))?$/i) {
&set($2);
+
} elsif ($what =~ /^(\d)$/i) {
&::DEBUG("read shortcut called.");
&read($1);
+
} elsif ($what =~ /^read(\s+(.*))?$/i) {
&read($2);
+
} elsif ($what =~ /^(latest|new)(\s+(.*))?$/i) {
&::DEBUG("latest called... hrm");
&latest($3 || $chan, 1);
+
} elsif ($what =~ /^list$/i) {
&::DEBUG("list longcut called.");
&list();
+
} elsif ($what =~ /^(expire|text|desc)(\s+(.*))?$/i) {
# shortcut/link.
# nice hack.
my($arg1,$arg2) = split(/\s+/, $3, 2);
&set("$arg1 $1 $arg2");
+
} elsif ($what =~ /^help(\s+(.*))?$/i) {
&::help("news$1");
+
+ } elsif ($what =~ /^(un)?notify$/i) {
+ my $state = ($1) ? 0 : 1;
+
+ # todo: don't notify even if "news" is called.
+ if (!&::IsChanConf("newsNotifyAll")) {
+ &::msg($::who, "not available for this channel or disabled altogether.");
+ return;
+ }
+
+ my $t = $::newsuser{$chan}{$::who};
+ if ($state) { # state = 1
+ if (defined $t and ($t == 0 or $t == -1)) {
+ &::msg($::who, "enabled notify.");
+ delete $::newsuser{$chan}{$::who};
+ return;
+ }
+ &::msg($::who, "already enabled.");
+
+ } else { # state = 0
+ my $x = $::newsuser{$chan}{$::who};
+ if (defined $x and ($x == 0 or $x == -1)) {
+ &::msg($::who, "notify already disabled");
+ return;
+ }
+ $::newsuser{$chan}{$::who} = -1;
+ &::msg($::who, "notify is now disabled.");
+ }
+
} else {
&::DEBUG("could not parse '$what'.");
&::msg($::who, "unknown command: $what");
}
if (&::IsChanConf("newsKeepRead")) {
- $::newsuser{$chan}{$::who} = time();
+ my $x = $::newsuser{$chan}{$::who};
+
+ if (defined $x and ($x == 0 or $x == -1)) {
+ &::DEBUG("not updating time for $::who.");
+ } else {
+ $::newsuser{$chan}{$::who} = time();
+ }
}
&::msg($::who, "|==== News for \002$chan\002:");
return;
}
+ my $t = $::newsuser{$chan}{$::who};
+ if (defined $t and ($t == 0 or $t == -1)) {
+ &::DEBUG("not displaying any new news for $::who");
+ return;
+ }
+
my @new;
foreach (keys %{ $::news{$chan} }) {
- my $t = $::newsuser{$chan}{$::who};
+ if (&::IsChanConf("newsNotifyAll") and !defined $t) {
+ &::DEBUG("setting time for $::who to 1...");
+ $::newsuser{$chan}{$::who} = 1;
+ $t = 1;
+ }
next if (!defined $t);
next if ($t > $::news{$chan}{$_}{Time});
if (!$flag) {
my $unread = scalar @new;
my $total = scalar keys %{ $::news{$chan} };
- &::msg($::who, "There are unread news in $chan ($unread unread, $total, total). /msg $::ident news latest");
+ return unless ($unread);
+
+ &::msg($::who, "There are unread news in $chan ($unread unread, $total total). /msg $::ident news latest. If you don't want further news notification, /msg $::ident news unnotify");
+
return;
}
&::msg($::who, "|= to read, do 'news read <#>' or 'news read <keyword>'");
# lame hack to prevent dupes if we just ignore it.
- $::newsuser{$chan}{$::who} = time();
+ my $x = $::newsuser{$chan}{$::who};
+ if (defined $x and ($x == 0 or $x == -1)) {
+ &::DEBUG("not updating time for $::who. (2)");
+ } else {
+ $::newsuser{$chan}{$::who} = time();
+ }
}
}