1 package Onis::Plugins::Core;
6 use Carp (qw(confess));
14 Plugin for the main table and the hourly-statistics. This is the most
15 complicated plugin so far.
19 use Onis::Config qw/get_config/;
20 use Onis::Html qw/html_escape get_filehandle/;
21 use Onis::Language qw/translate/;
22 use Onis::Users (qw(get_realname get_link get_image ident_to_name));
23 use Onis::Data::Core qw#get_all_nicks nick_to_ident ident_to_nick get_main_nick register_plugin#;
24 use Onis::Data::Persistent;
26 our $NickLinesCounter = Onis::Data::Persistent->new ('NickLinesCounter', 'nick',
28 lines00 lines01 lines02 lines03 lines04 lines05 lines06 lines07 lines08 lines09 lines10 lines11
29 lines12 lines13 lines14 lines15 lines16 lines17 lines18 lines19 lines20 lines21 lines22 lines23
32 our $NickWordsCounter = Onis::Data::Persistent->new ('NickWordsCounter', 'nick',
34 words00 words01 words02 words03 words04 words05 words06 words07 words08 words09 words10 words11
35 words12 words13 words14 words15 words16 words17 words18 words19 words20 words21 words22 words23
38 our $NickCharsCounter = Onis::Data::Persistent->new ('NickCharsCounter', 'nick',
40 chars00 chars01 chars02 chars03 chars04 chars05 chars06 chars07 chars08 chars09 chars10 chars11
41 chars12 chars13 chars14 chars15 chars16 chars17 chars18 chars19 chars20 chars21 chars22 chars23
45 our $QuoteCache = {}; # Saves per-nick information without any modification
46 our $QuoteData = {}; # Is generated before output. Nicks are merged according to Data::Core.
47 our $NickData = {}; # Same as above, but for nicks rather than quotes.
49 our @H_IMAGES = qw#dark-theme/h-red.png dark-theme/h-blue.png dark-theme/h-yellow.png dark-theme/h-green.png#;
50 our $QuoteCacheSize = 10;
54 our $SORT_BY = 'LINES';
55 our $DISPLAY_LINES = 'BOTH';
56 our $DISPLAY_WORDS = 'NONE';
57 our $DISPLAY_CHARS = 'NONE';
58 our $DISPLAY_TIMES = 0;
59 our $DISPLAY_IMAGES = 0;
60 our $DEFAULT_IMAGE = '';
61 our $BAR_HEIGHT = 130;
66 if (get_config ('quote_cache_size'))
68 my $tmp = get_config ('quote_cache_size');
70 $QuoteCacheSize = $tmp if ($tmp);
72 if (get_config ('quote_min'))
74 my $tmp = get_config ('quote_min');
76 $QuoteMin = $tmp if ($tmp);
78 if (get_config ('quote_max'))
80 my $tmp = get_config ('quote_max');
82 $QuoteMax = $tmp if ($tmp);
84 if (get_config ('min_word_length'))
86 my $tmp = get_config ('min_word_length');
88 $WORD_LENGTH = $tmp if ($tmp);
90 if (get_config ('display_lines'))
92 my $tmp = get_config ('display_lines');
95 if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
97 $DISPLAY_LINES = $tmp;
101 $tmp = get_config ('display_lines');
102 print STDERR $/, __FILE__, ": ``display_lines'' has been set to the invalid value ``$tmp''. ",
103 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``both''.";
106 if (get_config ('display_words'))
108 my $tmp = get_config ('display_words');
111 if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
113 $DISPLAY_WORDS = $tmp;
117 $tmp = get_config ('display_words');
118 print STDERR $/, __FILE__, ": ``display_words'' has been set to the invalid value ``$tmp''. ",
119 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``none''.";
122 if (get_config ('display_chars'))
124 my $tmp = get_config ('display_chars');
127 if (($tmp eq 'NONE') or ($tmp eq 'BAR') or ($tmp eq 'NUMBER') or ($tmp eq 'BOTH'))
129 $DISPLAY_CHARS = $tmp;
133 $tmp = get_config ('display_chars');
134 print STDERR $/, __FILE__, ": ``display_chars'' has been set to the invalid value ``$tmp''. ",
135 $/, __FILE__, ": Valid values are ``none'', ``bar'', ``number'' and ``both''. Using default value ``none''.";
138 if (get_config ('display_times'))
140 my $tmp = get_config ('display_times');
142 if ($tmp =~ m/true|on|yes/i)
146 elsif ($tmp =~ m/false|off|no/i)
152 print STDERR $/, __FILE__, ": ``display_times'' has been set to the invalid value ``$tmp''. ",
153 $/, __FILE__, ": Valid values are ``true'' and ``false''. Using default value ``false''.";
156 if (get_config ('display_images'))
158 my $tmp = get_config ('display_images');
160 if ($tmp =~ m/true|on|yes/i)
164 elsif ($tmp =~ m/false|off|no/i)
170 print STDERR $/, __FILE__, ": ``display_times'' has been set to the invalid value ``$tmp''. ",
171 $/, __FILE__, ": Valid values are ``true'' and ``false''. Using default value ``false''.";
174 if (get_config ('default_image'))
176 $DEFAULT_IMAGE = get_config ('default_image');
178 if (get_config ('sort_by'))
180 my $tmp = get_config ('sort_by');
183 if (($tmp eq 'LINES') or ($tmp eq 'WORDS') or ($tmp eq 'CHARS'))
189 $tmp = get_config ('sort_by');
190 print STDERR $/, __FILE__, ": ``sort_by'' has been set to the invalid value ``$tmp''. ",
191 $/, __FILE__, ": Valid values are ``lines'' and ``words''. Using default value ``lines''.";
194 if (get_config ('horizontal_images'))
196 my @tmp = get_config ('horizontal_images');
199 if (scalar (@tmp) != 4)
201 print STDERR $/, __FILE__, ": The number of horizontal images is not four. The output might look weird.", $/;
204 for ($i = 0; $i < 4; $i++)
206 if (!defined ($tmp[$i]))
211 $H_IMAGES[$i] = $tmp[$i];
214 if (get_config ('bar_height'))
216 my $tmp = get_config ('bar_height');
218 $BAR_HEIGHT = $tmp if ($tmp >= 10);
220 if (get_config ('bar_width'))
222 my $tmp = get_config ('bar_width');
224 $BAR_WIDTH = $tmp if ($tmp >= 10);
226 if (get_config ('longlines'))
228 my $tmp = get_config ('longlines');
230 $LongLines = $tmp if ($tmp);
232 if (get_config ('shortlines'))
234 my $tmp = get_config ('shortlines');
236 if ($tmp or ($tmp == 0))
242 register_plugin ('TEXT', \&add);
243 register_plugin ('ACTION', \&add);
244 register_plugin ('OUTPUT', \&output);
246 my $VERSION = '$Id$';
247 print STDERR $/, __FILE__, ": $VERSION" if ($::DEBUG);
255 my $nick = $data->{'nick'};
256 my $ident = $data->{'ident'};
257 my $hour = int ($data->{'hour'});
258 my $host = $data->{'host'};
259 my $text = $data->{'text'};
260 my $type = $data->{'type'};
261 my $time = $data->{'epoch'};
263 my $words = scalar (@{$data->{'words'}});
264 my $chars = length ($text);
266 if ($type eq 'ACTION')
268 $chars -= (length ($nick) + 3);
271 my @counter = $NickLinesCounter->get ($nick);
274 @counter = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
277 $NickLinesCounter->put ($nick, @counter);
279 @counter = $NickWordsCounter->get ($nick);
282 @counter = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
284 $counter[$hour] += $words;
285 $NickWordsCounter->put ($nick, @counter);
287 @counter = $NickCharsCounter->get ($nick);
290 @counter = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
292 $counter[$hour] += $chars;
293 $NickCharsCounter->put ($nick, @counter);
295 if ((length ($text) >= $QuoteMin)
296 and (length ($text) <= $QuoteMax))
298 if (!defined ($QuoteCache->{$nick}))
300 $QuoteCache->{$nick} = [];
302 push (@{$QuoteCache->{$nick}}, [$time, $text]);
305 if (defined ($QuoteCache->{$nick}))
307 while (scalar (@{$QuoteCache->{$nick}}) > $QuoteCacheSize)
309 shift (@{$QuoteCache->{$nick}});
318 for (get_all_nicks ())
321 my $main = get_main_nick ($nick);
323 if (!defined ($NickData->{$main}))
327 lines => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
328 words => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
329 chars => [qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0)],
336 my @counter = $NickLinesCounter->get ($nick);
340 for (my $i = 0; $i < 24; $i++)
342 $NickData->{$main}{'lines'}[$i] += $counter[$i];
343 $sum += $counter[$i];
345 $NickData->{$main}{'lines_total'} = $sum;
348 @counter = $NickWordsCounter->get ($nick);
352 for (my $i = 0; $i < 24; $i++)
354 $NickData->{$main}{'words'}[$i] += $counter[$i];
355 $sum += $counter[$i];
357 $NickData->{$main}{'words_total'} = $sum;
360 @counter = $NickWordsCounter->get ($nick);
364 for (my $i = 0; $i < 24; $i++)
366 $NickData->{$main}{'words'}[$i] += $counter[$i];
367 $sum += $counter[$i];
369 $NickData->{$main}{'chars_total'} = $sum;
372 if (!defined ($QuoteData->{$main}))
374 $QuoteData->{$main} = [];
376 if (defined ($QuoteCache->{$nick}))
378 my @new = sort (sub { $b->[0] <=> $a->[0] }, @{$QuoteCache->{$nick}}, @{$QuoteData->{$main}});
379 splice (@new, $QuoteCacheSize) if (scalar (@new) > $QuoteCacheSize);
380 $QuoteData->{$main} = \@new;
394 my $max = 0; # the most lines that were written in one hour..
395 my $total = 0; # the total amount of lines we wrote..
396 my $factor = 0; # used to find a bar's height
398 my @data = qw(0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0);
400 my @img_urls = get_config ('vertical_images');
403 @img_urls = qw#images/ver0n.png images/ver1n.png images/ver2n.png images/ver3n.png#;
406 my $fh = get_filehandle () or die;
408 # this for loop looks for the most amount of lines in one hour and sets
410 for (keys %$NickData)
414 for (my $i = 0; $i < 24; $i++)
416 $data[$i] += $NickData->{$nick}{'chars'}[$i];
420 for (my $i = 0; $i < 24; $i++)
422 $max = $data[$i] if ($max < $data[$i]);
432 $factor = (($BAR_HEIGHT - 1) / $max);
434 my $header = translate ('When do we actually talk here?');
435 print $fh "<h2>$header</h2>\n",
436 qq#<table class="hours_of_day">\n#,
439 # this for circles through the four colors. Each color represents six hours.
440 # (4 * 6 hours = 24 hours)
441 for (my $i = 0; $i <= 3; $i++)
443 for (my $j = 0; $j <= 5; $j++)
445 my $hour = (($i * 6) + $j);
446 if (!defined ($data[$hour]))
451 my $percent = 100 * ($data[$hour] / $total);
452 my $height = int ($data[$hour] * $factor) + 1;
453 my $img_url = $img_urls[$i];
455 print $fh ' <td>', sprintf ("%2.1f", $percent),
456 qq#%<br /><img src="$img_url" style="height: $height#,
457 qq#px;" alt="" /></td>\n#;
461 print $fh " </tr>\n",
462 qq# <tr class="hour_row">\n#;
463 print $fh map { " <td>$_</td>\n" } (0 .. 23);
464 print $fh " </tr>\n",
472 my @nicks = keys (%$NickData);
474 return unless (@nicks);
482 my $fh = get_filehandle () or die;
484 my $sort_field = lc ($SORT_BY);
489 ($tmp) = sort { $NickData->{$b}{'lines_total'} <=> $NickData->{$a}{'lines_total'} } (@nicks);
490 $max_lines = $NickData->{$tmp}{'lines_total'} || 0;
492 ($tmp) = sort { $NickData->{$b}{'words_total'} <=> $NickData->{$a}{'words_total'} } (@nicks);
493 $max_words = $NickData->{$tmp}{'words_total'} || 0;
495 ($tmp) = sort { $NickData->{$b}{'chars_total'} <=> $NickData->{$a}{'chars_total'} } (@nicks);
496 $max_chars = $NickData->{$tmp}{'chars_total'} || 0;
498 $trans = translate ('Most active nicks');
500 print $fh "<h2>$trans</h2>\n";
501 if ($SORT_BY eq 'LINES')
503 $trans = translate ('Nicks sorted by numbers of lines written');
505 elsif ($SORT_BY eq 'WORDS')
507 $trans = translate ('Nicks sorted by numbers of words written');
509 else # ($SORT_BY eq 'CHARS')
511 $trans = translate ('Nicks sorted by numbers of characters written');
513 print $fh "<p>($trans)</p>\n";
517 <table class="big_ranking">
519 <td class="invis"> </td>
523 $trans = translate ('Image');
524 print $fh " <th>$trans</th>\n";
528 $trans = translate ('Nick');
529 print $fh " <th>$trans</th>\n";
531 if ($DISPLAY_LINES ne 'NONE')
533 $trans = translate ('Number of Lines');
534 print $fh " <th>$trans</th>\n";
536 if ($DISPLAY_WORDS ne 'NONE')
538 $trans = translate ('Number of Words');
539 print $fh " <th>$trans</th>\n";
541 if ($DISPLAY_CHARS ne 'NONE')
543 $trans = translate ('Number of Characters');
544 print $fh " <th>$trans</th>\n";
548 $trans = translate ('When?');
549 print $fh " <th>$trans</th>\n";
552 $trans = translate ('Random Quote');
553 print $fh " <th>$trans</th>\n",
558 $NickData->{$b}{"${sort_field}_total"} <=> $NickData->{$a}{"${sort_field}_total"}
562 my $ident = nick_to_ident ($nick);
563 my $name = ident_to_name ($ident);
567 # As long as we didn't hit the
568 # $LongLines-limit we continue
570 if ($linescount <= $LongLines)
572 my $quote = translate ('-- no quote available --');
574 if (defined ($QuoteData->{$nick}))
576 my $num = scalar (@{$QuoteData->{$nick}});
577 my $rand = int (rand ($num));
578 $quote = html_escape ($QuoteData->{$nick}[$rand]);
586 $link = get_link ($name);
587 $image = get_image ($name);
588 $realname = get_realname ($name);
592 qq# <td class="numeration"># . $linescount . "</td>\n";
596 if ($DEFAULT_IMAGE and !$image)
598 $image = $DEFAULT_IMAGE;
601 print $fh qq# <td class="image">#;
606 print $fh qq#<a href="$link">#;
608 print $fh qq#<img src="$image" alt="$name" />#;
621 my $title = $realname;
624 $title = "User: $name; " if ($name);
625 $title .= "Ident: $ident";
627 print $fh qq# <td class="nick" title="$title">#;
631 print $fh qq#<a href="$link">$name</a></td>\n#
635 print $fh qq#$name</td>\n#;
638 if ($DISPLAY_LINES ne 'NONE')
640 print $fh qq# <td class="bar">#;
641 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'BAR'))
643 my $code = bar ($max_lines, $NickData->{$nick}{'lines'});
646 print $fh ' ' if ($DISPLAY_LINES eq 'BOTH');
647 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'NUMBER'))
649 print $fh $NickData->{$nick}{'lines_total'};
654 if ($DISPLAY_WORDS ne 'NONE')
656 print $fh qq# <td class="bar">#;
657 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'BAR'))
659 my $code = bar ($max_words, $NickData->{$nick}{'words'});
662 print $fh ' ' if ($DISPLAY_WORDS eq 'BOTH');
663 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'NUMBER'))
665 print $fh $NickData->{$nick}{'words_total'};
670 if ($DISPLAY_CHARS ne 'NONE')
672 print $fh qq# <td class="bar">#;
673 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'BAR'))
675 my $code = bar ($max_chars, $NickData->{$nick}{'chars'});
678 print $fh ' ' if ($DISPLAY_CHARS eq 'BOTH');
679 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'NUMBER'))
681 print $fh $NickData->{$nick}{'chars_total'};
688 my $code = bar ($NickData->{$nick}{'chars_total'}, $NickData->{$nick}{'chars'});
689 print $fh qq# <td class="bar">$code</td>\n#;
692 print $fh qq# <td class="quote">$quote</td>\n#,
695 if ($linescount == $LongLines)
697 print $fh "</table>\n\n";
701 # Ok, we have too many people to
702 # list them all so we start a
703 # smaller table and just list the
704 # names.. (Six names per line..)
705 elsif ($linescount <= ($LongLines + 6 * $ShortLines))
707 my $row_in_this_table = int (($linescount - $LongLines - 1) / 6);
708 my $col_in_this_table = ($linescount - $LongLines - 1) % 6;
711 if ($SORT_BY eq 'LINES')
713 $total = $NickData->{$nick}{'lines_total'};
715 elsif ($SORT_BY eq 'WORDS')
717 $total = $NickData->{$nick}{'words_total'};
719 else # ($SORT_BY eq 'CHARS')
721 $total = $NickData->{$nick}{'chars_total'};
724 my $title = $name ? get_realname ($name) : '';
727 $title = "User: $name; " if ($name);
728 $title .= "Ident: $ident";
731 if ($row_in_this_table == 0 and $col_in_this_table == 0)
733 $trans = translate ("They didn't write so much");
734 print $fh "<h2>$trans</h2>\n",
735 qq#<table class="small_ranking">\n#,
739 if ($col_in_this_table == 0 and $row_in_this_table != 0)
741 print $fh " </tr>\n",
745 print $fh qq# <td title="$title">$name ($total)</td>\n#;
747 if ($row_in_this_table == $ShortLines and $col_in_this_table == 5)
749 print $fh " </tr>\n",
754 # There is no else. There are
755 # just too many people around.
756 # I might add a "There are xyz
757 # unmentioned nicks"-line..
760 if (($linescount > $LongLines)
761 and ($linescount <= ($LongLines + 6 * $ShortLines)))
763 my $col = ($linescount - $LongLines - 1) % 6;
767 print $fh qq# <td> </td>\n#;
771 print $fh " </tr>\n";
774 if ($linescount != $LongLines)
776 print $fh "</table>\n\n";
780 # this is called by "&ranking ();" and prints the horizontal usage-bar in the
781 # detailed nick-table
788 confess unless (ref ($source eq 'ARRAY'));
790 # BAR_WIDTH is a least 10
791 my $max_width = $BAR_WIDTH - 4;
798 if (!$max_num) { return ($retval); }
799 $factor = $max_width / $max_num;
801 for ($i = 0; $i < 4; $i++)
805 my $img = $H_IMAGES[$i];
807 for ($j = 0; $j < 6; $j++)
809 my $hour = ($i * 6) + $j;
810 $sum += $source->[$hour];
813 $width += int (0.5 + ($sum * $factor));
815 $retval .= qq#<img src="$img" style="width: # . $width . q#px"#;
816 if ($i == 0) { $retval .= qq# class="first"#; }
817 elsif ($i == 3) { $retval .= qq# class="last"#; }
818 $retval .= ' alt="" />';
826 Florian octo Forster, E<lt>octo at verplant.orgE<gt>