]> git.donarmstrong.com Git - infobot.git/blobdiff - src/Modules/News.pl
news: can make news compulsory (chanset +newsNotifyAll)
[infobot.git] / src / Modules / News.pl
index c21799aa363bce36347936d41adf1eea397c1142..6a4d30f0d591a60d64f11493d531250d8a624cc1 100644 (file)
@@ -1,17 +1,17 @@
 #
 # News.pl: Advanced news management
 #   Author: dms
-#  Version: v0.2 (20010326)
+#  Version: v0.3 (20014012)
 #  Created: 20010326
 #    Notes: Testing done by greycat, kudos!
 #
 ### structure:
-# news{ channel }{ string } { items }
+# news{ channel }{ string } { item }
 # newsuser{ channel }{ user } = time()
-### where items is:
+### where item is:
 #      Time    - when it was added (used for sorting)
 #      Author  - Who by.
-#      Expire  - Time to expire
+#      Expire  - Time to expire.
 #      Text    - Actual text.
 ###
 
@@ -21,20 +21,29 @@ sub Parse {
     my($what)  = @_;
     $chan      = undef;
 
-    if ($::msgType eq "private") {
-       # todo: check if the channel exists aswell.
-       if (defined $what and $what =~ s/^($::mask{chan})\s*//) {
-           $chan = $1;
+    if (!keys %::news) {
+       if (!exists $::cache{newsFirst}) {
+           &::DEBUG("looks like we enabled news option just then; loading up news file just in case.");
+           $::cache{newsFirst} = 1;
        }
-    } else {
+
+       &readNews();
+    }
+
+    if ($::msgType ne "private") {
        $chan = $::chan;
     }
 
+    if (defined $what and $what =~ s/^($::mask{chan})\s*//) {
+       # todo: check if the channel exists aswell.
+       $chan = $1;
+    }
+
     if (!defined $chan) {
        my @chans = &::GetNickInChans($::who);
 
        if (scalar @chans > 1) {
-           &::msg($::who, "error: I dunno which channel you are referring to since you're on more than one.");
+           &::msg($::who, "error: I dunno which channel you are referring to since you're on more than one. Try 'news #chan ...' instead");
            return;
        }
 
@@ -54,27 +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 Text $arg2");
+       &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");
@@ -106,6 +156,7 @@ sub readNews {
                &::DEBUG("!defined item, never happen!");
                next;
            }
+
            $::news{$chan}{$item}{$1} = $2;
            next;
        }
@@ -130,6 +181,11 @@ sub readNews {
 }
 
 sub writeNews {
+    if (!scalar keys %::news) {
+       &::DEBUG("wN: nothing to write.");
+       return;
+    }
+
     my $file = "$::bot_base_dir/blootbot-news.txt";
 
     if (fileno NEWS) {
@@ -140,6 +196,7 @@ sub writeNews {
     # todo: add commands to output file.
     my $c = 0;
     my($cc,$ci,$cu) = (0,0,0);
+
     open(NEWS, ">$file");
     foreach $chan (sort keys %::news) {
        $c = scalar keys %{ $::news{$chan} };
@@ -163,11 +220,10 @@ sub writeNews {
     if (&::getChanConfList("newsKeepRead")) {
        # old users are removed in newsFlush(), perhaps it should be
        # done here.
-       &::DEBUG("newsuser cache...");
+
        foreach $chan (sort keys %::newsuser) {
-           &::DEBUG("newsuser cache: chan => $chan");
+
            foreach (sort keys %{ $::newsuser{$chan} }) {
-               &::DEBUG("newsuser cache: user => $_");
                print NEWS "U $chan $_ $::newsuser{$chan}{$_}\n";
                $cu++;
            }
@@ -203,13 +259,15 @@ sub add {
     $::news{$chan}{$str}{Author}       = $::who;
 
     my $agestr = &::Time2String($::news{$chan}{$str}{Expire} - time() );
-    my $item   = &getNewsItem($str);
+    my $item   = &newsS2N($str);
     &::msg($::who, "Added '\037$str\037' at [".localtime(time).
                "] by \002$::who\002 for item #\002$item\002.");
     &::msg($::who, "Now do 'news text $item <your_description>'");
     &::msg($::who, "This item will expire at \002".
-       localtime($::news{$chan}{$str}{Expire})."\002 [$agestr] "
+       localtime($::news{$chan}{$str}{Expire})."\002 [$agestr from now] "
     );
+
+    &writeNews();
 }
 
 sub del {
@@ -233,9 +291,8 @@ sub del {
            return;
        }
 
-       $item = &getNewsItem($what);
-       &::DEBUG("del: num: item => $item ($what)");
-       $what   = $item;        # hack hack hack.
+       $item   = &getNewsItem($what);
+       $what   = $item;                # hack hack hack.
 
     } else {
        $_      = &getNewsItem($what);  # hack hack hack.
@@ -288,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:");
@@ -297,9 +360,9 @@ sub list {
        my $t   = $::news{$chan}{$_}{Time};
        $newest = $t if ($t > $newest);
     }
-    &::msg($::who, "|= Last updated ".
-               &::Time2String(time() - $newest). " ago.");
-    &::msg($::who, " \037No\037  \037Item ".(" "x40)." \037");
+    my $timestr = &::Time2String(time() - $newest);
+    &::msg($::who, "|= Last updated $timestr ago.");
+    &::msg($::who, " \037Num\037 \037Item ".(" "x40)." \037");
 
     my $i = 1;
     foreach ( &getNewsAll() ) {
@@ -316,10 +379,9 @@ sub list {
                                $i, $subtopic));
        $i++;
     }
+
     &::msg($::who, "|= End of News.");
     &::msg($::who, "use 'news read <#>' or 'news read <keyword>'");
-
-    &::DEBUG("news cache => ".scalar(keys %::newsuser) );
 }
 
 sub read {
@@ -335,7 +397,6 @@ sub read {
        return;
     }
 
-#    my $item  = (exists $::news{$chan}{$str}) ? $str : &getNewsItem($str);
     my $item   = &getNewsItem($str);
     if (!defined $item or !scalar keys %{ $::news{$chan}{$item} }) {
        &::msg($::who, "No news item called '$str'");
@@ -347,14 +408,28 @@ sub read {
        return;
     }
 
-    # todo: show item number.
-    # todo: show ago-time aswell?
-    # todo: show request stats aswell.
-    my $t = localtime($::news{$chan}{$item}{Time});
-    my $a = $::news{$chan}{$item}{Author};
-    &::msg($::who, "+- News \002$chan\002 ##, item '\037$item\037':");
+    my $t      = localtime( $::news{$chan}{$item}{Time} );
+    my $a      = $::news{$chan}{$item}{Author};
+    my $text   = $::news{$chan}{$item}{Text};
+    my $num    = &newsS2N($item);
+    my $rwho   = $::news{$chan}{$item}{Request_By} || $::who;
+    my $rcount = $::news{$chan}{$item}{Request_Count} || 0;
+
+    if (length $text < $::param{maxKeySize}) {
+       &::DEBUG("NEWS: Possible news->factoid redirection.");
+       my $f   = &::getFactoid($text);
+
+       if (defined $f) {
+           &::DEBUG("NEWS: ok, $text is factoid redirection.");
+           $f =~ s/^<REPLY>\s*//i;     # anything else?
+           $text = $f;
+       }
+    }
+
+    &::msg($::who, "+- News \002$chan\002 #$num: \037$item\037");
     &::msg($::who, "| Added by $a at $t");
-    &::msg($::who, $::news{$chan}{$item}{Text});
+    &::msg($::who, "| Requested $rcount times, last by $rwho");
+    &::msg($::who, $text);
 
     $::news{$chan}{$item}{'Request_By'}   = $::who;
     $::news{$chan}{$item}{'Request_Time'} = time();
@@ -559,6 +634,7 @@ sub set {
        return;
     }
 
+    # todo: clean this up.
     my $old = $::news{$chan}{$news}{$what};
     if (defined $old) {
        &::DEBUG("old => $old.");
@@ -567,6 +643,78 @@ sub set {
     &::msg($::who, "Setting [$chan]/{$news}/<$what> to '$value'.");
 }
 
+sub latest {
+    my($tchan, $flag) = @_;
+
+    $chan ||= $tchan;  # hack hack hack.
+
+    # todo: if chan = undefined, guess.
+    if (!exists $::news{$chan}) {
+       &::msg($::who, "invalid chan $chan");
+       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} }) {
+       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});
+
+       push(@new, $_);
+    }
+
+    if (!scalar @new and $flag) {
+       &::msg($::who, "no new news for $chan.");
+       return;
+    }
+
+    if (!$flag) {
+       my $unread      = scalar @new;
+       my $total       = scalar keys %{ $::news{$chan} };
+       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;
+    }
+
+    if (scalar @new) {
+       &::msg($::who, "+==== New news for \002$chan\002 (".
+               scalar(@new)." new items):");
+
+       my $timestr = &::Time2String( time() - $::newsuser{$chan}{$::who} );
+       &::msg($::who, "|= Last time read $timestr ago");
+
+       foreach (@new) {
+           my $i   = &newsS2N($_);
+           my $age = time() - $::news{$chan}{$_}{Time};
+           &::msg($::who, sprintf("\002[\002%2d\002]\002 %s",
+               $i, $_) );
+#              $i, $_, &::Time2String($age) ) );
+       }
+
+       &::msg($::who, "|= to read, do 'news read <#>' or 'news read <keyword>'");
+
+       # lame hack to prevent dupes if we just ignore it.
+       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();
+       }
+    }
+}
+
 ###
 ### helpers...
 ###
@@ -585,6 +733,32 @@ sub getNewsAll {
     return @items;
 }
 
+sub newsS2N {
+    my($what)  = @_;
+    my @items;
+    my $no;
+
+    my %time;
+    foreach (keys %{ $::news{$chan} }) {
+       my $t = $::news{$chan}{$_}{Time};
+
+       if (!defined $t or $t !~ /^\d+$/) {
+           &::DEBUG("warn: t is undefined for news{$chan}{$_}{Time}; removing item.");
+           delete $::news{$chan}{$_};
+           next;
+       }
+
+       $time{$t} = $_;
+    }
+
+    foreach (sort { $a <=> $b } keys %time) {
+       $item++;
+       return $item if ($time{$_} eq $what);
+    }
+
+    &::DEBUG("newsS2N($what): failed...");
+}
+
 sub getNewsItem {
     my($what)  = @_;
     my $item   = 0;
@@ -611,22 +785,23 @@ sub getNewsItem {
 
     } else {
        # partial string to full string resolution
+       # in some cases, string->number resolution.
 
        my @items;
        my $no;
        foreach (sort { $a <=> $b } keys %time) {
            $item++;
-           $no = $item if ($time{$_} eq $what);
+#          $no = $item if ($time{$_} eq $what);
+           if ($time{$_} eq $what) {
+               $no = $item;
+               next;
+           }
+
            push(@items, $time{$_}) if ($time{$_} =~ /\Q$what\E/i);
        }
 
-       # since we have so much built into this function, there is so
-       # many guesses we can make.
-       # todo: split this command in the future into:
-       #       full_string->number and number->string
-       #       partial_string->full_string
        if (defined $no and !@items) {
-           &::DEBUG("string->number resolution.");
+           &::DEBUG("string->number resolution: $what->$no.");
            return $no;
        }
 
@@ -636,6 +811,7 @@ sub getNewsItem {
            return;
        }
 
+       &::DEBUG("gNI: part_string->full_string: $what->$items[0]");
        if (@items) {
            &::DEBUG("gNI: Guessed '$items[0]'.");
            return $items[0];