]> git.donarmstrong.com Git - infobot.git/blob - src/Modules/Factoids.pl
- forgot to set forked{}{PID} in addForked
[infobot.git] / src / Modules / Factoids.pl
1 #
2 #  Factoids.pl: Helpers for generating factoids statistics.
3 #       Author: dms
4 #      Version: v0.1 (20000514)
5 #     Splitted: SQLExtras.pl
6 #
7
8 if (&IsParam("useStrict")) { use strict; }
9
10 ###
11 # Usage: &CmdFactInfo($faqtoid, $query);
12 sub CmdFactInfo {
13     my ($faqtoid, $query) = (lc $_[0], $_[1]);
14     my @array;
15     my $string = "";
16
17     if ($faqtoid eq "") {
18         &help("factinfo");
19         return;
20     }
21
22     my $i = 0;
23     my %factinfo;
24     my @factinfo = &getFactInfo($faqtoid,"*");
25     foreach ( &dbGetRowInfo("factoids") ) {
26         $factinfo{$_} = $factinfo[$i] || '';
27         $i++;
28     }
29
30     # factoid does not exist.
31     if (scalar @factinfo <= 1) {
32         &performReply("there's no such factoid as \002$faqtoid\002");
33         return;
34     }
35
36     # fix for problem observed by asuffield.
37     # why did it happen though?
38     if (!$factinfo{'factoid_value'}) {
39         &performReply("there's no such factoid as \002$faqtoid\002; deleted because we don't have factoid_value!");
40         foreach (keys %factinfo) {
41             &DEBUG("factinfo{$_} => '$factinfo{$_}'.");
42         }
43 ###     &delFactoid($faqtoid);
44         return;
45     }
46
47     # created:
48     if ($factinfo{'created_by'}) {
49
50         $factinfo{'created_by'} =~ s/\!/ </;
51         $factinfo{'created_by'} .= ">";
52         $string  = "created by $factinfo{'created_by'}";
53
54         my $time = $factinfo{'created_time'};
55         if ($time) {
56             if (time() - $time > 60*60*24*7) {
57                 my $days = int( (time() - $time)/60/60/24 );
58                 $string .= " at \037". scalar(localtime $time). "\037" .
59                                 " ($days days)";
60             } else {
61                 $string .= " ".&Time2String(time() - $time)." ago";
62             }
63         }
64
65         push(@array,$string);
66     }
67
68     # modified:
69 #    if ($factinfo{'modified_by'}) {
70 #       $string = "last modified";
71 #
72 #       my $time = $factinfo{'modified_time'};
73 #       if ($time) {
74 #           if (time() - $time > 60*60*24*7) {
75 #               $string .= " at \037". scalar(localtime $time). "\037";
76 #           } else {
77 #               $string .= " ".&Time2String(time() - $time)." ago";
78 #           }
79 #       }
80 #
81 #       my @x;
82 #       foreach (split ",", $factinfo{'modified_by'}) {
83 #           /\!/;
84 #           push(@x, $`);
85 #       }
86 #       $string .= "by ".&IJoin(@x);
87 #
88 #       $i++;
89 #       push(@array,$string);
90 #    }
91
92     # requested:
93     if ($factinfo{'requested_by'}) {
94         my $requested_count = $factinfo{'requested_count'};
95
96         if ($requested_count) {
97             $string  = "it has been requested ";
98             if ($requested_count == 1) {
99                 $string .= "\002once\002";
100             } else {
101                 $string .= "\002". $requested_count. "\002 ".
102                         &fixPlural("time", $requested_count);
103             }
104         }
105
106         $string .= ", " if ($string ne "");
107
108         my $requested_by = $factinfo{'requested_by'};
109         $requested_by =~ /\!/;
110         $string .= "last by $`";
111
112         my $requested_time = $factinfo{'requested_time'};
113         if ($requested_time) {
114             if (time() - $requested_time > 60*60*24*7) {
115                 $string .= " at \037". scalar(localtime $requested_time). "\037";
116             } else {
117                 $string .= ", ".&Time2String(time() - $requested_time)." ago";
118             }
119         }
120
121         push(@array,$string);
122     }
123
124     # locked:
125     if ($factinfo{'locked_by'}) {
126         $factinfo{'locked_by'} =~ /\!/;
127         $string = "it has been locked by $`";
128
129         push(@array, $string);
130     }
131
132     # factoid was inserted not through the bot.
133     if (!scalar @array) {
134         &performReply("no extra info on \002$faqtoid\002");
135         return;
136     }
137
138     &performStrictReply("$factinfo{'factoid_key'} -- ". join("; ", @array) .".");
139     return;
140 }
141
142 sub CmdFactStats {
143     my ($type) = @_;
144
145     if ($type =~ /^author$/i) {
146         my %hash = &dbGetCol("factoids", "factoid_key","created_by");
147         my %author;
148
149         foreach (keys %hash) {
150             my $thisnuh = $hash{$_};
151
152             $thisnuh =~ /^(\S+)!\S+@\S+$/;
153             $author{lc $1}++;
154         }
155
156         if (!scalar keys %author) {
157             return 'sorry, no factoids with created_by field.';
158         }
159
160         # work-around.
161         my %count;
162         foreach (keys %author) {
163             $count{ $author{$_} }{$_} = 1;
164         }
165         undef %author;
166
167         my $count;
168         my @list;
169         foreach $count (sort { $b <=> $a } keys %count) {
170             my $author = join(", ", sort keys %{ $count{$count} });
171             push(@list, "$count by $author");
172         }
173
174         my $prefix = "factoid statistics by author: ";
175         return &formListReply(0, $prefix, @list);
176
177     } elsif ($type =~ /^broken$/i) {
178         &status("factstats(broken): starting...");
179         my $start_time  = &timeget();
180         my %data        = &dbGetCol("factoids", "factoid_key","factoid_value");
181         my @list;
182
183         my $delta_time  = &timedelta($start_time);
184         &status(sprintf("factstats(broken): %.02f sec to retreive all factoids.", $delta_time)) if ($delta_time > 0);
185         $start_time     = &timeget();
186
187         # parse the factoids.
188         foreach (keys %data) {
189             if (&validFactoid($_, $data{$_}) == 0) {
190                 s/([\,\;]+)/\037$1\037/g;       # highlight chars.
191                 push(@list, $_);                # push it.
192             }
193         }
194
195         $delta_time     = &timedelta($start_time);
196         &status(sprintf("factstats(broken): %.02f sec to complete.", $delta_time)) if ($delta_time > 0);
197
198         # bail out on no results.
199         if (scalar @list == 0) {
200             return 'no broken factoids... wooohoo.';
201         }
202
203         # parse the results.
204         my $prefix = "broken factoid ";
205         return &formListReply(1, $prefix, @list);
206
207     } elsif ($type =~ /^deadredir$/i) {
208         my @list = &searchTable("factoids", "factoid_key",
209                         "factoid_value", "^<REPLY> see ");
210         my %redir;
211         my $f;
212
213         for (@list) {
214             my $factoid = $_;
215             my $val = &getFactInfo($factoid, "factoid_value");
216             if ($val =~ /^<REPLY> see( also)? (.*?)\.?$/i) {
217                 my $redirf = lc $2;
218                 my $redir = &getFactInfo($redirf, "factoid_value");
219                 next if (defined $redir);
220                 next if (length $val > 50);
221
222                 $redir{$redirf}{$factoid} = 1;
223             }
224         }
225
226         my @newlist;
227         foreach $f (keys %redir) {
228             my @sublist = keys %{ $redir{$f} };
229             for (@sublist) {
230                 s/([\,\;]+)/\037$1\037/g;
231             }
232
233             push(@newlist, join(', ', @sublist)." => $f");
234         }
235
236         # parse the results.
237         my $prefix = "Loose link (dead) redirections in factoids ";
238         return &formListReply(1, $prefix, @newlist);
239
240     } elsif ($type =~ /^dup(licate|e)$/i) {
241         &status("factstats(dupe): starting...");
242         my $start_time  = &timeget();
243         my %hash        = &dbGetCol("factoids", "factoid_key", "factoid_value", 1);
244         my $refs        = 0;
245         my @list;
246         my $v;
247
248         foreach $v (keys %hash) {
249             my $count = scalar(keys %{ $hash{$v} });
250             next if ($count == 1);
251
252             my @sublist;
253             foreach (keys %{ $hash{$v} }) {
254                 if ($v =~ /^<REPLY> see /i) {
255                     $refs++;
256                     next;
257                 }
258
259                 s/([\,\;]+)/\037$1\037/g;
260                 if ($_ eq "") {
261                     &WARN("dupe: _ = NULL. should never happen!.");
262                     next;
263                 }
264                 push(@sublist, $_);
265             }
266
267             next unless (scalar @sublist);
268
269             push(@list, join(", ", @sublist));
270         }
271
272         &status("factstats(dupe): (good) dupe refs: $refs.");
273         my $delta_time  = &timedelta($start_time);
274         &status(sprintf("factstats(dupe): %.02f sec to complete", $delta_time)) if ($delta_time > 0);
275
276         # bail out on no results.
277         if (scalar @list == 0) {
278             return "no duplicate factoids... woohoo.";
279         }
280
281         # parse the results.
282         my $prefix = "dupe factoid ";
283         return &formListReply(1, $prefix, @list);
284
285     } elsif ($type =~ /^nullfactoids$/i) {
286         my $query = "SELECT factoid_key,factoid_value FROM factoids WHERE factoid_value=''";
287         my $sth = $dbh->prepare($query);
288         &ERROR("factstats(null): => '$query'.") unless $sth->execute;
289
290         my @list;
291         while (my @row = $sth->fetchrow_array) {
292             if ($row[1] ne "") {
293                 &DEBUG("row[1] != NULL for $row[0].");
294                 next;
295             }
296
297             &DEBUG("row[0] => '$row[0]'.");
298             push(@list, $row[0]);
299         }
300         $sth->finish;
301
302         # parse the results.
303         my $prefix = "NULL factoids (not deleted yet) ";
304         return &formListReply(1, $prefix, @list);
305
306     } elsif ($type =~ /^(2|too)short$/i) {
307         # Custom select statement.
308         my $query = "SELECT factoid_key,factoid_value FROM factoids WHERE length(factoid_value) <= 40";
309         my $sth = $dbh->prepare($query);
310         &ERROR("factstats(lame): => '$query'.") unless $sth->execute;
311
312         my @list;
313         while (my @row = $sth->fetchrow_array) {
314             my($key,$val) = ($row[0], $row[1]);
315             my $match = 0;
316             $match++ if ($val =~ /\s{3,}/);
317             next unless ($match);
318
319             my $v = &getFactoid($val);
320             if (defined $v) {
321                 &DEBUG("key $key => $val => $v");
322             }
323
324             $key =~ s/\,/\037\,\037/g;
325             push(@list, $key);
326         }
327         $sth->finish;
328
329         # parse the results.
330         my $prefix = "Lame factoids ";
331         return &formListReply(1, $prefix, @list);
332
333     } elsif ($type =~ /^listfix$/i) {
334         # Custom select statement.
335         my $query = "SELECT factoid_key,factoid_value FROM factoids";
336         my $sth = $dbh->prepare($query);
337         &ERROR("factstats(listfix): => '$query'.") unless $sth->execute;
338
339         my @list;
340         while (my @row = $sth->fetchrow_array) {
341             my($key,$val) = ($row[0], $row[1]);
342             my $match = 0;
343             $match++ if ($val =~ /\S+,? or \S+,? or \S+,? or \S+,?/);
344             next unless ($match);
345
346             $key =~ s/\,/\037\,\037/g;
347             push(@list, $key);
348             $val =~ s/,? or /, /g;
349             &DEBUG("fixed: => $val.");
350             &setFactInfo($key,"factoid_value", $val);
351         }
352         $sth->finish;
353
354         # parse the results.
355         my $prefix = "Inefficient lists fixed ";
356         return &formListReply(1, $prefix, @list);
357
358     } elsif ($type =~ /^locked$/i) {
359         my %hash = &dbGetCol("factoids", "factoid_key","locked_by");
360         my @list = keys %hash;
361
362         for (@list) {
363             s/([\,\;]+)/\037$1\037/g;
364         }
365
366         my $prefix = "factoid statistics on $type ";
367         return &formListReply(0, $prefix, @list);
368
369     } elsif ($type =~ /^new$/i) {
370         my %hash = &dbGetCol("factoids", "factoid_key","created_time");
371         my %age;
372
373         foreach (keys %hash) {
374             my $created_time = $hash{$_};
375             my $delta_time   = time() - $created_time;
376             next if ($delta_time >= 60*60*24);
377
378             $age{$delta_time}{$_} = 1;
379         }
380
381         if (scalar keys %age == 0) {
382             return "sorry, no new factoids.";
383         }
384
385         my @list;
386         foreach (sort {$a <=> $b} keys %age) {
387             push(@list, join(",", keys %{ $age{$_} }));
388         }
389
390         my $prefix = "new factoids in the last 24hours ";
391         return &formListReply(0, $prefix, @list);
392
393     } elsif ($type =~ /^part(ial)?dupe$/i) {
394         ### requires "custom" select statement... oh well...
395         my $start_time  = &timeget();
396
397         # form length|key and key=length hash list.
398         &status("factstats(partdupe): forming length hash list.");
399         my $query = "SELECT factoid_key,factoid_value,length(factoid_value) AS length FROM factoids WHERE length(factoid_value) >= 192 ORDER BY length";
400         my $sth = $dbh->prepare($query);
401         &ERROR("factstats(partdupe): => '$query'.") unless $sth->execute;
402
403         my (@key, @list);
404         my (%key, %length);
405         while (my @row = $sth->fetchrow_array) {
406             $length{$row[2]}{$row[0]} = 1;      # length(value)|key.
407             $key{$row[0]} = $row[1];            # key=value.
408             push(@key, $row[0]);
409         }
410         $sth->finish;
411         &status("factstats(partdupe): total keys => '". scalar(@key) ."'.");
412         &status("factstats(partdupe): now deciphering data gathered");
413
414         my @length = sort { $a <=> $b } keys %length;
415         my $key;
416
417         foreach $key (@key) {
418             shift @length if (length $key{$key} == $length[0]);
419
420             my $val = quotemeta $key{$key};
421             my @sublist;
422             my $length;
423             foreach $length (@length) {
424                 foreach (keys %{ $length{$length} }) {
425                     if ($key{$_} =~ /^$val/i) {
426                         s/([\,\;]+)/\037$1\037/g;
427                         s/( and|and )/\037$1\037/g;
428                         push(@sublist,$key." and ".$_);
429                     }
430                 }
431             }
432             push(@list, join(" ,",@sublist)) if (scalar @sublist);
433         }
434
435         my $delta_time = sprintf("%.02fs", &timedelta($start_time) );
436         &status("factstats(partdupe): $delta_time sec to complete.") if ($delta_time > 0);
437
438         # bail out on no results.
439         if (scalar @list == 0) {
440             return "no initial partial duplicate factoids... woohoo.";
441         }
442
443         # parse the results.
444         my $prefix = "initial partial dupe factoid ";
445         return &formListReply(1, $prefix, @list);
446
447     } elsif ($type =~ /^profanity$/i) {
448         my %data = &dbGetCol("factoids", "factoid_key","factoid_value");
449         my @list;
450
451         foreach (keys %data) {
452             push(@list, $_) if (&hasProfanity($_." ".$data{$_}));
453         }
454
455         # parse the results.
456         my $prefix = "Profanity in factoids ";
457         return &formListReply(1, $prefix, @list);
458
459     } elsif ($type =~ /^redir(ection)?$/i) {
460         my @list = &searchTable("factoids", "factoid_key",
461                         "factoid_value", "^<REPLY> see ");
462         my %redir;
463         my $f;
464
465         for (@list) {
466             my $factoid = $_;
467             my $val = &getFactInfo($factoid, "factoid_value");
468             if ($val =~ /^<REPLY> see( also)? (.*?)\.?$/i) {
469                 my $redir       = lc $2;
470                 my $redirval    = &getFactInfo($redir, "factoid_value");
471                 if (defined $redirval) {
472                     $redir{$redir}{$factoid} = 1;
473                 } else {
474                     &WARN("factstats(redir): '$factoid' has loose link => '$redir'.");
475                 }
476             }
477         }
478
479         my @newlist;
480         foreach $f (keys %redir) {
481             my @sublist = keys %{ $redir{$f} };
482             for (@sublist) {
483                 s/([\,\;]+)/\037$1\037/g;
484             }
485
486             push(@newlist, "$f => ". join(', ', @sublist));
487         }
488
489         # parse the results.
490         my $prefix = "Redirections in factoids ";
491         return &formListReply(1, $prefix, @newlist);
492
493     } elsif ($type =~ /^request(ed)?$/i) {
494         my %hash = &dbGetCol("factoids", "factoid_key", "requested_count",1);
495
496         if (!scalar keys %hash) {
497             return 'sorry, no factoids have been questioned.';
498         }
499
500         my $count;
501         my @list;
502         my $total       = 0;
503         foreach $count (sort {$b <=> $a} keys %hash) {
504             my @faqtoids = sort keys %{ $hash{$count} };
505
506             for (@faqtoids) {
507                 s/([\,\;]+)/\037$1\037/g;
508             }
509             $total      += $count * scalar(@faqtoids);
510
511             push(@list, "$count - ". join(", ", @faqtoids));
512         }
513         unshift(@list, "\037$total - TOTAL\037");
514
515         my $prefix = "factoid statistics on $type ";
516         return &formListReply(0, $prefix, @list);
517
518     } elsif ($type =~ /^requesters?$/i) {
519         my %hash = &dbGetCol("factoids", "factoid_key","requested_by");
520         my %requester;
521
522         foreach (keys %hash) {
523             my $thisnuh = $hash{$_};
524
525             $thisnuh =~ /^(\S+)!\S+@\S+$/;
526             $requester{lc $1}++;
527         }
528
529         if (!scalar keys %requester) {
530             return 'sorry, no factoids with requested_by field.';
531         }
532
533         # work-around.
534         my %count;
535         foreach (keys %requester) {
536             $count{ $requester{$_} }{$_} = 1;
537         }
538         undef %requester;
539
540         my $count;
541         my @list;
542         my $total       = 0;
543         my $users       = 0;
544         foreach $count (sort { $b <=> $a } keys %count) {
545             my $requester = join(", ", sort keys %{ $count{$count} });
546             $total      += $count * scalar(keys %{ $count{$count} });
547             $users      += scalar(keys %{ $count{$count} });
548             push(@list, "$count by $requester");
549         }
550         unshift(@list, "\037$total TOTAL REQUESTS; $users UNIQUE REQUESTERS\037");
551         # should not the above value be the same as collected by
552         # 'requested'? soemthing weird is going on!
553
554         my $prefix = "rank of top factoid requesters: ";
555         return &formListReply(0, $prefix, @list);
556
557     } elsif ($type =~ /^seefix$/i) {
558         my @list = &searchTable("factoids", "factoid_key",
559                         "factoid_value", "^see ");
560         my @newlist;
561         my $fixed = 0;
562         my %loop;
563         my $f;
564
565         for (@list) {
566             my $factoid = $_;
567             my $val = &getFactInfo($factoid, "factoid_value");
568         
569             next unless ($val =~ /^see( also)? (.*?)\.?$/i);
570
571             my $redirf  = lc $2;
572             my $redir   = &getFactInfo($redirf, "factoid_value");
573
574             if ($redirf =~ /^\Q$factoid\W$/i) {
575                 &delFactoid($factoid);
576                 $loop{$factoid} = 1;
577             }
578
579             if (defined $redir) {       # good.
580                 &setFactInfo($factoid,"factoid_value","<REPLY> see $redir");
581                 $fixed++;
582             } else {
583                 push(@newlist, $redirf);
584             }
585         }
586
587         # parse the results.
588         &msg($who, "Fixed $fixed factoids.");
589         &msg($who, "Self looped factoids removed: ".
590                 sort(keys %loop) ) if (scalar keys %loop);
591
592         my $prefix = "Loose link (dead) redirections in factoids ";
593         return &formListReply(1, $prefix, @newlist);
594
595     } elsif ($type =~ /^(2|too)long$/i) {
596         my @list;
597
598         # factoid_key.
599         $query = "SELECT factoid_key FROM factoids WHERE length(factoid_key) >= $param{'maxKeySize'}";
600         my $sth = $dbh->prepare($query);
601         $sth->execute;
602         while (my @row = $sth->fetchrow_array) {
603             push(@list,$row[0]);
604         }
605
606         # factoid_value.
607         my $query = "SELECT factoid_key,factoid_value FROM factoids WHERE length(factoid_value) >= $param{'maxDataSize'}";
608         $sth = $dbh->prepare($query);
609         $sth->execute;
610         while (my @row = $sth->fetchrow_array) {
611             push(@list,sprintf("\002%s\002 - %s", length($row[1]), $row[0]));
612         }
613
614         if (scalar @list == 0) {
615             return "good. no factoids exceed length.";
616         }
617
618         # parse the results.
619         my $prefix = "factoid key||value exceeding length ";
620         return &formListReply(1, $prefix, @list);
621
622     } elsif ($type =~ /^unrequest(ed)?$/i) {
623         my @list = &dbRawReturn("SELECT factoid_key FROM factoids WHERE requested_count IS NULL");
624
625         for (@list) {
626             s/([\,\;]+)/\037$1\037/g;
627         }
628
629         my $prefix = "Unrequested factoids ";
630         return &formListReply(0, $prefix, @list);
631     }
632
633     return "error: invalid type => '$type'.";
634 }
635
636 sub CmdListAuth {
637     my ($query) = @_;
638     my @list = &searchTable("factoids","factoid_key", "created_by", "^$query!");
639
640     my $prefix = "factoid author list by '$query' ";
641     &performStrictReply( &formListReply(1, $prefix, @list) );
642 }
643
644 1;