File Coverage

blib/lib/Bot/BasicBot/Pluggable/Module/Karma.pm
Criterion Covered Total %
statement 104 107 97.2
branch 46 54 85.1
condition 11 18 61.1
subroutine 14 14 100.0
pod 5 9 55.5
total 180 202 89.1


line stmt bran cond sub pod time code
1             package Bot::BasicBot::Pluggable::Module::Karma;
2             $Bot::BasicBot::Pluggable::Module::Karma::VERSION = '1.11';
3 3     3   9 use base qw(Bot::BasicBot::Pluggable::Module);
  3         6  
  3         181  
4 3     3   10 use warnings;
  3         34  
  3         66  
5 3     3   9 use strict;
  3         3  
  3         3013  
6              
7             sub init {
8 3     3 1 3 my $self = shift;
9 3         25 $self->config(
10             {
11             user_ignore_selfkarma => 1,
12             user_num_comments => 3,
13             user_show_givers => 1,
14             user_randomize_reasons => 1,
15             user_karma_change_response => 1,
16             }
17             );
18             }
19              
20             sub help {
21             return
22 1     1 1 4 "Gives karma for or against a particular thing. Usage: <thing>++ # comment, <thing>-- # comment, karma <thing>, explain <thing>.";
23             }
24              
25             sub told {
26 136     136 1 119 my ( $self, $mess ) = @_;
27 136         109 my $body = $mess->{body};
28 136 50       202 return 0 unless defined $body;
29              
30             # If someone is trying to change the bot's karma, we'll have our bot nick in
31             # {addressed}, and '++' or '-' in the body ('-' rather than '--' because
32             # Bot::BasicBot removes one of the dashes as it considers it part of the
33             # address)
34 136 0 0     222 if ( $mess->{address} && ($body eq '++' or $body eq '-') ) {
      33        
35 0 0       0 $body = '--' if $body eq '-';
36 0         0 $body = $mess->{address} . $body;
37             }
38              
39 136         249 my $op_re = qr{ ( \-\- | \+\+ ) }x;
40 136         171 my $comment_re = qr{ (?: \s* \# \s* (.+) )? }x;
41 136         639 for my $regex (
42             qr{^ (\S+) $op_re $comment_re }x, # singleword++
43             qr{^ \( (.+) \) $op_re $comment_re }x # (more words)++
44             ) {
45 258 100       883 if (my($thing, $op, $comment) = $body =~ $regex) {
46 16 100       29 my $add = $op eq '++' ? 1 : 0;
47 16 100 100     51 if (
48             ( $1 eq $mess->{who} ) and $self->get("user_ignore_selfkarma")
49             ){
50 2         8 return;
51             }
52 14         25 my $reply = $self->add_karma( $thing, $add, $comment, $mess->{who} );
53 14 100       30 if (lc $thing eq lc $self->bot->nick) {
54 2 100       14 $reply .= ' ' . ($add ? '(thanks!)' : '(pffft)');
55             }
56 14         103 return $reply;
57             }
58             }
59              
60             # OK, handle "karma" / "explain" commands
61 120         366 my ( $command, $param ) = split( /\s+/, $body, 2 );
62 120         125 $command = lc($command);
63              
64 120 100 100     516 if ( $command eq "karma" ) {
    100          
65 8         90 $param =~ s/\?+$//; # handle interrogatives - lop off trailing question marks
66 8 50 66     27 if ($param && $param eq 'chameleon') {
67 0         0 return "Karma karma karma karma karma chameleon, "
68             . "you come and go, you come and go...";
69             }
70 8   66     15 $param ||= $mess->{who};
71 8         18 return "$param has karma of " . $self->get_karma($param) . ".";
72            
73             } elsif ( $command eq "explain" and $param ) {
74 107         101 $param =~ s/^karma\s+//i;
75 107         147 my ( $karma, $good, $bad ) = $self->get_karma($param);
76 107         164 my $reply = "positive: " . $self->format_reasons($good) . "; ";
77 107         151 $reply .= "negative: " . $self->format_reasons($bad) . "; ";
78 107         163 $reply .= "overall: $karma.";
79              
80 107         579 return $reply;
81             }
82             }
83              
84              
85             sub format_reasons {
86 214     214 0 166 my ( $self, $reason ) = @_;
87 214         340 my $num_comments = $self->get('user_num_comments');
88              
89 214 100       295 if ( $num_comments == 0 ) {
90 4         5 return scalar( $reason->() );
91             }
92              
93 210         216 my @reasons = $reason->();
94 210         178 my $num_reasons = @reasons;
95              
96 210 100       279 if ( $num_reasons == 0 ) {
97 1         3 return 'nothing';
98             }
99              
100 209 100       276 if ( $num_reasons == 1 ) {
101 106         125 return ( $self->maybe_add_giver(@reasons) )[0];
102             }
103              
104 103         151 $self->trim_list( \@reasons, $num_comments );
105 103         136 return join( ', ', $self->maybe_add_giver(@reasons) );
106             }
107              
108             sub maybe_add_giver {
109 209     209 0 191 my ( $self, @reasons ) = @_;
110 209 100       297 if ( $self->get('user_show_givers') ) {
111              
112             # adding a (user) string to the all reasons
113 202         162 return map { $_->{reason} . ' (' . $_->{who} . ')' } @reasons;
  303         877  
114             }
115             else {
116              
117             # just returning the reason string of the reason hash referenes
118 7         8 return map { $_->{reason} } @reasons;
  9         28  
119             }
120             }
121              
122             sub get_karma {
123 133     133 1 118 my ( $self, $thing ) = @_;
124 133         112 $thing = lc($thing);
125 133         132 $thing =~ s/-/ /g;
126              
127 133 100       83 my @changes = grep { ref } @{ $self->get("karma_$thing") || [] };
  903         761  
  133         335  
128              
129 133         111 my ( @good, @bad );
130 133         111 my $karma = 0;
131 133         81 my $positive = 0;
132 133         84 my $negative = 0;
133              
134 133         129 for my $row (@changes) {
135              
136             # just push non empty reasons on the array
137 903         686 my $reason = $row->{reason};
138 903 100       886 if ( $row->{positive} ) { $positive++; push( @good, +{ %$row } ) if $reason }
  462 100       281  
  462         848  
139 441 100       267 else { $negative++; push( @bad, +{ %$row } ) if $reason }
  441         984  
140             }
141 133         114 $karma = $positive - $negative;
142              
143             # The subroutine references return differant values when called.
144             # If they are called in scalar context, they return the overall
145             # positive or negative karma, but when called in list context you
146             # get an array of hash references with all non empty reasons back.
147              
148             return wantarray()
149             ? (
150             $karma,
151 107 100   107   178 sub { return wantarray ? @good : $positive },
152 107 100   107   170 sub { return wantarray ? @bad : $negative }
153             )
154 133 100       555 : $karma;
155             }
156              
157             sub add_karma {
158 14     14 1 17 my ( $self, $thing, $good, $reason, $who ) = @_;
159 14         13 $thing = lc($thing);
160 14         17 $thing =~ s/-/ /g;
161 14         34 my $row =
162             { reason => $reason, who => $who, timestamp => time, positive => $good };
163 14 100       14 my @changes = map { +{ %$_ } } grep { ref } @{ $self->get("karma_$thing") || [] };
  31         92  
  31         33  
  14         33  
164 14         19 push @changes, $row;
165 14         34 $self->set( "karma_$thing" => \@changes );
166 14         28 my $respond = $self->get('user_karma_change_response');
167 14 50       24 $respond = 1 if !defined $respond;
168 14 50       37 return $respond ?
169             "Karma for $thing is now " . scalar $self->get_karma($thing) : 1;
170             }
171              
172             sub trim_list {
173 103     103 0 82 my ( $self, $list, $count ) = @_;
174              
175             # If randomization isn't requested we just return the reasons
176             # in reversed chronological order
177              
178 103 100       148 if ( $self->get('user_randomize_reasons') ) {
179 100         123 fisher_yates_shuffle($list);
180             }
181             else {
182 3         11 @$list = reverse sort { $b->{timestamp} cmp $a->{timestamp} } @$list;
  7         16  
183             }
184              
185 103 100       159 if ( scalar(@$list) > $count ) {
186 102         158 @$list = splice( @$list, 0, $count );
187             }
188             }
189              
190             sub fisher_yates_shuffle {
191 100     100 0 73 my $array = shift;
192 100         67 my $i = @$array;
193 100         436 while ( $i-- ) {
194 300         298 my $j = int rand( $i + 1 );
195 300         516 @$array[ $i, $j ] = @$array[ $j, $i ];
196             }
197             }
198              
199             1;
200              
201             __END__
202              
203             =head1 NAME
204              
205             Bot::BasicBot::Pluggable::Module::Karma - tracks karma for various concepts
206              
207             =head1 VERSION
208              
209             version 1.11
210              
211             =head1 IRC USAGE
212              
213             =over 4
214              
215             =item <thing>++ # <comment>
216              
217             Increases the karma for <thing>.
218              
219             Responds with the new karma for <thing> unless C<karma_change_response> is set
220             to a false value.
221              
222             =item <thing>-- # <comment>
223              
224             Decreases the karma for <thing>.
225              
226             Responds with the new karma for <thing> unless C<karma_change_response> is set
227             to a false value.
228              
229             =item karma <thing>
230              
231             Replies with the karma rating for <thing>.
232              
233             =item explain <thing>
234              
235             Lists three each good and bad things said about <thing>:
236              
237             <user> explain Morbus
238             <bot> positive: committing lots of bot documentation; fixing the
239             fisher_yates; negative: filling the dev list. overall: 5
240              
241             =back
242              
243             =head1 METHODS
244              
245             =over 4
246              
247             =item get_karma($username)
248              
249             Returns either a string representing the total number of karma
250             points for the passed C<$username> or the total number of karma
251             points and subroutine reference for good and bad karma comments.
252             These references return the according karma levels when called in
253             scalar context or a array of hash reference. Every hash reference
254             has entries for the timestamp (timestamp), the giver (who) and the
255             explanation string (reason) for its karma action.
256              
257             =item add_karma($thing, $good, $reason, $who)
258              
259             Adds or subtracts from the passed C<$thing>'s karma. C<$good> is either 1 (to
260             add a karma point to the C<$thing> or 0 (to subtract). C<$reason> is an
261             optional string commenting on the reason for the change, and C<$who> is the
262             person modifying the karma of C<$thing>. Nothing is returned.
263              
264             =back
265              
266             =head1 VARS
267              
268             =over 4
269              
270             =item ignore_selfkarma
271              
272             Defaults to 1; determines whether to respect selfkarmaing or not.
273              
274             =item num_comments
275              
276             Defaults to 3; number of good and bad comments to display on
277             explanations. Set this variable to 0 if you do not want to list
278             reasons at all.
279              
280             =item show_givers
281              
282             Defaults to 1; whether to show who gave good or bad comments on
283             explanations.
284              
285             =item randomize_reasons
286              
287             Defaults to 1; whether to randomize the order of reasons. If set
288             to 0, the reasons are sorted in reversed chronological order.
289              
290             =item karma_change_response
291              
292             Defaults to 1; whether to show a response when the karma of a
293             thing is changed. If true, the bot will reply with the new karma.
294             If set to 0, the bot will silently update the karma, without
295             a response.
296              
297             =back
298              
299             =head1 AUTHOR
300              
301             Mario Domgoergen <mdom@cpan.org>
302              
303             This program is free software; you can redistribute it
304             and/or modify it under the same terms as Perl itself.