]> git.donarmstrong.com Git - infobot.git/blob - src/Modules/Factoids.pl
bbc01349b873692ae4ebb3bfc26f172eaed08879
[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 use strict;
9
10 use vars qw($dbh $who);
11 use vars qw(%param);
12
13 ###
14 # Usage: &CmdFactInfo($faqtoid, $query);
15 sub CmdFactInfo {
16     my ( $faqtoid, $query ) = ( lc $_[0], $_[1] );
17     my @array;
18     my $string = '';
19
20     if ( $faqtoid eq '' ) {
21         &help('factinfo');
22         return;
23     }
24
25     my %factinfo =
26       &sqlSelectRowHash( 'factoids', '*', { factoid_key => $faqtoid } );
27
28     # factoid does not exist.
29     if ( scalar( keys %factinfo ) <= 1 ) {
30         &performReply("there's no such factoid as \002$faqtoid\002");
31         return;
32     }
33
34     # fix for problem observed by asuffield.
35     # why did it happen though?
36     if ( !$factinfo{'factoid_value'} ) {
37         &performReply(
38 "there's no such factoid as \002$faqtoid\002; deleted because we don't have factoid_value!"
39         );
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"
59                   . scalar( gmtime $time ) . "\037"
60                   . " ($days days)";
61             }
62             else {
63                 $string .= ' ' . &Time2String( time() - $time ) . ' ago';
64             }
65         }
66
67         push( @array, $string );
68     }
69
70     # modified: (TimRiker asks: why do you keep turning this off?)
71     if ( $factinfo{'modified_by'} ) {
72         $string = 'last modified';
73
74         my $time = $factinfo{'modified_time'};
75         if ($time) {
76             if ( time() - $time > 60 * 60 * 24 * 7 ) {
77                 $string .= " at \037" . scalar( gmtime $time ) . "\037";
78             }
79             else {
80                 $string .= ' ' . &Time2String( time() - $time ) . ' ago ';
81             }
82         }
83
84         $string .= ' by ' . ( split ',', $factinfo{'modified_by'} )[0];
85
86         push( @array, $string );
87     }
88
89     # requested:
90     if ( $factinfo{'requested_by'} ) {
91         my $requested_count = $factinfo{'requested_count'};
92
93         if ($requested_count) {
94             $string = 'it has been requested ';
95             if ( $requested_count == 1 ) {
96                 $string .= "\002once\002";
97             }
98             else {
99                 $string .= "\002"
100                   . $requested_count . "\002 "
101                   . &fixPlural( 'time', $requested_count );
102             }
103
104             my $requested_by = $factinfo{'requested_by'};
105             $requested_by =~ /\!/;
106             $string .= ", last by $`";
107
108             my $requested_time = $factinfo{'requested_time'};
109             if ($requested_time) {
110                 if ( time() - $requested_time > 60 * 60 * 24 * 7 ) {
111                     $string .=
112                       " at \037" . scalar( localtime $requested_time ) . "\037";
113                 }
114                 else {
115                     $string .=
116                       ', ' . &Time2String( time() - $requested_time ) . ' ago';
117                 }
118             }
119         }
120         else {
121             $string = 'has not been requested yet';
122         }
123
124         push( @array, $string );
125     }
126
127     # locked:
128     if ( $factinfo{'locked_by'} ) {
129         $factinfo{'locked_by'} =~ /\!/;
130         $string = "it has been locked by $`";
131
132         push( @array, $string );
133     }
134
135     # factoid was inserted not through the bot.
136     if ( !scalar @array ) {
137         &performReply("no extra info on \002$faqtoid\002");
138         return;
139     }
140
141     &performStrictReply(
142         "$factinfo{'factoid_key'} -- " . join( '; ', @array ) . '.' );
143     return;
144 }
145
146 sub CmdFactStats {
147     my ($type) = @_;
148
149     if ( $type =~ /^author$/i ) {
150         my %hash = &sqlSelectColHash(
151             'factoids', 'factoid_key,created_by',
152             undef,      'WHERE created_by IS NOT NULL'
153         );
154         my %author;
155
156         foreach my $factoid ( keys %hash ) {
157             my $thisnuh = $hash{$factoid};
158
159             $thisnuh =~ /^(\S+)!\S+@\S+$/;
160             $author{ lc $1 }++;
161         }
162
163         if ( !scalar keys %author ) {
164             return 'sorry, no factoids with created_by field.';
165         }
166
167         # work-around.
168         my %count;
169         foreach ( keys %author ) {
170             $count{ $author{$_} }{$_} = 1;
171         }
172         undef %author;
173
174         my $count;
175         my @list;
176         foreach $count ( sort { $b <=> $a } keys %count ) {
177             my $author = join( ', ', sort keys %{ $count{$count} } );
178             push( @list, "$count by $author" );
179         }
180
181         my $prefix = 'factoid statistics by author: ';
182         return &formListReply( 0, $prefix, @list );
183
184     }
185     elsif ( $type =~ /^vandalism$/i ) {
186         &status('factstats(vandalism): starting...');
187         my $start_time = &timeget();
188         my %data       = &sqlSelectColHash(
189             'factoids', 'factoid_key,factoid_value',
190             undef,      'WHERE factoid_value IS NOT NULL'
191         );
192         my @list;
193
194         my $delta_time = &timedelta($start_time);
195         &status(
196             sprintf(
197                 'factstats(vandalism): %.02f sec to retreive all factoids.',
198                 $delta_time )
199         ) if ( $delta_time > 0 );
200         $start_time = &timeget();
201
202         # parse the factoids.
203         foreach ( keys %data ) {
204             if ( &validFactoid( $_, $data{$_} ) == 0 ) {
205                 s/([\,\;]+)/\037$1\037/g;    # highlight chars.
206                 push( @list, $_ );           # push it.
207             }
208         }
209
210         $delta_time = &timedelta($start_time);
211         &status(
212             sprintf( 'factstats(vandalism): %.02f sec to complete.',
213                 $delta_time )
214         ) if ( $delta_time > 0 );
215
216         # bail out on no results.
217         if ( scalar @list == 0 ) {
218             return 'no vandalised factoids... wooohoo.';
219         }
220
221         # parse the results.
222         my $prefix = 'Vandalised factoid ';
223         return &formListReply( 1, $prefix, @list );
224
225     }
226     elsif ( $type =~ /^total$/i ) {
227         &status('factstats(total): starting...');
228         my $start_time = &timeget();
229         my @list;
230         my $str;
231         my ( $i, $j );
232         my %hash;
233
234         ### lets do it.
235         # total factoids requests.
236         $i = &sumKey( 'factoids', 'requested_count' );
237         push( @list, "total requests - $i" );
238
239         # total factoids modified.
240         $str = &countKeys( 'factoids', 'modified_by' );
241         push( @list, "total modified - $str" );
242
243         # total factoids modified.
244         $j   = &countKeys( 'factoids', 'requested_count' );
245         $str = &countKeys( 'factoids', 'factoid_key' );
246         push( @list, 'total non-requested - ' . ( $str - $i ) );
247
248         # average request/factoid.
249         # i/j == total(requested_count)/count(requested_count)
250         $str = sprintf( '%.01f', $i / $j );
251         push( @list, "average requested per factoid - $str" );
252
253         # total prepared for deletion.
254         $str =
255           scalar(
256             &searchTable( 'factoids', 'factoid_key', 'factoid_value', ' #DEL' )
257           );
258         push( @list, "total prepared for deletion - $str" );
259
260         # total unique authors.
261         # TODO: convert to sqlSelectColHash ? (or ColArray?)
262         foreach (
263             &sqlRawReturn(
264                 'SELECT created_by FROM factoids WHERE created_by IS NOT NULL')
265           )
266         {
267             /^(\S+)!/;
268             my $nick = lc $1;
269             $hash{$nick}++;
270         }
271         push( @list, 'total unique authors - ' . ( scalar keys %hash ) );
272         undef %hash;
273
274         # total unique requesters.
275         foreach (
276             &sqlRawReturn(
277 'SELECT requested_by FROM factoids WHERE requested_by IS NOT NULL'
278             )
279           )
280         {
281             /^(\S+)!/;
282             my $nick = lc $1;
283             $hash{$nick}++;
284         }
285         push( @list, 'total unique requesters - ' . ( scalar keys %hash ) );
286         undef %hash;
287
288         ### end of 'job'.
289
290         my $delta_time = &timedelta($start_time);
291         &status(
292             sprintf( 'factstats(broken): %.02f sec to retreive all factoids.',
293                 $delta_time )
294         ) if ( $delta_time > 0 );
295         $start_time = &timeget();
296
297         # bail out on no results.
298         if ( scalar @list == 0 ) {
299             return 'no broken factoids... wooohoo.';
300         }
301
302         # parse the results.
303         my $prefix = 'General factoid statistics ';
304         return &formListReply( 1, $prefix, @list );
305
306     }
307     elsif ( $type =~ /^deadredir$/i ) {
308         my @list =
309           &searchTable( 'factoids', 'factoid_key', 'factoid_value',
310             '^<REPLY> see ' );
311         my %redir;
312         my $f;
313
314         for (@list) {
315             my $factoid = $_;
316             my $val = &getFactInfo( $factoid, 'factoid_value' );
317             if ( $val =~ /^<REPLY> ?see( also)? (.*?)\.?$/i ) {
318                 my $redirf = lc $2;
319                 my $redir = &getFactInfo( $redirf, 'factoid_value' );
320                 next if ( defined $redir );
321                 next if ( length $val > 50 );
322
323                 $redir{$redirf}{$factoid} = 1;
324             }
325         }
326
327         my @newlist;
328         foreach $f ( keys %redir ) {
329             my @sublist = keys %{ $redir{$f} };
330             for (@sublist) {
331                 s/([\,\;]+)/\037$1\037/g;
332             }
333
334             push( @newlist, join( ', ', @sublist ) . " => $f" );
335         }
336
337         # parse the results.
338         my $prefix = 'Loose link (dead) redirections in factoids ';
339         return &formListReply( 1, $prefix, @newlist );
340
341     }
342     elsif ( $type =~ /^dup(licate|e)$/i ) {
343         &status('factstats(dupe): starting...');
344         my $start_time = &timeget();
345         my %hash =
346           &sqlSelectColHash( 'factoids', 'factoid_key,factoid_value', undef,
347             'WHERE factoid_value IS NOT NULL', 1 );
348         my $refs = 0;
349         my @list;
350         my $v;
351
352         foreach $v ( keys %hash ) {
353             my $count = scalar( keys %{ $hash{$v} } );
354             next if ( $count == 1 );
355
356             my @sublist;
357             foreach ( keys %{ $hash{$v} } ) {
358                 if ( $v =~ /^<REPLY> see /i ) {
359                     $refs++;
360                     next;
361                 }
362
363                 s/([\,\;]+)/\037$1\037/g;
364                 if ( $_ eq '' ) {
365                     &WARN('dupe: _ = NULL. should never happen!.');
366                     next;
367                 }
368                 push( @sublist, $_ );
369             }
370
371             next unless ( scalar @sublist );
372
373             push( @list, join( ', ', @sublist ) );
374         }
375
376         &status("factstats(dupe): (good) dupe refs: $refs.");
377         my $delta_time = &timedelta($start_time);
378         &status(
379             sprintf( 'factstats(dupe): %.02f sec to complete', $delta_time ) )
380           if ( $delta_time > 0 );
381
382         # bail out on no results.
383         if ( scalar @list == 0 ) {
384             return 'no duplicate factoids... woohoo.';
385         }
386
387         # parse the results.
388         my $prefix = 'dupe factoid ';
389         return &formListReply( 1, $prefix, @list );
390
391     }
392     elsif ( $type =~ /^nullfactoids$/i ) {
393         my $query =
394 "SELECT factoid_key,factoid_value FROM factoids WHERE factoid_value=''";
395         my $sth = $dbh->prepare($query);
396         &ERROR("factstats(null): => '$query'.") unless $sth->execute;
397
398         my @list;
399         while ( my @row = $sth->fetchrow_array ) {
400             if ( $row[1] ne '' ) {
401                 &DEBUG("row[1] != NULL for $row[0].");
402                 next;
403             }
404
405             &DEBUG("row[0] => '$row[0]'.");
406             push( @list, $row[0] );
407         }
408         $sth->finish;
409
410         # parse the results.
411         my $prefix = 'NULL factoids (not deleted yet) ';
412         return &formListReply( 1, $prefix, @list );
413
414     }
415     elsif ( $type =~ /^(2|too)short$/i ) {
416
417         # Custom select statement.
418         my $query =
419 'SELECT factoid_key,factoid_value FROM factoids WHERE length(factoid_value) <= 40';
420         my $sth = $dbh->prepare($query);
421         &ERROR("factstats(lame): => '$query'.") unless $sth->execute;
422
423         my @list;
424         while ( my @row = $sth->fetchrow_array ) {
425             my ( $key, $val ) = ( $row[0], $row[1] );
426             my $match = 0;
427             $match++ if ( $val =~ /\s{3,}/ );
428             next unless ($match);
429
430             my $v = &getFactoid($val);
431             if ( defined $v ) {
432                 &DEBUG("key $key => $val => $v");
433             }
434
435             $key =~ s/\,/\037\,\037/g;
436             push( @list, $key );
437         }
438         $sth->finish;
439
440         # parse the results.
441         my $prefix = 'Lame factoids ';
442         return &formListReply( 1, $prefix, @list );
443
444     }
445     elsif ( $type =~ /^listfix$/i ) {
446
447         # Custom select statement.
448         my $query = 'SELECT factoid_key,factoid_value FROM factoids';
449         my $sth   = $dbh->prepare($query);
450         &ERROR("factstats(listfix): => '$query'.") unless $sth->execute;
451
452         my @list;
453         while ( my @row = $sth->fetchrow_array ) {
454             my ( $key, $val ) = ( $row[0], $row[1] );
455             my $match = 0;
456             $match++ if ( $val =~ /\S+,? or \S+,? or \S+,? or \S+,?/ );
457             next unless ($match);
458
459             $key =~ s/\,/\037\,\037/g;
460             push( @list, $key );
461             $val =~ s/,? or /, /g;
462             &DEBUG("fixed: => $val.");
463             &setFactInfo( $key, 'factoid_value', $val );
464         }
465         $sth->finish;
466
467         # parse the results.
468         my $prefix = 'Inefficient lists fixed ';
469         return &formListReply( 1, $prefix, @list );
470
471     }
472     elsif ( $type =~ /^locked$/i ) {
473         my %hash = &sqlSelectColHash(
474             'factoids', 'factoid_key,locked_by',
475             undef,      'WHERE locked_by IS NOT NULL'
476         );
477         my @list = keys %hash;
478
479         for (@list) {
480             s/([\,\;]+)/\037$1\037/g;
481         }
482
483         my $prefix = "factoid statistics on $type ";
484         return &formListReply( 0, $prefix, @list );
485
486     }
487     elsif ( $type =~ /^new$/i ) {
488         my %hash = &sqlSelectColHash(
489             'factoids', 'factoid_key,created_time',
490             undef,      'WHERE created_time IS NOT NULL'
491         );
492         my %age;
493
494         foreach ( keys %hash ) {
495             my $created_time = $hash{$_};
496             my $delta_time   = time() - $created_time;
497             next if ( $delta_time >= 60 * 60 * 24 );
498
499             $age{$delta_time}{$_} = 1;
500         }
501
502         if ( scalar keys %age == 0 ) {
503             return 'sorry, no new factoids.';
504         }
505
506         my @list;
507         foreach ( sort { $a <=> $b } keys %age ) {
508             push( @list, join( ',', keys %{ $age{$_} } ) );
509         }
510
511         my $prefix = 'new factoids in the last 24hours ';
512         return &formListReply( 0, $prefix, @list );
513
514     }
515     elsif ( $type =~ /^part(ial)?dupe$/i ) {
516         ### requires 'custom' select statement... oh well...
517         my $start_time = &timeget();
518
519         # form length|key and key=length hash list.
520         &status('factstats(partdupe): forming length hash list.');
521         my $query =
522 'SELECT factoid_key,factoid_value,length(factoid_value) AS length FROM factoids WHERE length(factoid_value) >= 192 ORDER BY length';
523         my $sth = $dbh->prepare($query);
524         &ERROR("factstats(partdupe): => '$query'.") unless $sth->execute;
525
526         my ( @key, @list );
527         my ( %key, %length );
528         while ( my @row = $sth->fetchrow_array ) {
529             $length{ $row[2] }{ $row[0] } = 1;    # length(value)|key.
530             $key{ $row[0] } = $row[1];            # key=value.
531             push( @key, $row[0] );
532         }
533         $sth->finish;
534         &status( "factstats(partdupe): total keys => '" . scalar(@key) . "'." );
535         &status('factstats(partdupe): now deciphering data gathered');
536
537         my @length = sort { $a <=> $b } keys %length;
538         my $key;
539
540         foreach $key (@key) {
541             shift @length if ( length $key{$key} == $length[0] );
542
543             my $val = quotemeta $key{$key};
544             my @sublist;
545             my $length;
546             foreach $length (@length) {
547                 foreach ( keys %{ $length{$length} } ) {
548                     if ( $key{$_} =~ /^$val/i ) {
549                         s/([\,\;]+)/\037$1\037/g;
550                         s/( and|and )/\037$1\037/g;
551                         push( @sublist, $key . ' and ' . $_ );
552                     }
553                 }
554             }
555             push( @list, join( ' ,', @sublist ) ) if ( scalar @sublist );
556         }
557
558         my $delta_time = sprintf( '%.02fs', &timedelta($start_time) );
559         &status("factstats(partdupe): $delta_time sec to complete.")
560           if ( $delta_time > 0 );
561
562         # bail out on no results.
563         if ( scalar @list == 0 ) {
564             return 'no initial partial duplicate factoids... woohoo.';
565         }
566
567         # parse the results.
568         my $prefix = 'initial partial dupe factoid ';
569         return &formListReply( 1, $prefix, @list );
570
571     }
572     elsif ( $type =~ /^profanity$/i ) {
573         my %data = &sqlSelectColHash(
574             'factoids', 'factoid_key,factoid_value',
575             undef,      'WHERE factoid_value IS NOT NULL'
576         );
577         my @list;
578
579         foreach ( keys %data ) {
580             push( @list, $_ ) if ( &hasProfanity( $_ . ' ' . $data{$_} ) );
581         }
582
583         # parse the results.
584         my $prefix = 'Profanity in factoids ';
585         return &formListReply( 1, $prefix, @list );
586
587     }
588     elsif ( $type =~ /^redir(ection)?$/i ) {
589         my @list =
590           &searchTable( 'factoids', 'factoid_key', 'factoid_value',
591             '^<REPLY> see ' );
592         my %redir;
593         my $f;
594         my $dangling = 0;
595
596         for (@list) {
597             my $factoid = $_;
598             my $val = &getFactInfo( $factoid, 'factoid_value' );
599             if ( $val =~ /^<REPLY> see( also)? (.*?)\.?$/i ) {
600                 my $redir = lc $2;
601                 my $redirval = &getFactInfo( $redir, 'factoid_value' );
602                 if ( defined $redirval ) {
603                     $redir{$redir}{$factoid} = 1;
604                 }
605                 else {
606                     &DEBUG(
607 "factstats(redir): '$factoid' has loose link => '$redir'."
608                     );
609                     $dangling++;
610                 }
611             }
612         }
613
614         my @newlist;
615         foreach $f ( keys %redir ) {
616             my @sublist = keys %{ $redir{$f} };
617             for (@sublist) {
618                 s/([\,\;]+)/\037$1\037/g;
619             }
620
621             push( @newlist, "$f => " . join( ', ', @sublist ) );
622         }
623
624         # parse the results.
625         my $prefix = "Redirections in factoids, $dangling dangling ";
626         return &formListReply( 1, $prefix, @newlist );
627
628     }
629     elsif ( $type =~ /^request(ed)?$/i ) {
630         my %hash =
631           &sqlSelectColHash( 'factoids', 'factoid_key,requested_count', undef,
632             'WHERE requested_count IS NOT NULL', 1 );
633
634         if ( !scalar keys %hash ) {
635             return 'sorry, no factoids have been questioned.';
636         }
637
638         my $count;
639         my @list;
640         my $total = 0;
641         foreach $count ( sort { $b <=> $a } keys %hash ) {
642             my @faqtoids = sort keys %{ $hash{$count} };
643
644             for (@faqtoids) {
645                 s/([\,\;]+)/\037$1\037/g;
646             }
647             $total += $count * scalar(@faqtoids);
648
649             push( @list, "$count - " . join( ', ', @faqtoids ) );
650         }
651         unshift( @list, "\037$total - TOTAL\037" );
652
653         my $prefix = "factoid statistics on $type ";
654         return &formListReply( 0, $prefix, @list );
655
656     }
657     elsif ( $type =~ /^reqrate$/i ) {
658         my %hash = &sqlSelectColHash(
659             'factoids',
660 "factoid_key,(unix_timestamp() - created_time)/requested_count as rate",
661             undef,
662 'WHERE requested_by IS NOT NULL and created_time IS NOT NULL ORDER BY rate LIMIT 15',
663             1
664         );
665
666         my $rate;
667         my @list;
668         my $total = 0;
669         my $users = 0;
670         foreach $rate ( sort { $b <=> $a } keys %hash ) {
671             my $f = join( ', ', sort keys %{ $hash{$rate} } );
672             my $str = "$f - " . &Time2String($rate);
673             $str =~ s/\002//g;
674             push( @list, $str );
675         }
676
677         my $prefix = "Rank of top factoid rate (time/req): ";
678         return &formListReply( 0, $prefix, @list );
679
680     }
681     elsif ( $type =~ /^requesters?$/i ) {
682         my %hash = &sqlSelectColHash(
683             'factoids', 'factoid_key,requested_by',
684             undef,      'WHERE requested_by IS NOT NULL'
685         );
686         my %requester;
687
688         foreach ( keys %hash ) {
689             my $thisnuh = $hash{$_};
690
691             $thisnuh =~ /^(\S+)!\S+@\S+$/;
692             $requester{ lc $1 }++;
693         }
694
695         if ( !scalar keys %requester ) {
696             return 'sorry, no factoids with requested_by field.';
697         }
698
699         # work-around.
700         my %count;
701         foreach ( keys %requester ) {
702             $count{ $requester{$_} }{$_} = 1;
703         }
704         undef %requester;
705
706         my $count;
707         my @list;
708         my $total = 0;
709         my $users = 0;
710         foreach $count ( sort { $b <=> $a } keys %count ) {
711             my $requester = join( ', ', sort keys %{ $count{$count} } );
712             $total += $count * scalar( keys %{ $count{$count} } );
713             $users += scalar( keys %{ $count{$count} } );
714             push( @list, "$count by $requester" );
715         }
716         unshift( @list,
717             "\037$total TOTAL REQUESTS; $users UNIQUE REQUESTERS\037" );
718
719         # should not the above value be the same as collected by
720         # 'requested'? soemthing weird is going on!
721
722         my $prefix = 'rank of top factoid requesters: ';
723         return &formListReply( 0, $prefix, @list );
724
725     }
726     elsif ( $type =~ /^seefix$/i ) {
727         my @list =
728           &searchTable( 'factoids', 'factoid_key', 'factoid_value', '^see ' );
729         my @newlist;
730         my $fixed = 0;
731         my %loop;
732         my $f;
733
734         for (@list) {
735             my $factoid = $_;
736             my $val = &getFactInfo( $factoid, 'factoid_value' );
737
738             next unless ( $val =~ /^see( also)? (.*?)\.?$/i );
739
740             my $redirf = lc $2;
741             my $redir = &getFactInfo( $redirf, 'factoid_value' );
742
743             if ( $redirf =~ /^\Q$factoid\W$/i ) {
744                 &delFactoid($factoid);
745                 $loop{$factoid} = 1;
746             }
747
748             if ( defined $redir ) {    # good.
749                 &setFactInfo( $factoid, 'factoid_value', "<REPLY> see $redir" );
750                 $fixed++;
751             }
752             else {
753                 push( @newlist, $redirf );
754             }
755         }
756
757         # parse the results.
758         &msg( $who, "Fixed $fixed factoids." );
759         &msg( $who, 'Self looped factoids removed: ' . keys %loop )
760           if ( scalar keys %loop );
761
762         my $prefix = "Loose link (dead) redirections in factoids ";
763         return &formListReply( 1, $prefix, @newlist );
764
765     }
766     elsif ( $type =~ /^(2|too)long$/i ) {
767         my @list;
768         my $query;
769
770         # factoid_key.
771         $query =
772 "SELECT factoid_key FROM factoids WHERE length(factoid_key) >= $param{'maxKeySize'}";
773         my $sth = $dbh->prepare($query);
774         $sth->execute;
775         while ( my @row = $sth->fetchrow_array ) {
776             push( @list, $row[0] );
777         }
778         $sth->finish;
779
780         # factoid_value.
781         $query =
782 "SELECT factoid_key,factoid_value FROM factoids WHERE length(factoid_value) >= $param{'maxDataSize'}";
783         $sth = $dbh->prepare($query);
784         $sth->execute;
785         while ( my @row = $sth->fetchrow_array ) {
786             push( @list,
787                 sprintf( "\002%s\002 - %s", length( $row[1] ), $row[0] ) );
788         }
789         $sth->finish;
790
791         if ( scalar @list == 0 ) {
792             return 'good. no factoids exceed length.';
793         }
794
795         # parse the results.
796         my $prefix = 'factoid key||value exceeding length ';
797         return &formListReply( 1, $prefix, @list );
798
799     }
800     elsif ( $type =~ /^unrequest(ed)?$/i ) {
801
802         # TODO: use sqlSelect()
803         my ($count) =
804           &sqlRawReturn(
805             "SELECT COUNT(*) FROM factoids WHERE requested_count = '0'");
806
807         return "Unrequested factoids: $count";
808     }
809
810     return "error: invalid type => '$type'.";
811 }
812
813 sub CmdListAuth {
814     my ($query) = @_;
815     my $maxshow = &::getChanConfDefault( 'maxListReplyCount', 15, $chan );
816     my @list =
817       &searchTable( 'factoids', 'factoid_key', 'created_by', "^$query!" );
818     @list = grep( !/\#DEL\#$/, @list ) if ( scalar(@list) > $maxshow );
819
820     my $prefix = "factoid author list by '$query' ";
821     &performStrictReply( &formListReply( 1, $prefix, @list ) );
822 }
823
824 1;
825
826 # vim:ts=4:sw=4:expandtab:tw=80