]> git.donarmstrong.com Git - infobot.git/commitdiff
forgot to add this file, heh.
authordms <dms@c11ca15a-4712-0410-83d8-924469b57eb5>
Sun, 1 Apr 2001 15:25:18 +0000 (15:25 +0000)
committerdms <dms@c11ca15a-4712-0410-83d8-924469b57eb5>
Sun, 1 Apr 2001 15:25:18 +0000 (15:25 +0000)
git-svn-id: https://svn.code.sf.net/p/infobot/code/trunk/blootbot@426 c11ca15a-4712-0410-83d8-924469b57eb5

src/Modules/News.pl [new file with mode: 0644]

diff --git a/src/Modules/News.pl b/src/Modules/News.pl
new file mode 100644 (file)
index 0000000..c21799a
--- /dev/null
@@ -0,0 +1,672 @@
+#
+# News.pl: Advanced news management
+#   Author: dms
+#  Version: v0.2 (20010326)
+#  Created: 20010326
+#    Notes: Testing done by greycat, kudos!
+#
+### structure:
+# news{ channel }{ string } { items }
+# newsuser{ channel }{ user } = time()
+### where items is:
+#      Time    - when it was added (used for sorting)
+#      Author  - Who by.
+#      Expire  - Time to expire
+#      Text    - Actual text.
+###
+
+package News;
+
+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;
+       }
+    } else {
+       $chan = $::chan;
+    }
+
+    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.");
+           return;
+       }
+
+       if (scalar @chans == 0) {
+           &::msg($::who, "error: I couldn't find you on any chan. This must be a bug!");
+           return;
+       }
+
+       $chan = $chans[0];
+       &::DEBUG("Guessed $::who being on chan $chan");
+    }
+
+    if (!defined $what or $what =~ /^\s*$/) {
+       &list();
+       return;
+    }
+
+    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 =~ /^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");
+    } elsif ($what =~ /^help(\s+(.*))?$/i) {
+       &::help("news$1");
+    } else {
+       &::DEBUG("could not parse '$what'.");
+       &::msg($::who, "unknown command: $what");
+    }
+}
+
+sub readNews {
+    my $file = "$::bot_base_dir/blootbot-news.txt";
+    if (! -f $file) {
+       return;
+    }
+
+    if (fileno NEWS) {
+       &::DEBUG("readNews: fileno exists, should never happen.");
+       return;
+    }
+
+    my($item,$chan);
+    my($ci,$cu) = (0,0);
+
+    open(NEWS, $file);
+    while (<NEWS>) {
+       chop;
+
+       # todo: allow commands.
+
+       if (/^[\s\t]+(\S+):[\s\t]+(.*)$/) {
+           if (!defined $item) {
+               &::DEBUG("!defined item, never happen!");
+               next;
+           }
+           $::news{$chan}{$item}{$1} = $2;
+           next;
+       }
+
+       # U <chan> <nick> <time>
+       if (/^U\s+(\S+)\s+(\S+)\s+(\d+)$/) {
+           $::newsuser{$1}{$2} = $3;
+           $cu++;
+           next;
+       }
+
+       if (/^(\S+)[\s\t]+(.*)$/) {
+           $chan = $1;
+           $item = $2;
+           $ci++;
+       }
+    }
+    close NEWS;
+
+    &::status("News: Read $ci items for ".scalar(keys %::news)
+               ." chans, $cu users cache");
+}
+
+sub writeNews {
+    my $file = "$::bot_base_dir/blootbot-news.txt";
+
+    if (fileno NEWS) {
+       &::ERROR("fileno NEWS exists, should never happen.");
+       return;
+    }
+
+    # 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} };
+       next unless ($c);
+       $cc++;
+
+       foreach $item (sort keys %{ $::news{$chan} }) {
+           $c = scalar keys %{ $::news{$chan}{$item} };
+           next unless ($c);
+           $ci++;
+
+           print NEWS "$chan $item\n";
+           foreach $what (sort keys %{ $::news{$chan}{$item} }) {
+               print NEWS "    $what: $::news{$chan}{$item}{$what}\n";
+           }
+           print NEWS "\n";
+       }
+    }
+
+    # todo: show how many users we wrote down.
+    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++;
+           }
+       }
+    }
+
+    close NEWS;
+
+    &::status("News: Wrote $ci items for $cc chans, $cu user cache.");
+}
+
+sub add {
+    my($str) = @_;
+
+    if (!defined $chan or !defined $str or $str =~ /^\s*$/) {
+       &::help("news add");
+       return;
+    }
+
+    if (length $str > 64) {
+       &::msg($::who, "That's not really an item (>64chars)");
+       return;
+    }
+
+    if (exists $::news{$chan}{$str}{Time}) {
+       &::msg($::who, "'$str' for $chan already exists!");
+       return;
+    }
+
+    $::news{$chan}{$str}{Time} = time();
+    my $expire = &::getChanConfDefault("newsDefaultExpire",7);
+    $::news{$chan}{$str}{Expire}       = time() + $expire*60*60*24;
+    $::news{$chan}{$str}{Author}       = $::who;
+
+    my $agestr = &::Time2String($::news{$chan}{$str}{Expire} - time() );
+    my $item   = &getNewsItem($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] "
+    );
+}
+
+sub del {
+    my($what)  = @_;
+    my $item   = 0;
+
+    if (!defined $what) {
+       &::help("news del");
+       return;
+    }
+
+    if ($what =~ /^\d+$/) {
+       my $count = scalar keys %{ $::news{$chan} };
+       if (!$count) {
+           &::msg($::who, "No news for $chan.");
+           return;
+       }
+
+       if ($what > $count or $what < 0) {
+           &::msg($::who, "$what is out of range (max $count)");
+           return;
+       }
+
+       $item = &getNewsItem($what);
+       &::DEBUG("del: num: item => $item ($what)");
+       $what   = $item;        # hack hack hack.
+
+    } else {
+       $_      = &getNewsItem($what);  # hack hack hack.
+       $what   = $_ if (defined $_);
+
+       if (!exists $::news{$chan}{$what}) {
+           my @found;
+           foreach (keys %{ $::news{$chan} }) {
+               next unless (/\Q$what\E/);
+               push(@found, $_);
+           }
+
+           if (!scalar @found) {
+               &::msg($::who, "could not find $what.");
+               return;
+           }
+
+           if (scalar @found > 1) {
+               &::msg($::who, "too many matches for $what.");
+               return;
+           }
+
+           $what       = $found[0];
+           &::DEBUG("del: str: guessed what => $what");
+       }
+    }
+
+    if (exists $::news{$chan}{$what}) {
+       my $auth = 0;
+       $auth++ if ($::who eq $::news{$chan}{$what}{Author});
+       $auth++ if (&::IsFlag("o"));
+
+       if (!$auth) {
+           # todo: show when it'll expire.
+           &::msg($::who, "Sorry, you cannot remove items; just let them expire on their own.");
+           return;
+       }
+
+       &::msg($::who, "ok, deleted '$what' from \002$chan\002...");
+       delete $::news{$chan}{$what};
+    } else {
+       &::msg($::who, "error: not found $what in news for $chan.");
+    }
+}
+
+sub list {
+    if (!scalar keys %{ $::news{$chan} }) {
+       &::msg($::who, "No News for \002$chan\002.");
+       return;
+    }
+
+    if (&::IsChanConf("newsKeepRead")) {
+       $::newsuser{$chan}{$::who} = time();
+    }
+
+    &::msg($::who, "|==== News for \002$chan\002:");
+    my $newest = 0;
+    foreach (keys %{ $::news{$chan} }) {
+       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 $i = 1;
+    foreach ( &getNewsAll() ) {
+       my $subtopic    = $_;
+       my $setby       = $::news{$chan}{$subtopic}{Author};
+
+       if (!defined $subtopic) {
+           &::DEBUG("warn: subtopic == undef.");
+           next;
+       }
+
+       # todo: show request stats aswell.
+       &::msg($::who, sprintf("\002[\002%2d\002]\002 %s",
+                               $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 {
+    my($str) = @_;
+
+    if (!defined $chan or !defined $str or $str =~ /^\s*$/) {
+       &::help("news read");
+       return;
+    }
+
+    if (!scalar keys %{ $::news{$chan} }) {
+       &::msg($::who, "No News for \002$chan\002.");
+       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'");
+       return;
+    }
+
+    if (!exists $::news{$chan}{$item}{Text}) {
+       &::msg($::who, "Someone forgot to add info to this news item");
+       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':");
+    &::msg($::who, "| Added by $a at $t");
+    &::msg($::who, $::news{$chan}{$item}{Text});
+
+    $::news{$chan}{$item}{'Request_By'}   = $::who;
+    $::news{$chan}{$item}{'Request_Time'} = time();
+    $::news{$chan}{$item}{'Request_Count'}++;
+}
+
+sub mod {
+    my($item, $str) = split /\s+/, $_[0], 2;
+
+    if (!defined $item or $item eq "" or $str =~ /^\s*$/) {
+       &::help("news mod");
+       return;
+    }
+
+    my $news = &getNewsItem($item);
+
+    if (!defined $news) {
+       &::DEBUG("error: mod: news == undefined.");
+       return;
+    }
+    my $nnews = $::news{$chan}{$news}{Text};
+    my $mod_news  = $news;
+    my $mod_nnews = $nnews;
+
+    # SAR patch. mu++
+    if ($str =~ m|^\s*s([/,#\|])(.+?)\1(.*?)\1([a-z]*);?\s*$|) {
+       my ($delim, $op, $np, $flags) = ($1,$2,$3,$4);
+
+       if ($flags !~ /^(g)?$/) {
+           &::msg($::who, "error: Invalid flags to regex.");
+           return;
+       }
+
+       ### TODO: use m### to make code safe!
+       # todo: make code safer.
+       my $done = 0;
+       # todo: use eval to deal with flags easily.
+       if ($flags eq "") {
+           $done++ if (!$done and $mod_news  =~ s/\Q$op\E/$np/);
+           $done++ if (!$done and $mod_nnews =~ s/\Q$op\E/$np/);
+        } elsif ($flags eq "g") {
+           $done++ if ($mod_news  =~ s/\Q$op\E/$np/g);
+           $done++ if ($mod_nnews =~ s/\Q$op\E/$np/g);
+       }
+
+       if (!$done) {
+           &::msg($::who, "warning: regex not found in news.");
+           return;
+       }
+
+       if ($mod_news ne $news) { # news item.
+           if (exists $::news{$chan}{$mod_news}) {
+               &::msg($::who, "item '$mod_news' already exists.");
+               return;
+           }
+
+           &::msg($::who, "Moving item '$news' to '$mod_news' with SAR s/$op/$np/.");
+           foreach (keys %{ $::news{$chan}{$news} }) {
+               $::news{$chan}{$mod_news}{$_} = $::news{$chan}{$news}{$_};
+               delete $::news{$chan}{$news}{$_};
+           }
+           # needed?
+           delete $::news{$chan}{$news};
+       }
+
+       if ($mod_nnews ne $nnews) { # news Text/Description.
+           &::msg($::who, "Changing text for '$news' SAR s/$op/$np/.");
+           if ($mod_news ne $news) {
+               $::news{$chan}{$mod_news}{Text} = $mod_nnews;
+           } else {
+               $::news{$chan}{$news}{Text}     = $mod_nnews;
+           }
+       }
+
+       return;
+    } else {
+       &::msg($::who, "error: that regex failed ;(");
+       return;
+    }
+
+    &::msg($::who, "error: Invalid regex. Try s/1/2/, s#3#4#...");
+}
+
+sub set {
+    my($args) = @_;
+    $args =~ /^(\S+)\s+(\S+)\s+(.*)$/;
+    my($item, $what, $value) = ($1,$2,$3);
+
+    &::DEBUG("set called.");
+
+    if ($item eq "") {
+       &::help("news set");
+       return;
+    }
+
+    &::DEBUG("item => '$item'.");
+    my $news = &getNewsItem($item);
+    &::DEBUG("news => '$news'");
+
+    if (!defined $news) {
+       &::msg($::who, "Could not find item '$item' substring or # in news list.");
+       return;
+    }
+
+    # list all values for chan.
+    if (!defined $what) {
+       &::DEBUG("set: 1");
+       return;
+    }
+
+    my $ok = 0;
+    my @elements = ("Expire","Text");
+    foreach (@elements) {
+       next unless ($what =~ /^$_$/i);
+       $what = $_;
+       $ok++;
+       last;
+    }
+
+    if (!$ok) {
+       &::msg($::who, "Invalid set.  Try: @elements");
+       return;
+    }
+
+    # show (read) what.
+    if (!defined $value) {
+       &::DEBUG("set: 2");
+       return;
+    }
+
+    if (!exists $::news{$chan}{$news}) {
+       &::msg($::who, "news '$news' does not exist");
+       return;
+    }
+
+    if ($what eq "Expire") {
+       # todo: use do_set().
+
+       my $time = 0;
+       my $plus = ($value =~ s/^\+//g);
+       while ($value =~ s/^(\d+)(\S*)\s*//) {
+           my($int,$unit) = ($1,$2);
+           $time += $int       if ($unit =~ /^s(ecs?)?$/i);
+           $time += $int*60    if ($unit =~ /^m(in(utes?)?)?$/i);
+           $time += $int*60*60 if ($unit =~ /^h(ours?)?$/i);
+           $time += $int*60*60*24 if (!$unit or $unit =~ /^d(ays?)?$/i);
+           $time += $int*60*60*24*7 if ($unit =~ /^w(eeks?)?$/i);
+           $time += $int*60*60*24*30 if ($unit =~ /^mon(th)?$/i);
+       }
+
+       if ($value =~ s/^never$//i) {
+           # never.
+           $time = -1;
+       } elsif ($plus) {
+           # from now.
+           $time += time();
+       } else {
+           # from creation of item.
+           $time += $::news{$chan}{$news}{Time};
+       }
+
+       if (!$time or ($value and $value !~ /^never$/i)) {
+           &::DEBUG("set: Expire... need to parse.");
+           return;
+       }
+
+       if ($time == -1) {
+           &::msg($::who, "Set never expire for \002$item\002." );
+       } elsif ($time < -1) {
+           &::DEBUG("time should never be negative ($time).");
+           return;
+       } else {
+           &::msg($::who, "Set expire for \002$item\002, to ".
+               localtime($time) ." [".&::Time2String($time - time())."]" );
+
+           if (time() > $time) {
+               &::DEBUG("hrm... time() > $time, should expire.");
+           }
+       }
+
+
+       $::news{$chan}{$news}{Expire} = $time;
+
+       return;
+    }
+
+    my $auth = 0;
+    &::DEBUG("who => '$::who'");
+    my $author = $::news{$chan}{$news}{Author};
+    $auth++ if ($::who eq $author);
+    $auth++ if (&::IsFlag("o"));
+    if (!defined $author) {
+       &::DEBUG("news{$chan}{$news}{Author} is not defined! auth'd anyway");
+       $::news{$chan}{$news}{Author} = $::who;
+       $author = $::who;
+       $auth++;
+    }
+
+    if (!$auth) {
+       # todo: show when it'll expire.
+       &::msg($::who, "Sorry, you cannot set items. (author $author owns it)");
+       return;
+    }
+
+    my $old = $::news{$chan}{$news}{$what};
+    if (defined $old) {
+       &::DEBUG("old => $old.");
+    }
+    $::news{$chan}{$news}{$what} = $value;
+    &::msg($::who, "Setting [$chan]/{$news}/<$what> to '$value'.");
+}
+
+###
+### helpers...
+###
+
+sub getNewsAll {
+    my %time;
+    foreach (keys %{ $::news{$chan} }) {
+       $time{ $::news{$chan}{$_}{Time} } = $_;
+    }
+
+    my @items;
+    foreach (sort { $a <=> $b } keys %time) {
+       push(@items, $time{$_});
+    }
+
+    return @items;
+}
+
+sub getNewsItem {
+    my($what)  = @_;
+    my $item   = 0;
+
+    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} = $_;
+    }
+
+    # number to string resolution.
+    if ($what =~ /^\d+$/) {
+       foreach (sort { $a <=> $b } keys %time) {
+           $item++;
+           return $time{$_} if ($item == $what);
+       }
+
+    } else {
+       # partial string to full string resolution
+
+       my @items;
+       my $no;
+       foreach (sort { $a <=> $b } keys %time) {
+           $item++;
+           $no = $item if ($time{$_} eq $what);
+           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.");
+           return $no;
+       }
+
+       if (scalar @items > 1) {
+           &::DEBUG("Multiple matches, not guessing.");
+           &::msg($::who, "Multiple matches, not guessing.");
+           return;
+       }
+
+       if (@items) {
+           &::DEBUG("gNI: Guessed '$items[0]'.");
+           return $items[0];
+       } else {
+           &::DEBUG("gNI: No match.");
+           return;
+       }
+    }
+
+    &::ERROR("getNewsItem: Should not happen (what = $what)");
+    return;
+}
+
+sub do_set {
+    my($what,$value) = @_;
+
+    if (!defined $chan) {
+       &::DEBUG("do_set: chan not defined.");
+       return;
+    }
+
+    if (!defined $what or $what =~ /^\s*$/) {
+       &::DEBUG("what $what is not defined.");
+       return;
+    }
+    if (!defined $value or $value =~ /^\s*$/) {
+       &::DEBUG("value $value is not defined.");
+       return;
+    }
+
+    &::DEBUG("do_set: TODO...");
+}
+
+1;