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.20';
3 3     3   11 use base qw(Bot::BasicBot::Pluggable::Module);
  3         3  
  3         184  
4 3     3   13 use warnings;
  3         31  
  3         61  
5 3     3   7 use strict;
  3         8  
  3         3097  
6              
7             sub init {
8 3     3 1 4 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 112 my ( $self, $mess ) = @_;
27 136         128 my $body = $mess->{body};
28 136 50       199 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     191 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         235 my $op_re = qr{ ( \-\- | \+\+ ) }x;
40 136         156 my $comment_re = qr{ (?: \s* \# \s* (.+) )? }x;
41 136         614 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       886 if (my($thing, $op, $comment) = $body =~ $regex) {
46 16 100       29 my $add = $op eq '++' ? 1 : 0;
47 16 100 100     49 if (
48             ( $1 eq $mess->{who} ) and $self->get("user_ignore_selfkarma")
49             ){
50 2         8 return;
51             }
52 14         19 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       13 $reply .= ' ' . ($add ? '(thanks!)' : '(pffft)');
55             }
56 14         99 return $reply;
57             }
58             }
59              
60             # OK, handle "karma" / "explain" commands
61 120         360 my ( $command, $param ) = split( /\s+/, $body, 2 );
62 120         121 $command = lc($command);
63              
64 120 100 100     489 if ( $command eq "karma" ) {
    100          
65 8         88 $param =~ s/\?+$//; # handle interrogatives - lop off trailing question marks
66 8 50 66     29 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     16 $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         104 $param =~ s/^karma\s+//i;
75 107         137 my ( $karma, $good, $bad ) = $self->get_karma($param);
76 107         142 my $reply = "positive: " . $self->format_reasons($good) . "; ";
77 107         131 $reply .= "negative: " . $self->format_reasons($bad) . "; ";
78 107         146 $reply .= "overall: $karma.";
79              
80 107         563 return $reply;
81             }
82             }
83              
84              
85             sub format_reasons {
86 214     214 0 156 my ( $self, $reason ) = @_;
87 214         301 my $num_comments = $self->get('user_num_comments');
88              
89 214 100       277 if ( $num_comments == 0 ) {
90 4         3 return scalar( $reason->() );
91             }
92              
93 210         190 my @reasons = $reason->();
94 210         173 my $num_reasons = @reasons;
95              
96 210 100       263 if ( $num_reasons == 0 ) {
97 1         3 return 'nothing';
98             }
99              
100 209 100       229 if ( $num_reasons == 1 ) {
101 106         120 return ( $self->maybe_add_giver(@reasons) )[0];
102             }
103              
104 103         127 $self->trim_list( \@reasons, $num_comments );
105 103         127 return join( ', ', $self->maybe_add_giver(@reasons) );
106             }
107              
108             sub maybe_add_giver {
109 209     209 0 177 my ( $self, @reasons ) = @_;
110 209 100       290 if ( $self->get('user_show_givers') ) {
111              
112             # adding a (user) string to the all reasons
113 202         172 return map { $_->{reason} . ' (' . $_->{who} . ')' } @reasons;
  303         859  
114             }
115             else {
116              
117             # just returning the reason string of the reason hash referenes
118 7         7 return map { $_->{reason} } @reasons;
  9         28  
119             }
120             }
121              
122             sub get_karma {
123 133     133 1 485 my ( $self, $thing ) = @_;
124 133         113 $thing = lc($thing);
125 133         125 $thing =~ s/-/ /g;
126              
127 133 100       89 my @changes = grep { ref } @{ $self->get("karma_$thing") || [] };
  903         709  
  133         282  
128              
129 133         114 my ( @good, @bad );
130 133         91 my $karma = 0;
131 133         81 my $positive = 0;
132 133         88 my $negative = 0;
133              
134 133         136 for my $row (@changes) {
135              
136             # just push non empty reasons on the array
137 903         594 my $reason = $row->{reason};
138 903 100       1101 if ( $row->{positive} ) { $positive++; push( @good, +{ %$row } ) if $reason }
  462 100       270  
  462         808  
139 441 100       258 else { $negative++; push( @bad, +{ %$row } ) if $reason }
  441         899  
140             }
141 133         108 $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   165 sub { return wantarray ? @good : $positive },
152 107 100   107   152 sub { return wantarray ? @bad : $negative }
153             )
154 133 100       554 : $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         15 $thing =~ s/-/ /g;
161 14         32 my $row =
162             { reason => $reason, who => $who, timestamp => time, positive => $good };
163 14 100       11 my @changes = map { +{ %$_ } } grep { ref } @{ $self->get("karma_$thing") || [] };
  31         94  
  31         30  
  14         30  
164 14         21 push @changes, $row;
165 14         35 $self->set( "karma_$thing" => \@changes );
166 14         30 my $respond = $self->get('user_karma_change_response');
167 14 50       21 $respond = 1 if !defined $respond;
168 14 50       34 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       151 if ( $self->get('user_randomize_reasons') ) {
179 100         105 fisher_yates_shuffle($list);
180             }
181             else {
182 3         11 @$list = reverse sort { $b->{timestamp} cmp $a->{timestamp} } @$list;
  7         18  
183             }
184              
185 103 100       147 if ( scalar(@$list) > $count ) {
186 102         159 @$list = splice( @$list, 0, $count );
187             }
188             }
189              
190             sub fisher_yates_shuffle {
191 100     100 0 64 my $array = shift;
192 100         76 my $i = @$array;
193 100         129 while ( $i-- ) {
194 300         298 my $j = int rand( $i + 1 );
195 300         495 @$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.20
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.