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.10';
3 3     3   11 use base qw(Bot::BasicBot::Pluggable::Module);
  3         3  
  3         186  
4 3     3   13 use warnings;
  3         44  
  3         66  
5 3     3   9 use strict;
  3         3  
  3         2963  
6              
7             sub init {
8 3     3 1 3 my $self = shift;
9 3         24 $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 3 "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 114 my ( $self, $mess ) = @_;
27 136         109 my $body = $mess->{body};
28 136 50       205 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     187 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         228 my $op_re = qr{ ( \-\- | \+\+ ) }x;
40 136         185 my $comment_re = qr{ (?: \s* \# \s* (.+) )? }x;
41 136         650 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       865 if (my($thing, $op, $comment) = $body =~ $regex) {
46 16 100       31 my $add = $op eq '++' ? 1 : 0;
47 16 100 100     53 if (
48             ( $1 eq $mess->{who} ) and $self->get("user_ignore_selfkarma")
49             ){
50 2         9 return;
51             }
52 14         25 my $reply = $self->add_karma( $thing, $add, $comment, $mess->{who} );
53 14 100       29 if (lc $thing eq lc $self->bot->nick) {
54 2 100       14 $reply .= ' ' . ($add ? '(thanks!)' : '(pffft)');
55             }
56 14         123 return $reply;
57             }
58             }
59              
60             # OK, handle "karma" / "explain" commands
61 120         370 my ( $command, $param ) = split( /\s+/, $body, 2 );
62 120         123 $command = lc($command);
63              
64 120 100 100     494 if ( $command eq "karma" ) {
    100          
65 8         87 $param =~ s/\?+$//; # handle interrogatives - lop off trailing question marks
66 8 50 66     28 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     17 $param ||= $mess->{who};
71 8         16 return "$param has karma of " . $self->get_karma($param) . ".";
72            
73             } elsif ( $command eq "explain" and $param ) {
74 107         102 $param =~ s/^karma\s+//i;
75 107         144 my ( $karma, $good, $bad ) = $self->get_karma($param);
76 107         183 my $reply = "positive: " . $self->format_reasons($good) . "; ";
77 107         137 $reply .= "negative: " . $self->format_reasons($bad) . "; ";
78 107         156 $reply .= "overall: $karma.";
79              
80 107         585 return $reply;
81             }
82             }
83              
84              
85             sub format_reasons {
86 214     214 0 156 my ( $self, $reason ) = @_;
87 214         323 my $num_comments = $self->get('user_num_comments');
88              
89 214 100       294 if ( $num_comments == 0 ) {
90 4         6 return scalar( $reason->() );
91             }
92              
93 210         209 my @reasons = $reason->();
94 210         165 my $num_reasons = @reasons;
95              
96 210 100       250 if ( $num_reasons == 0 ) {
97 1         4 return 'nothing';
98             }
99              
100 209 100       269 if ( $num_reasons == 1 ) {
101 106         132 return ( $self->maybe_add_giver(@reasons) )[0];
102             }
103              
104 103         133 $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 213 my ( $self, @reasons ) = @_;
110 209 100       317 if ( $self->get('user_show_givers') ) {
111              
112             # adding a (user) string to the all reasons
113 202         179 return map { $_->{reason} . ' (' . $_->{who} . ')' } @reasons;
  303         869  
114             }
115             else {
116              
117             # just returning the reason string of the reason hash referenes
118 7         9 return map { $_->{reason} } @reasons;
  9         34  
119             }
120             }
121              
122             sub get_karma {
123 133     133 1 111 my ( $self, $thing ) = @_;
124 133         108 $thing = lc($thing);
125 133         123 $thing =~ s/-/ /g;
126              
127 133 100       78 my @changes = grep { ref } @{ $self->get("karma_$thing") || [] };
  903         734  
  133         280  
128              
129 133         120 my ( @good, @bad );
130 133         91 my $karma = 0;
131 133         80 my $positive = 0;
132 133         90 my $negative = 0;
133              
134 133         129 for my $row (@changes) {
135              
136             # just push non empty reasons on the array
137 903         611 my $reason = $row->{reason};
138 903 100       844 if ( $row->{positive} ) { $positive++; push( @good, +{ %$row } ) if $reason }
  462 100       274  
  462         781  
139 441 100       271 else { $negative++; push( @bad, +{ %$row } ) if $reason }
  441         947  
140             }
141 133         117 $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   167 sub { return wantarray ? @good : $positive },
152 107 100   107   172 sub { return wantarray ? @bad : $negative }
153             )
154 133 100       561 : $karma;
155             }
156              
157             sub add_karma {
158 14     14 1 18 my ( $self, $thing, $good, $reason, $who ) = @_;
159 14         18 $thing = lc($thing);
160 14         23 $thing =~ s/-/ /g;
161 14         38 my $row =
162             { reason => $reason, who => $who, timestamp => time, positive => $good };
163 14 100       15 my @changes = map { +{ %$_ } } grep { ref } @{ $self->get("karma_$thing") || [] };
  31         104  
  31         39  
  14         36  
164 14         17 push @changes, $row;
165 14         33 $self->set( "karma_$thing" => \@changes );
166 14         33 my $respond = $self->get('user_karma_change_response');
167 14 50       26 $respond = 1 if !defined $respond;
168 14 50       39 return $respond ?
169             "Karma for $thing is now " . scalar $self->get_karma($thing) : 1;
170             }
171              
172             sub trim_list {
173 103     103 0 77 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       156 if ( $self->get('user_randomize_reasons') ) {
179 100         109 fisher_yates_shuffle($list);
180             }
181             else {
182 3         8 @$list = reverse sort { $b->{timestamp} cmp $a->{timestamp} } @$list;
  7         15  
183             }
184              
185 103 100       165 if ( scalar(@$list) > $count ) {
186 102         152 @$list = splice( @$list, 0, $count );
187             }
188             }
189              
190             sub fisher_yates_shuffle {
191 100     100 0 71 my $array = shift;
192 100         80 my $i = @$array;
193 100         138 while ( $i-- ) {
194 300         298 my $j = int rand( $i + 1 );
195 300         454 @$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.10
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.