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 ($i, $j); # used in for-loops
397 my $factor = 0; # used to find a bar's height
398 my $newline = ''; # buffer variable..
400 my @data = @{$DATA->{'byhour'}};
402 my @img_urls = get_config ('vertical_images');
405 @img_urls = qw#images/ver0n.png images/ver1n.png images/ver2n.png images/ver3n.png#;
408 my $fh = get_filehandle () or die;
410 # this for loop looks for the most amount of lines in one hour and sets
412 for ($i = 0; $i < 24; $i++)
414 if (!defined ($data[$i]))
421 if ($data[$i] > $max)
433 $factor = (($BAR_HEIGHT - 1) / $max);
435 my $header = translate ('When do we actually talk here?');
436 print $fh "<h2>$header</h2>\n",
437 qq#<table class="hours_of_day">\n#,
440 # this for circles through the four colors. Each color represents six hours.
441 # (4 * 6 hours = 24 hours)
442 for ($i = 0; $i <= 3; $i++)
444 for ($j = 0; $j <= 5; $j++)
446 my $hour = (($i * 6) + $j);
447 if (!defined ($data[$hour]))
452 my $percent = 100 * ($data[$hour] / $total);
453 my $height = int ($data[$hour] * $factor) + 1;
454 my $img_url = $img_urls[$i];
456 print $fh ' <td>', sprintf ("%2.1f", $percent),
457 qq#%<br /><img src="$img_url" style="height: $height#,
458 qq#px;" alt="" /></td>\n#;
462 print $fh " </tr>\n",
463 qq# <tr class="hour_row">\n#;
464 print $fh map { " <td>$_</td>\n" } (0 .. 23);
465 print $fh " </tr>\n",
473 my @nicks = keys (%$NickData);
475 return unless (@nicks);
483 my $fh = get_filehandle () or die;
485 my $sort_field = lc ($SORT_BY);
490 ($tmp) = sort { $NickData->{$b}{'lines_total'} <=> $NickData->{$a}{'lines_total'} } (@nicks);
491 $max_lines = $NickData->{$tmp}{'lines_total'} || 0;
493 ($tmp) = sort { $NickData->{$b}{'words_total'} <=> $NickData->{$a}{'words_total'} } (@nicks);
494 $max_words = $NickData->{$tmp}{'words_total'} || 0;
496 ($tmp) = sort { $NickData->{$b}{'chars_total'} <=> $NickData->{$a}{'chars_total'} } (@nicks);
497 $max_chars = $NickData->{$tmp}{'chars_total'} || 0;
499 $trans = translate ('Most active nicks');
501 print $fh "<h2>$trans</h2>\n";
502 if ($SORT_BY eq 'LINES')
504 $trans = translate ('Nicks sorted by numbers of lines written');
506 elsif ($SORT_BY eq 'WORDS')
508 $trans = translate ('Nicks sorted by numbers of words written');
510 else # ($SORT_BY eq 'CHARS')
512 $trans = translate ('Nicks sorted by numbers of characters written');
514 print $fh "<p>($trans)</p>\n";
518 <table class="big_ranking">
520 <td class="invis"> </td>
524 $trans = translate ('Image');
525 print $fh " <th>$trans</th>\n";
529 $trans = translate ('Nick');
530 print $fh " <th>$trans</th>\n";
532 if ($DISPLAY_LINES ne 'NONE')
534 $trans = translate ('Number of Lines');
535 print $fh " <th>$trans</th>\n";
537 if ($DISPLAY_WORDS ne 'NONE')
539 $trans = translate ('Number of Words');
540 print $fh " <th>$trans</th>\n";
542 if ($DISPLAY_CHARS ne 'NONE')
544 $trans = translate ('Number of Characters');
545 print $fh " <th>$trans</th>\n";
549 $trans = translate ('When?');
550 print $fh " <th>$trans</th>\n";
553 $trans = translate ('Random Quote');
554 print $fh " <th>$trans</th>\n",
559 $NickData->{$b}{"${sort_field}_total"} <=> $NickData->{$a}{"${sort_field}_total"}
563 my $ident = nick_to_ident ($nick);
564 my $name = ident_to_name ($ident);
568 # As long as we didn't hit the
569 # $LongLines-limit we continue
571 if ($linescount <= $LongLines)
573 my $quote = translate ('-- no quote available --');
575 if (defined ($QuoteData->{$nick}))
577 my $num = scalar (@{$QuoteData->{$nick}});
578 my $rand = int (rand ($num));
579 $quote = html_escape ($QuoteData->{$nick}[$rand]);
587 $link = get_link ($name);
588 $image = get_image ($name);
589 $realname = get_realname ($name);
593 qq# <td class="numeration"># . $linescount . "</td>\n";
597 if ($DEFAULT_IMAGE and !$image)
599 $image = $DEFAULT_IMAGE;
602 print $fh qq# <td class="image">#;
607 print $fh qq#<a href="$link">#;
609 print $fh qq#<img src="$image" alt="$name" />#;
622 my $title = $realname;
625 $title = "User: $name; " if ($name);
626 $title .= "Ident: $ident";
628 print $fh qq# <td class="nick" title="$title">#;
632 print $fh qq#<a href="$link">$name</a></td>\n#
636 print $fh qq#$name</td>\n#;
639 if ($DISPLAY_LINES ne 'NONE')
641 print $fh qq# <td class="bar">#;
642 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'BAR'))
644 my $code = bar ($max_lines, $NickData->{$nick}{'lines'});
647 print $fh ' ' if ($DISPLAY_LINES eq 'BOTH');
648 if (($DISPLAY_LINES eq 'BOTH') or ($DISPLAY_LINES eq 'NUMBER'))
650 print $fh $NickData->{$nick}{'lines_total'};
655 if ($DISPLAY_WORDS ne 'NONE')
657 print $fh qq# <td class="bar">#;
658 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'BAR'))
660 my $code = bar ($max_words, $NickData->{$nick}{'words'});
663 print $fh ' ' if ($DISPLAY_WORDS eq 'BOTH');
664 if (($DISPLAY_WORDS eq 'BOTH') or ($DISPLAY_WORDS eq 'NUMBER'))
666 print $fh $NickData->{$nick}{'words_total'};
671 if ($DISPLAY_CHARS ne 'NONE')
673 print $fh qq# <td class="bar">#;
674 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'BAR'))
676 my $code = bar ($max_chars, $NickData->{$nick}{'chars'});
679 print $fh ' ' if ($DISPLAY_CHARS eq 'BOTH');
680 if (($DISPLAY_CHARS eq 'BOTH') or ($DISPLAY_CHARS eq 'NUMBER'))
682 print $fh $NickData->{$nick}{'chars_total'};
689 my $code = bar ($NickData->{$nick}{'chars_total'}, $NickData->{$nick}{'chars'});
690 print $fh qq# <td class="bar">$code</td>\n#;
693 print $fh qq# <td class="quote">$quote</td>\n#,
696 if ($linescount == $LongLines)
698 print $fh "</table>\n\n";
702 # Ok, we have too many people to
703 # list them all so we start a
704 # smaller table and just list the
705 # names.. (Six names per line..)
706 elsif ($linescount <= ($LongLines + 6 * $ShortLines))
708 my $row_in_this_table = int (($linescount - $LongLines - 1) / 6);
709 my $col_in_this_table = ($linescount - $LongLines - 1) % 6;
712 if ($SORT_BY eq 'LINES')
714 $total = $DATA->{'byname'}{$name}{'lines'};
716 elsif ($SORT_BY eq 'WORDS')
718 $total = $DATA->{'byname'}{$name}{'words'};
720 else # ($SORT_BY eq 'CHARS')
722 $total = $DATA->{'byname'}{$name}{'chars'};
725 if ($row_in_this_table == 0 and $col_in_this_table == 0)
727 $trans = translate ("They didn't write so much");
728 print $fh "<h2>$trans</h2>\n",
729 qq#<table class="small_ranking">\n#,
733 if ($col_in_this_table == 0 and $row_in_this_table != 0)
735 print $fh " </tr>\n",
739 print $fh " <td>$name ($total)</td>\n";
741 if ($row_in_this_table == $ShortLines and $col_in_this_table == 5)
743 print $fh " </tr>\n",
748 # There is no else. There are
749 # just too many people around.
750 # I might add a "There are xyz
751 # unmentioned nicks"-line..
754 if (($linescount > $LongLines)
755 and ($linescount <= ($LongLines + 6 * $ShortLines)))
757 my $col = ($linescount - $LongLines - 1) % 6;
761 print $fh qq# <td> </td>\n#;
765 print $fh " </tr>\n";
768 if ($linescount != $LongLines)
770 print $fh "</table>\n\n";
774 # this is called by "&ranking ();" and prints the horizontal usage-bar in the
775 # detailed nick-table
782 confess unless (ref ($source eq 'ARRAY'));
784 # BAR_WIDTH is a least 10
785 my $max_width = $BAR_WIDTH - 4;
792 if (!$max_num) { return ($retval); }
793 $factor = $max_width / $max_num;
795 for ($i = 0; $i < 4; $i++)
799 my $img = $H_IMAGES[$i];
801 for ($j = 0; $j < 6; $j++)
803 my $hour = ($i * 6) + $j;
804 $sum += $source->[$hour];
807 $width += int (0.5 + ($sum * $factor));
809 $retval .= qq#<img src="$img" style="width: # . $width . q#px"#;
810 if ($i == 0) { $retval .= qq# class="first"#; }
811 elsif ($i == 3) { $retval .= qq# class="last"#; }
812 $retval .= ' alt="" />';
820 Florian octo Forster, E<lt>octo at verplant.orgE<gt>