]> git.donarmstrong.com Git - infobot.git/blob - src/Modules/News.pl
news: can make news compulsory (chanset +newsNotifyAll)
[infobot.git] / src / Modules / News.pl
1 #
2 # News.pl: Advanced news management
3 #   Author: dms
4 #  Version: v0.3 (20014012)
5 #  Created: 20010326
6 #    Notes: Testing done by greycat, kudos!
7 #
8 ### structure:
9 # news{ channel }{ string } { item }
10 # newsuser{ channel }{ user } = time()
11 ### where item is:
12 #       Time    - when it was added (used for sorting)
13 #       Author  - Who by.
14 #       Expire  - Time to expire.
15 #       Text    - Actual text.
16 ###
17
18 package News;
19
20 sub Parse {
21     my($what)   = @_;
22     $chan       = undef;
23
24     if (!keys %::news) {
25         if (!exists $::cache{newsFirst}) {
26             &::DEBUG("looks like we enabled news option just then; loading up news file just in case.");
27             $::cache{newsFirst} = 1;
28         }
29
30         &readNews();
31     }
32
33     if ($::msgType ne "private") {
34         $chan = $::chan;
35     }
36
37     if (defined $what and $what =~ s/^($::mask{chan})\s*//) {
38         # todo: check if the channel exists aswell.
39         $chan = $1;
40     }
41
42     if (!defined $chan) {
43         my @chans = &::GetNickInChans($::who);
44
45         if (scalar @chans > 1) {
46             &::msg($::who, "error: I dunno which channel you are referring to since you're on more than one. Try 'news #chan ...' instead");
47             return;
48         }
49
50         if (scalar @chans == 0) {
51             &::msg($::who, "error: I couldn't find you on any chan. This must be a bug!");
52             return;
53         }
54
55         $chan = $chans[0];
56         &::DEBUG("Guessed $::who being on chan $chan");
57     }
58
59     if (!defined $what or $what =~ /^\s*$/) {
60         &list();
61         return;
62     }
63
64     if ($what =~ /^add(\s+(.*))?$/i) {
65         &add($2);
66
67     } elsif ($what =~ /^del(\s+(.*))?$/i) {
68         &del($2);
69
70     } elsif ($what =~ /^mod(\s+(.*))?$/i) {
71         &mod($2);
72
73     } elsif ($what =~ /^set(\s+(.*))?$/i) {
74         &set($2);
75
76     } elsif ($what =~ /^(\d)$/i) {
77         &::DEBUG("read shortcut called.");
78         &read($1);
79
80     } elsif ($what =~ /^read(\s+(.*))?$/i) {
81         &read($2);
82
83     } elsif ($what =~ /^(latest|new)(\s+(.*))?$/i) {
84         &::DEBUG("latest called... hrm");
85         &latest($3 || $chan, 1);
86
87     } elsif ($what =~ /^list$/i) {
88         &::DEBUG("list longcut called.");
89         &list();
90
91     } elsif ($what =~ /^(expire|text|desc)(\s+(.*))?$/i) {
92         # shortcut/link.
93         # nice hack.
94         my($arg1,$arg2) = split(/\s+/, $3, 2);
95         &set("$arg1 $1 $arg2");
96
97     } elsif ($what =~ /^help(\s+(.*))?$/i) {
98         &::help("news$1");
99
100     } elsif ($what =~ /^(un)?notify$/i) {
101         my $state = ($1) ? 0 : 1;
102
103         # todo: don't notify even if "news" is called.
104         if (!&::IsChanConf("newsNotifyAll")) {
105             &::msg($::who, "not available for this channel or disabled altogether.");
106             return;
107         }
108
109         my $t = $::newsuser{$chan}{$::who};
110         if ($state) {   # state = 1
111             if (defined $t and ($t == 0 or $t == -1)) {
112                 &::msg($::who, "enabled notify.");
113                 delete $::newsuser{$chan}{$::who};
114                 return;
115             }
116             &::msg($::who, "already enabled.");
117
118         } else {                # state = 0
119             my $x = $::newsuser{$chan}{$::who};
120             if (defined $x and ($x == 0 or $x == -1)) {
121                 &::msg($::who, "notify already disabled");
122                 return;
123             }
124             $::newsuser{$chan}{$::who} = -1;
125             &::msg($::who, "notify is now disabled.");
126         }
127
128     } else {
129         &::DEBUG("could not parse '$what'.");
130         &::msg($::who, "unknown command: $what");
131     }
132 }
133
134 sub readNews {
135     my $file = "$::bot_base_dir/blootbot-news.txt";
136     if (! -f $file) {
137         return;
138     }
139
140     if (fileno NEWS) {
141         &::DEBUG("readNews: fileno exists, should never happen.");
142         return;
143     }
144
145     my($item,$chan);
146     my($ci,$cu) = (0,0);
147
148     open(NEWS, $file);
149     while (<NEWS>) {
150         chop;
151
152         # todo: allow commands.
153
154         if (/^[\s\t]+(\S+):[\s\t]+(.*)$/) {
155             if (!defined $item) {
156                 &::DEBUG("!defined item, never happen!");
157                 next;
158             }
159
160             $::news{$chan}{$item}{$1} = $2;
161             next;
162         }
163
164         # U <chan> <nick> <time>
165         if (/^U\s+(\S+)\s+(\S+)\s+(\d+)$/) {
166             $::newsuser{$1}{$2} = $3;
167             $cu++;
168             next;
169         }
170
171         if (/^(\S+)[\s\t]+(.*)$/) {
172             $chan = $1;
173             $item = $2;
174             $ci++;
175         }
176     }
177     close NEWS;
178
179     &::status("News: Read $ci items for ".scalar(keys %::news)
180                 ." chans, $cu users cache");
181 }
182
183 sub writeNews {
184     if (!scalar keys %::news) {
185         &::DEBUG("wN: nothing to write.");
186         return;
187     }
188
189     my $file = "$::bot_base_dir/blootbot-news.txt";
190
191     if (fileno NEWS) {
192         &::ERROR("fileno NEWS exists, should never happen.");
193         return;
194     }
195
196     # todo: add commands to output file.
197     my $c = 0;
198     my($cc,$ci,$cu) = (0,0,0);
199
200     open(NEWS, ">$file");
201     foreach $chan (sort keys %::news) {
202         $c = scalar keys %{ $::news{$chan} };
203         next unless ($c);
204         $cc++;
205
206         foreach $item (sort keys %{ $::news{$chan} }) {
207             $c = scalar keys %{ $::news{$chan}{$item} };
208             next unless ($c);
209             $ci++;
210
211             print NEWS "$chan $item\n";
212             foreach $what (sort keys %{ $::news{$chan}{$item} }) {
213                 print NEWS "    $what: $::news{$chan}{$item}{$what}\n";
214             }
215             print NEWS "\n";
216         }
217     }
218
219     # todo: show how many users we wrote down.
220     if (&::getChanConfList("newsKeepRead")) {
221         # old users are removed in newsFlush(), perhaps it should be
222         # done here.
223
224         foreach $chan (sort keys %::newsuser) {
225
226             foreach (sort keys %{ $::newsuser{$chan} }) {
227                 print NEWS "U $chan $_ $::newsuser{$chan}{$_}\n";
228                 $cu++;
229             }
230         }
231     }
232
233     close NEWS;
234
235     &::status("News: Wrote $ci items for $cc chans, $cu user cache.");
236 }
237
238 sub add {
239     my($str) = @_;
240
241     if (!defined $chan or !defined $str or $str =~ /^\s*$/) {
242         &::help("news add");
243         return;
244     }
245
246     if (length $str > 64) {
247         &::msg($::who, "That's not really an item (>64chars)");
248         return;
249     }
250
251     if (exists $::news{$chan}{$str}{Time}) {
252         &::msg($::who, "'$str' for $chan already exists!");
253         return;
254     }
255
256     $::news{$chan}{$str}{Time}  = time();
257     my $expire = &::getChanConfDefault("newsDefaultExpire",7);
258     $::news{$chan}{$str}{Expire}        = time() + $expire*60*60*24;
259     $::news{$chan}{$str}{Author}        = $::who;
260
261     my $agestr  = &::Time2String($::news{$chan}{$str}{Expire} - time() );
262     my $item    = &newsS2N($str);
263     &::msg($::who, "Added '\037$str\037' at [".localtime(time).
264                 "] by \002$::who\002 for item #\002$item\002.");
265     &::msg($::who, "Now do 'news text $item <your_description>'");
266     &::msg($::who, "This item will expire at \002".
267         localtime($::news{$chan}{$str}{Expire})."\002 [$agestr from now] "
268     );
269
270     &writeNews();
271 }
272
273 sub del {
274     my($what)   = @_;
275     my $item    = 0;
276
277     if (!defined $what) {
278         &::help("news del");
279         return;
280     }
281
282     if ($what =~ /^\d+$/) {
283         my $count = scalar keys %{ $::news{$chan} };
284         if (!$count) {
285             &::msg($::who, "No news for $chan.");
286             return;
287         }
288
289         if ($what > $count or $what < 0) {
290             &::msg($::who, "$what is out of range (max $count)");
291             return;
292         }
293
294         $item   = &getNewsItem($what);
295         $what   = $item;                # hack hack hack.
296
297     } else {
298         $_      = &getNewsItem($what);  # hack hack hack.
299         $what   = $_ if (defined $_);
300
301         if (!exists $::news{$chan}{$what}) {
302             my @found;
303             foreach (keys %{ $::news{$chan} }) {
304                 next unless (/\Q$what\E/);
305                 push(@found, $_);
306             }
307
308             if (!scalar @found) {
309                 &::msg($::who, "could not find $what.");
310                 return;
311             }
312
313             if (scalar @found > 1) {
314                 &::msg($::who, "too many matches for $what.");
315                 return;
316             }
317
318             $what       = $found[0];
319             &::DEBUG("del: str: guessed what => $what");
320         }
321     }
322
323     if (exists $::news{$chan}{$what}) {
324         my $auth = 0;
325         $auth++ if ($::who eq $::news{$chan}{$what}{Author});
326         $auth++ if (&::IsFlag("o"));
327
328         if (!$auth) {
329             # todo: show when it'll expire.
330             &::msg($::who, "Sorry, you cannot remove items; just let them expire on their own.");
331             return;
332         }
333
334         &::msg($::who, "ok, deleted '$what' from \002$chan\002...");
335         delete $::news{$chan}{$what};
336     } else {
337         &::msg($::who, "error: not found $what in news for $chan.");
338     }
339 }
340
341 sub list {
342     if (!scalar keys %{ $::news{$chan} }) {
343         &::msg($::who, "No News for \002$chan\002.");
344         return;
345     }
346
347     if (&::IsChanConf("newsKeepRead")) {
348         my $x = $::newsuser{$chan}{$::who};
349
350         if (defined $x and ($x == 0 or $x == -1)) {
351             &::DEBUG("not updating time for $::who.");
352         } else {
353             $::newsuser{$chan}{$::who} = time();
354         }
355     }
356
357     &::msg($::who, "|==== News for \002$chan\002:");
358     my $newest  = 0;
359     foreach (keys %{ $::news{$chan} }) {
360         my $t   = $::news{$chan}{$_}{Time};
361         $newest = $t if ($t > $newest);
362     }
363     my $timestr = &::Time2String(time() - $newest);
364     &::msg($::who, "|= Last updated $timestr ago.");
365     &::msg($::who, " \037Num\037 \037Item ".(" "x40)." \037");
366
367     my $i = 1;
368     foreach ( &getNewsAll() ) {
369         my $subtopic    = $_;
370         my $setby       = $::news{$chan}{$subtopic}{Author};
371
372         if (!defined $subtopic) {
373             &::DEBUG("warn: subtopic == undef.");
374             next;
375         }
376
377         # todo: show request stats aswell.
378         &::msg($::who, sprintf("\002[\002%2d\002]\002 %s",
379                                 $i, $subtopic));
380         $i++;
381     }
382
383     &::msg($::who, "|= End of News.");
384     &::msg($::who, "use 'news read <#>' or 'news read <keyword>'");
385 }
386
387 sub read {
388     my($str) = @_;
389
390     if (!defined $chan or !defined $str or $str =~ /^\s*$/) {
391         &::help("news read");
392         return;
393     }
394
395     if (!scalar keys %{ $::news{$chan} }) {
396         &::msg($::who, "No News for \002$chan\002.");
397         return;
398     }
399
400     my $item    = &getNewsItem($str);
401     if (!defined $item or !scalar keys %{ $::news{$chan}{$item} }) {
402         &::msg($::who, "No news item called '$str'");
403         return;
404     }
405
406     if (!exists $::news{$chan}{$item}{Text}) {
407         &::msg($::who, "Someone forgot to add info to this news item");
408         return;
409     }
410
411     my $t       = localtime( $::news{$chan}{$item}{Time} );
412     my $a       = $::news{$chan}{$item}{Author};
413     my $text    = $::news{$chan}{$item}{Text};
414     my $num     = &newsS2N($item);
415     my $rwho    = $::news{$chan}{$item}{Request_By} || $::who;
416     my $rcount  = $::news{$chan}{$item}{Request_Count} || 0;
417
418     if (length $text < $::param{maxKeySize}) {
419         &::DEBUG("NEWS: Possible news->factoid redirection.");
420         my $f   = &::getFactoid($text);
421
422         if (defined $f) {
423             &::DEBUG("NEWS: ok, $text is factoid redirection.");
424             $f =~ s/^<REPLY>\s*//i;     # anything else?
425             $text = $f;
426         }
427     }
428
429     &::msg($::who, "+- News \002$chan\002 #$num: \037$item\037");
430     &::msg($::who, "| Added by $a at $t");
431     &::msg($::who, "| Requested $rcount times, last by $rwho");
432     &::msg($::who, $text);
433
434     $::news{$chan}{$item}{'Request_By'}   = $::who;
435     $::news{$chan}{$item}{'Request_Time'} = time();
436     $::news{$chan}{$item}{'Request_Count'}++;
437 }
438
439 sub mod {
440     my($item, $str) = split /\s+/, $_[0], 2;
441
442     if (!defined $item or $item eq "" or $str =~ /^\s*$/) {
443         &::help("news mod");
444         return;
445     }
446
447     my $news = &getNewsItem($item);
448
449     if (!defined $news) {
450         &::DEBUG("error: mod: news == undefined.");
451         return;
452     }
453     my $nnews = $::news{$chan}{$news}{Text};
454     my $mod_news  = $news;
455     my $mod_nnews = $nnews;
456
457     # SAR patch. mu++
458     if ($str =~ m|^\s*s([/,#\|])(.+?)\1(.*?)\1([a-z]*);?\s*$|) {
459         my ($delim, $op, $np, $flags) = ($1,$2,$3,$4);
460
461         if ($flags !~ /^(g)?$/) {
462             &::msg($::who, "error: Invalid flags to regex.");
463             return;
464         }
465
466         ### TODO: use m### to make code safe!
467         # todo: make code safer.
468         my $done = 0;
469         # todo: use eval to deal with flags easily.
470         if ($flags eq "") {
471             $done++ if (!$done and $mod_news  =~ s/\Q$op\E/$np/);
472             $done++ if (!$done and $mod_nnews =~ s/\Q$op\E/$np/);
473         } elsif ($flags eq "g") {
474             $done++ if ($mod_news  =~ s/\Q$op\E/$np/g);
475             $done++ if ($mod_nnews =~ s/\Q$op\E/$np/g);
476         }
477
478         if (!$done) {
479             &::msg($::who, "warning: regex not found in news.");
480             return;
481         }
482
483         if ($mod_news ne $news) { # news item.
484             if (exists $::news{$chan}{$mod_news}) {
485                 &::msg($::who, "item '$mod_news' already exists.");
486                 return;
487             }
488
489             &::msg($::who, "Moving item '$news' to '$mod_news' with SAR s/$op/$np/.");
490             foreach (keys %{ $::news{$chan}{$news} }) {
491                 $::news{$chan}{$mod_news}{$_} = $::news{$chan}{$news}{$_};
492                 delete $::news{$chan}{$news}{$_};
493             }
494             # needed?
495             delete $::news{$chan}{$news};
496         }
497
498         if ($mod_nnews ne $nnews) { # news Text/Description.
499             &::msg($::who, "Changing text for '$news' SAR s/$op/$np/.");
500             if ($mod_news ne $news) {
501                 $::news{$chan}{$mod_news}{Text} = $mod_nnews;
502             } else {
503                 $::news{$chan}{$news}{Text}     = $mod_nnews;
504             }
505         }
506
507         return;
508     } else {
509         &::msg($::who, "error: that regex failed ;(");
510         return;
511     }
512
513     &::msg($::who, "error: Invalid regex. Try s/1/2/, s#3#4#...");
514 }
515
516 sub set {
517     my($args) = @_;
518     $args =~ /^(\S+)\s+(\S+)\s+(.*)$/;
519     my($item, $what, $value) = ($1,$2,$3);
520
521     &::DEBUG("set called.");
522
523     if ($item eq "") {
524         &::help("news set");
525         return;
526     }
527
528     &::DEBUG("item => '$item'.");
529     my $news = &getNewsItem($item);
530     &::DEBUG("news => '$news'");
531
532     if (!defined $news) {
533         &::msg($::who, "Could not find item '$item' substring or # in news list.");
534         return;
535     }
536
537     # list all values for chan.
538     if (!defined $what) {
539         &::DEBUG("set: 1");
540         return;
541     }
542
543     my $ok = 0;
544     my @elements = ("Expire","Text");
545     foreach (@elements) {
546         next unless ($what =~ /^$_$/i);
547         $what = $_;
548         $ok++;
549         last;
550     }
551
552     if (!$ok) {
553         &::msg($::who, "Invalid set.  Try: @elements");
554         return;
555     }
556
557     # show (read) what.
558     if (!defined $value) {
559         &::DEBUG("set: 2");
560         return;
561     }
562
563     if (!exists $::news{$chan}{$news}) {
564         &::msg($::who, "news '$news' does not exist");
565         return;
566     }
567
568     if ($what eq "Expire") {
569         # todo: use do_set().
570
571         my $time = 0;
572         my $plus = ($value =~ s/^\+//g);
573         while ($value =~ s/^(\d+)(\S*)\s*//) {
574             my($int,$unit) = ($1,$2);
575             $time += $int       if ($unit =~ /^s(ecs?)?$/i);
576             $time += $int*60    if ($unit =~ /^m(in(utes?)?)?$/i);
577             $time += $int*60*60 if ($unit =~ /^h(ours?)?$/i);
578             $time += $int*60*60*24 if (!$unit or $unit =~ /^d(ays?)?$/i);
579             $time += $int*60*60*24*7 if ($unit =~ /^w(eeks?)?$/i);
580             $time += $int*60*60*24*30 if ($unit =~ /^mon(th)?$/i);
581         }
582
583         if ($value =~ s/^never$//i) {
584             # never.
585             $time = -1;
586         } elsif ($plus) {
587             # from now.
588             $time += time();
589         } else {
590             # from creation of item.
591             $time += $::news{$chan}{$news}{Time};
592         }
593
594         if (!$time or ($value and $value !~ /^never$/i)) {
595             &::DEBUG("set: Expire... need to parse.");
596             return;
597         }
598
599         if ($time == -1) {
600             &::msg($::who, "Set never expire for \002$item\002." );
601         } elsif ($time < -1) {
602             &::DEBUG("time should never be negative ($time).");
603             return;
604         } else {
605             &::msg($::who, "Set expire for \002$item\002, to ".
606                 localtime($time) ." [".&::Time2String($time - time())."]" );
607
608             if (time() > $time) {
609                 &::DEBUG("hrm... time() > $time, should expire.");
610             }
611         }
612
613
614         $::news{$chan}{$news}{Expire} = $time;
615
616         return;
617     }
618
619     my $auth = 0;
620     &::DEBUG("who => '$::who'");
621     my $author = $::news{$chan}{$news}{Author};
622     $auth++ if ($::who eq $author);
623     $auth++ if (&::IsFlag("o"));
624     if (!defined $author) {
625         &::DEBUG("news{$chan}{$news}{Author} is not defined! auth'd anyway");
626         $::news{$chan}{$news}{Author} = $::who;
627         $author = $::who;
628         $auth++;
629     }
630
631     if (!$auth) {
632         # todo: show when it'll expire.
633         &::msg($::who, "Sorry, you cannot set items. (author $author owns it)");
634         return;
635     }
636
637     # todo: clean this up.
638     my $old = $::news{$chan}{$news}{$what};
639     if (defined $old) {
640         &::DEBUG("old => $old.");
641     }
642     $::news{$chan}{$news}{$what} = $value;
643     &::msg($::who, "Setting [$chan]/{$news}/<$what> to '$value'.");
644 }
645
646 sub latest {
647     my($tchan, $flag) = @_;
648
649     $chan ||= $tchan;   # hack hack hack.
650
651     # todo: if chan = undefined, guess.
652     if (!exists $::news{$chan}) {
653         &::msg($::who, "invalid chan $chan");
654         return;
655     }
656
657     my $t = $::newsuser{$chan}{$::who};
658     if (defined $t and ($t == 0 or $t == -1)) {
659         &::DEBUG("not displaying any new news for $::who");
660         return;
661     }
662
663     my @new;
664     foreach (keys %{ $::news{$chan} }) {
665         if (&::IsChanConf("newsNotifyAll") and !defined $t) {
666             &::DEBUG("setting time for $::who to 1...");
667             $::newsuser{$chan}{$::who} = 1;
668             $t = 1;
669         }
670         next if (!defined $t);
671         next if ($t > $::news{$chan}{$_}{Time});
672
673         push(@new, $_);
674     }
675
676     if (!scalar @new and $flag) {
677         &::msg($::who, "no new news for $chan.");
678         return;
679     }
680
681     if (!$flag) {
682         my $unread      = scalar @new;
683         my $total       = scalar keys %{ $::news{$chan} };
684         return unless ($unread);
685
686         &::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");
687
688         return;
689     }
690
691     if (scalar @new) {
692         &::msg($::who, "+==== New news for \002$chan\002 (".
693                 scalar(@new)." new items):");
694
695         my $timestr = &::Time2String( time() - $::newsuser{$chan}{$::who} );
696         &::msg($::who, "|= Last time read $timestr ago");
697
698         foreach (@new) {
699             my $i   = &newsS2N($_);
700             my $age = time() - $::news{$chan}{$_}{Time};
701             &::msg($::who, sprintf("\002[\002%2d\002]\002 %s",
702                 $i, $_) );
703 #               $i, $_, &::Time2String($age) ) );
704         }
705
706         &::msg($::who, "|= to read, do 'news read <#>' or 'news read <keyword>'");
707
708         # lame hack to prevent dupes if we just ignore it.
709         my $x = $::newsuser{$chan}{$::who};
710         if (defined $x and ($x == 0 or $x == -1)) {
711             &::DEBUG("not updating time for $::who. (2)");
712         } else {
713             $::newsuser{$chan}{$::who} = time();
714         }
715     }
716 }
717
718 ###
719 ### helpers...
720 ###
721
722 sub getNewsAll {
723     my %time;
724     foreach (keys %{ $::news{$chan} }) {
725         $time{ $::news{$chan}{$_}{Time} } = $_;
726     }
727
728     my @items;
729     foreach (sort { $a <=> $b } keys %time) {
730         push(@items, $time{$_});
731     }
732
733     return @items;
734 }
735
736 sub newsS2N {
737     my($what)   = @_;
738     my @items;
739     my $no;
740
741     my %time;
742     foreach (keys %{ $::news{$chan} }) {
743         my $t = $::news{$chan}{$_}{Time};
744
745         if (!defined $t or $t !~ /^\d+$/) {
746             &::DEBUG("warn: t is undefined for news{$chan}{$_}{Time}; removing item.");
747             delete $::news{$chan}{$_};
748             next;
749         }
750
751         $time{$t} = $_;
752     }
753
754     foreach (sort { $a <=> $b } keys %time) {
755         $item++;
756         return $item if ($time{$_} eq $what);
757     }
758
759     &::DEBUG("newsS2N($what): failed...");
760 }
761
762 sub getNewsItem {
763     my($what)   = @_;
764     my $item    = 0;
765
766     my %time;
767     foreach (keys %{ $::news{$chan} }) {
768         my $t = $::news{$chan}{$_}{Time};
769
770         if (!defined $t or $t !~ /^\d+$/) {
771             &::DEBUG("warn: t is undefined for news{$chan}{$_}{Time}; removing item.");
772             delete $::news{$chan}{$_};
773             next;
774         }
775
776         $time{$t} = $_;
777     }
778
779     # number to string resolution.
780     if ($what =~ /^\d+$/) {
781         foreach (sort { $a <=> $b } keys %time) {
782             $item++;
783             return $time{$_} if ($item == $what);
784         }
785
786     } else {
787         # partial string to full string resolution
788         # in some cases, string->number resolution.
789
790         my @items;
791         my $no;
792         foreach (sort { $a <=> $b } keys %time) {
793             $item++;
794 #           $no = $item if ($time{$_} eq $what);
795             if ($time{$_} eq $what) {
796                 $no = $item;
797                 next;
798             }
799
800             push(@items, $time{$_}) if ($time{$_} =~ /\Q$what\E/i);
801         }
802
803         if (defined $no and !@items) {
804             &::DEBUG("string->number resolution: $what->$no.");
805             return $no;
806         }
807
808         if (scalar @items > 1) {
809             &::DEBUG("Multiple matches, not guessing.");
810             &::msg($::who, "Multiple matches, not guessing.");
811             return;
812         }
813
814         &::DEBUG("gNI: part_string->full_string: $what->$items[0]");
815         if (@items) {
816             &::DEBUG("gNI: Guessed '$items[0]'.");
817             return $items[0];
818         } else {
819             &::DEBUG("gNI: No match.");
820             return;
821         }
822     }
823
824     &::ERROR("getNewsItem: Should not happen (what = $what)");
825     return;
826 }
827
828 sub do_set {
829     my($what,$value) = @_;
830
831     if (!defined $chan) {
832         &::DEBUG("do_set: chan not defined.");
833         return;
834     }
835
836     if (!defined $what or $what =~ /^\s*$/) {
837         &::DEBUG("what $what is not defined.");
838         return;
839     }
840     if (!defined $value or $value =~ /^\s*$/) {
841         &::DEBUG("value $value is not defined.");
842         return;
843     }
844
845     &::DEBUG("do_set: TODO...");
846 }
847
848 1;