]> git.donarmstrong.com Git - infobot.git/commitdiff
news: can make news compulsory (chanset +newsNotifyAll)
authordms <dms@c11ca15a-4712-0410-83d8-924469b57eb5>
Fri, 13 Apr 2001 15:23:27 +0000 (15:23 +0000)
committerdms <dms@c11ca15a-4712-0410-83d8-924469b57eb5>
Fri, 13 Apr 2001 15:23:27 +0000 (15:23 +0000)
      and opt-out (news unnotify)
irchooks: splitted into IrcHelpers.pl so we can reload it on the fly.
factoids: added debugging for short factoids that may be botched up
          references

git-svn-id: https://svn.code.sf.net/p/infobot/code/trunk/blootbot@438 c11ca15a-4712-0410-83d8-924469b57eb5

src/IRC/IrcHelpers.pl [new file with mode: 0644]
src/IRC/IrcHooks.pl
src/IRC/Schedulers.pl
src/Modules/Factoids.pl
src/Modules/News.pl

diff --git a/src/IRC/IrcHelpers.pl b/src/IRC/IrcHelpers.pl
new file mode 100644 (file)
index 0000000..c4e4530
--- /dev/null
@@ -0,0 +1,280 @@
+#
+# 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;
index 461d9de730b69a5ba45c33c878bc8d6fc60d4b4e..d217fc5cfcd85549c841b17996e573e01057b9a2 100644 (file)
@@ -941,271 +941,4 @@ sub on_whoisuser {
     $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;
index b9d46239f22b3e98b744138395d17ec19751854c..e3e224c8b97ebbfceb0c011a4451124e2a91bc27 100644 (file)
@@ -324,7 +324,7 @@ sub newsFlush {
     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;
            }
index d6c20b4a1e382f1480e3c04136099435186303b6..46cbf8d0370e24b14650e2c4c1f6f77e99e900b1 100644 (file)
@@ -316,6 +316,11 @@ sub CmdFactStats {
            $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);
        }
index ab274d32edbb3ac750844963dcd39f0b78de32a1..6a4d30f0d591a60d64f11493d531250d8a624cc1 100644 (file)
@@ -63,30 +63,68 @@ sub Parse {
 
     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");
@@ -307,7 +345,13 @@ sub list {
     }
 
     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:");
@@ -610,9 +654,19 @@ sub latest {
        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});
 
@@ -627,7 +681,10 @@ sub latest {
     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;
     }
 
@@ -649,7 +706,12 @@ sub latest {
        &::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();
+       }
     }
 }