File Coverage

blib/lib/POE/Component/IRC/Plugin/BotCommand.pm
Criterion Covered Total %
statement 183 207 88.4
branch 80 134 59.7
condition 15 54 27.7
subroutine 20 20 100.0
pod 5 10 50.0
total 303 425 71.2


line stmt bran cond sub pod time code
1             package POE::Component::IRC::Plugin::BotCommand;
2             our $AUTHORITY = 'cpan:HINRIK';
3             # vim: set expandtab ts=4 sw=4 ai:
4             $POE::Component::IRC::Plugin::BotCommand::VERSION = '6.93';
5 9     9   8300 use strict;
  9         30  
  9         333  
6 9     9   62 use warnings FATAL => 'all';
  9         23  
  9         556  
7 9     9   64 use Carp;
  9         21  
  9         719  
8 9     9   67 use IRC::Utils qw( parse_user strip_color strip_formatting );
  9         26  
  9         1033  
9 9     9   283 use POE::Component::IRC::Plugin qw( :ALL );
  9         36  
  9         13563  
10              
11             sub new {
12 8     8 1 6348 my ($package) = shift;
13 8 50       48 croak "$package requires an even number of arguments" if @_ & 1;
14 8         39 my %args = @_;
15              
16 8 50       49 $args{Method} = 'notice' if !defined $args{Method};
17              
18 8         23 for my $cmd (keys %{ $args{Commands} }) {
  8         41  
19 11 100       38 if (ref $args{Commands}->{$cmd} eq 'HASH') {
20             croak "$cmd: no info provided"
21 1 50       5 if !exists $args{Commands}->{$cmd}->{info} ;
22             $args{Commands}->{lc $cmd}->{handler} =
23             sprintf("irc_botcmd_%s", lc($cmd))
24 1 50       14 if !$args{Commands}->{lc $cmd}->{handler};
25             }
26 11         76 $args{Commands}->{lc $cmd} = delete $args{Commands}->{$cmd};
27             }
28 8         38 return bless \%args, $package;
29             }
30              
31             sub PCI_register {
32 8     8 0 1175 my ($self, $irc) = splice @_, 0, 2;
33              
34 8 100       68 $self->{Addressed} = 1 if !defined $self->{Addressed};
35 8 100       75 $self->{Prefix} = '!' if !defined $self->{Prefix};
36 8 50       41 $self->{In_channels} = 1 if !defined $self->{In_channels};
37 8 50       73 $self->{In_private} = 1 if !defined $self->{In_private};
38 8         64 $self->{rx_cmd_args} = qr/^(\S+)(?:\s+(.+))?$/;
39 8         28 $self->{irc} = $irc;
40              
41 8         80 $irc->plugin_register( $self, 'SERVER', qw(msg public) );
42 8         389 return 1;
43             }
44              
45             sub PCI_unregister {
46 8     8 0 2120 return 1;
47             }
48              
49             sub S_msg {
50 2     2 0 177 my ($self, $irc) = splice @_, 0, 2;
51 2         5 my $who = ${ $_[0] };
  2         6  
52 2         9 my $where = parse_user($who);
53 2         43 my $what = ${ $_[2] };
  2         4  
54              
55 2 50       9 return PCI_EAT_NONE if !$self->{In_private};
56 2         8 $what = $self->_normalize($what);
57              
58 2 50       7 if (!$self->{Bare_private}) {
59 0 0       0 return PCI_EAT_NONE if $what !~ s/^\Q$self->{Prefix}\E//;
60             }
61              
62 2         4 my ($cmd, $args);
63 2 50       20 if (!(($cmd, $args) = $what =~ $self->{rx_cmd_args})) {
64 0         0 return PCI_EAT_NONE;
65             }
66              
67 2         11 $self->_handle_cmd($who, $where, $cmd, $args);
68 2 50       10 return $self->{Eat} ? PCI_EAT_PLUGIN : PCI_EAT_NONE;
69             }
70              
71             sub S_public {
72 16     16 0 970 my ($self, $irc) = splice @_, 0, 2;
73 16         38 my $who = ${ $_[0] };
  16         58  
74 16         39 my $where = ${ $_[1] }->[0];
  16         61  
75 16         33 my $what = ${ $_[2] };
  16         39  
76 16         78 my $me = $irc->nick_name();
77              
78 16 50       73 return PCI_EAT_NONE if !$self->{In_channels};
79 16         74 $what = $self->_normalize($what);
80              
81 16 100       74 if ($self->{Addressed}) {
82 10 50       360 return PCI_EAT_NONE if !(($what) = $what =~ m/^\s*\Q$me\E[:,;.!?~]?\s*(.*)$/);
83             }
84             else {
85 6 50       95 return PCI_EAT_NONE if $what !~ s/^\Q$self->{Prefix}\E//;
86             }
87              
88 16         72 my ($cmd, $args);
89 16 50       188 if (!(($cmd, $args) = $what =~ $self->{rx_cmd_args})) {
90 0         0 return PCI_EAT_NONE;
91             }
92              
93 16         134 $self->_handle_cmd($who, $where, $cmd, $args);
94 16 100       92 return $self->{Eat} ? PCI_EAT_PLUGIN : PCI_EAT_NONE;
95             }
96              
97             sub _normalize {
98 18     18   62 my ($self, $line) = @_;
99 18         100 $line = strip_color($line);
100 18         615 $line = strip_formatting($line);
101 18         364 return $line;
102             }
103              
104             sub _handle_cmd {
105 18     18   69 my ($self, $who, $where, $cmd, $args) = @_;
106 18         49 my $irc = $self->{irc};
107 18 50       45 my $chantypes = join('', @{ $irc->isupport('CHANTYPES') || ['#', '&']});
  18         138  
108 18 100       241 my $public = $where =~ /^[$chantypes]/ ? 1 : 0;
109 18         99 $cmd = lc $cmd;
110              
111 18         62 my $cmd_unresolved = $cmd;
112              
113 18 50       222 if((my $cmd_resolved = $self->resolve_alias($cmd)))
114             {
115 0         0 $cmd = $cmd_resolved;
116             }
117              
118              
119 18 100       100 if (defined $self->{Commands}->{$cmd}) {
120 11 100       64 if (ref $self->{Commands}->{$cmd} eq 'HASH') {
121 1 50       14 my @args_array = defined $args ? split /\s+/, $args : ();
122 1 50 33     13 if (defined($self->{Commands}->{$cmd}->{args}) &&
      33        
      33        
      33        
123             ref($self->{Commands}->{$cmd}->{args}) eq 'ARRAY' &&
124 1         9 @{ $self->{Commands}->{$cmd}->{args} } &&
125             (@args_array < @{ $self->{Commands}->{$cmd}->{args} } ||
126             (!defined $self->{Commands}->{$cmd}->{variable} &&
127             @args_array > @{ $self->{Commands}->{$cmd}->{args} }))
128             ) {
129 0         0 $irc->yield($self->{Method}, $where,
130             "Not enough or too many arguments. See help for $cmd");
131 0         0 return;
132             }
133              
134 1 50 0     6 if(defined $self->{Commands}->{$cmd}->{variable} ||
      0        
      33        
135             (defined($self->{Commands}->{$cmd}->{args}) &&
136             ref($self->{Commands}->{$cmd}->{args}) eq 'ARRAY' &&
137 0         0 @{ $self->{Commands}->{$cmd}->{args} }))
138             {
139 1         4 $args = {};
140 1 50 33     10 if( defined($self->{Commands}->{$cmd}->{args}) &&
      33        
141             ref($self->{Commands}->{$cmd}->{args}) eq 'ARRAY' &&
142 1         5 @{ $self->{Commands}->{$cmd}->{args} })
143             {
144 1         3 for (@{ $self->{Commands}->{$cmd}->{args} }) {
  1         5  
145 2         5 my $in_arg = shift @args_array;
146 2 100       9 if (ref $self->{Commands}->{$cmd}->{$_} eq 'ARRAY') {
147 1         3 my @values = @{ $self->{Commands}->{$cmd}->{$_} };
  1         26  
148 1         5 shift @values;
149              
150 9     9   83 use List::Util qw(none);
  9         20  
  9         16841  
151             # Check if argument has one of possible values
152 1 50   1   15 if (none { $_ eq $in_arg} @values) {
  1         6  
153 0         0 $irc->yield($self->{Method}, $where,
154             "$_ can be one of ".join '|', @values);
155 0         0 return;
156             }
157              
158             }
159 2         10 $args->{$_} = $in_arg;
160             }
161             }
162              
163             # Process remaining arguments if variable is set
164 1         3 my $arg_cnt = 0;
165 1 50       5 if (defined $self->{Commands}->{$cmd}->{variable}) {
166 1         4 for (@args_array) {
167 1         6 $args->{"opt".$arg_cnt++} = $_;
168             }
169             }
170             }
171             }
172             }
173              
174 18 100       111 if (ref $self->{Auth_sub} eq 'CODE') {
175 3         12 my ($authed, $errors) = $self->{Auth_sub}->($self->{irc}, $who, $where, $cmd, $args, $cmd_unresolved);
176              
177 3 100       20 if (!$authed) {
178 1 50       5 my @errors = ref $errors eq 'ARRAY'
179             ? @$errors
180             : 'You are not authorized to use this command.';
181 1 50       5 if (!$self->{Ignore_unauthorized}) {
182 1         4 for my $error (@errors) {
183 1         6 $irc->yield($self->{Method}, $where, $error);
184             }
185             }
186 1         109 return;
187             }
188             }
189              
190 17 100       87 if (defined $self->{Commands}->{$cmd}) {
    50          
    0          
191 11 100       59 my $handler = (ref($self->{Commands}->{$cmd}) eq 'HASH' ? $self->{Commands}->{$cmd}->{handler} : "irc_botcmd_$cmd");
192 11         104 $irc->send_event_next($handler => $who, $where, $args, $cmd, $cmd_unresolved);
193             }
194             elsif ($cmd =~ /^help$/i) {
195 6         25 my @help = $self->_get_help($args, $public);
196 6         36 $irc->yield($self->{Method} => $where => $_) for @help;
197             }
198             elsif (!$self->{Ignore_unknown}) {
199 0         0 my @help = $self->_get_help($cmd, $public);
200 0         0 $irc->yield($self->{Method} => $where => $_) for @help;
201             }
202              
203 17         2047 return;
204             }
205              
206             sub _get_help {
207 6     6   19 my ($self, $args, $public) = @_;
208 6         15 my $irc = $self->{irc};
209             my $p = $self->{Addressed} && $public
210             ? $irc->nick_name().': '
211 6 50 33     46 : $self->{Prefix};
212              
213 6         16 my @help;
214 6 100       21 if (defined $args) {
215 3         17 my $cmd = (split /\s+/, $args, 2)[0];
216              
217 3         8 $cmd = lc $cmd;
218              
219 3   33     11 my $cmd_resolved = $self->resolve_alias($cmd) || $cmd;
220              
221 3 100       12 if (exists $self->{Commands}->{$cmd_resolved}) {
222 1 50       9 if (ref $self->{Commands}->{$cmd_resolved} eq 'HASH') {
223             push @help, "Syntax: $p$cmd".
224             ( defined($self->{Commands}->{$cmd_resolved}->{args}) &&
225             ref($self->{Commands}->{$cmd_resolved}->{args}) eq 'ARRAY' ?
226 1         8 " ".join ' ', @{ $self->{Commands}->{$cmd_resolved}->{args} } :
227             "" ).
228             (defined $self->{Commands}->{$cmd_resolved}->{variable} ?
229 1 50 33     15 " ..." : "");
    50          
230             push @help, split /\015?\012/,
231 1         7 "Description: ".$self->{Commands}->{$cmd_resolved}->{info};
232 1 50 33     25 if( defined($self->{Commands}->{$cmd_resolved}->{args}) &&
      33        
233             ref($self->{Commands}->{$cmd_resolved}->{args}) eq 'ARRAY' &&
234 1         9 @{ $self->{Commands}->{$cmd_resolved}->{args} })
235             {
236 1         5 push @help, "Arguments:";
237              
238 1         2 for my $arg (@{ $self->{Commands}->{$cmd_resolved}->{args} }) {
  1         5  
239 2 50       14 next if not defined $self->{Commands}->{$cmd_resolved}->{$arg};
240 2 100       12 if (ref $self->{Commands}->{$cmd_resolved}->{$arg} eq 'ARRAY') {
241 1         3 my @arg_usage = @{$self->{Commands}->{$cmd_resolved}->{$arg}};
  1         4  
242 1         9 push @help, " $arg: ".$arg_usage[0].
243             " (".(join '|', @arg_usage[1..$#arg_usage]).")"
244             }
245             else {
246             push @help, " $arg: ".
247 1         5 $self->{Commands}->{$cmd_resolved}->{$arg};
248             }
249             }
250             }
251              
252             push @help, "Alias of: ${p}${cmd_resolved}" .
253             (ref($self->{Commands}->{$cmd_resolved}->{args}) eq 'ARRAY' ?
254 0         0 " ".join ' ', @{ $self->{Commands}->{$cmd_resolved}->{args} } :
255             "" ).
256             (defined $self->{Commands}->{$cmd_resolved}->{variable} ?
257 1 0       5 " ..." : "")
    0          
    50          
258             if $cmd_resolved ne $cmd;
259              
260 1         10 my @aliases = grep { $_ ne $cmd } $self->list_aliases($cmd_resolved);
  0         0  
261              
262 1 50       4 if($cmd_resolved ne $cmd)
263             {
264 0         0 push @aliases, $cmd_resolved;
265             }
266              
267 1 50       4 push @help, "Aliases: ".join( " ", @aliases) if scalar(@aliases);
268             }
269             else {
270 0         0 @help = split /\015?\012/, $self->{Commands}->{$cmd};
271             }
272             }
273             else {
274 2         10 push @help, "Unknown command: $cmd";
275 2         8 push @help, "To get a list of commands, use: ${p}help";
276             }
277             }
278             else {
279 3 100       8 if (keys %{ $self->{Commands} }) {
  3         34  
280 1         4 push @help, 'Commands: ' . join ', ', sort keys %{ $self->{Commands} };
  1         7  
281 1         4 push @help, "For more details, use: ${p}help ";
282             }
283             else {
284 2         15 push @help, 'No commands are defined';
285             }
286             }
287              
288 6 50       25 if(ref($self->{'Help_sub'}) eq 'CODE')
289             {
290 0 0       0 my ($cmd, $args) = (defined $args ? split /\s+/, $args, 2 : ('', ''));
291              
292 0   0     0 my $cmd_resolved = $self->resolve_alias($cmd) || $cmd;
293              
294 0         0 return $self->{'Help_sub'}->($self->{irc}, $cmd, $cmd_resolved, $args, @help);
295             }
296             else
297             {
298 6         26 return @help;
299             }
300             }
301              
302             sub add {
303 8     8 1 3408 my ($self, $cmd, $usage) = @_;
304 8         30 $cmd = lc $cmd;
305 8 50       49 return if exists $self->{Commands}->{$cmd};
306              
307 8 100       39 if (ref $usage eq 'HASH') {
308 1 50 33     8 return if !exists $usage->{info} || !@{ $usage->{args} };
  1         6  
309             }
310              
311 8         28 $self->{Commands}->{$cmd} = $usage;
312 8         50 return 1;
313             }
314              
315             sub remove {
316 5     5 1 24 my ($self, $cmd) = @_;
317 5         16 $cmd = lc $cmd;
318 5 50       36 return if !exists $self->{Commands}->{$cmd};
319 5         16 delete $self->{Commands}->{$cmd};
320 5         24 return 1;
321             }
322              
323             sub list {
324 26     26 1 73 my ($self) = @_;
325 26         53 return %{ $self->{Commands} };
  26         165  
326             }
327              
328             sub resolve_alias {
329 21     21 1 69 my ($self, $alias) = @_;
330            
331 21         68 my %cmds = $self->list();
332              
333             #TODO: refactor using smartmatch/Perl6::Junction if feasible
334 21         116 while(my ($cmd, $info) = each(%cmds))
335             {
336 32 100       235 next unless ref($info) eq 'HASH';
337 6 50 33     35 next unless $info->{aliases} && ref($info->{aliases}) eq 'ARRAY';
338 0         0 my @aliases = @{$info->{aliases}};
  0         0  
339            
340 0         0 foreach my $cmdalias (@aliases)
341             {
342 0 0       0 return $cmd if $alias eq $cmdalias;
343             }
344             }
345              
346 21         113 return undef;
347             }
348              
349             sub list_aliases
350             {
351 1     1 0 3 my ($self, $cmd) = @_;
352 1         11 $cmd = lc $cmd;
353 1 50       7 return if !exists $self->{Commands}->{$cmd};
354 1 50       4 return unless ref($self->{Commands}->{$cmd}) eq 'HASH';
355 1 50 33     8 return unless exists $self->{Commands}->{$cmd}->{aliases} && ref($self->{Commands}->{$cmd}->{aliases}) eq 'ARRAY';
356 0           return @{$self->{Commands}->{$cmd}->{aliases}};
  0            
357              
358             }
359              
360             1;
361              
362             =encoding utf8
363              
364             =head1 NAME
365              
366             POE::Component::IRC::Plugin::BotCommand - A PoCo-IRC plugin which handles
367             commands issued to your bot
368              
369             =head1 SYNOPSIS
370              
371             use POE;
372             use POE::Component::Client::DNS;
373             use POE::Component::IRC;
374             use POE::Component::IRC::Plugin::BotCommand;
375              
376             my @channels = ('#channel1', '#channel2');
377             my $dns = POE::Component::Client::DNS->spawn();
378             my $irc = POE::Component::IRC->spawn(
379             nick => 'YourBot',
380             server => 'some.irc.server',
381             );
382              
383             POE::Session->create(
384             package_states => [
385             main => [ qw(_start irc_001 irc_botcmd_slap irc_botcmd_lookup dns_response) ],
386             ],
387             );
388              
389             $poe_kernel->run();
390              
391             sub _start {
392             $irc->plugin_add('BotCommand', POE::Component::IRC::Plugin::BotCommand->new(
393             Commands => {
394             slap => 'Takes one argument: a nickname to slap.',
395             lookup => 'Takes two arguments: a record type (optional), and a host.',
396             }
397             ));
398             $irc->yield(register => qw(001 botcmd_slap botcmd_lookup));
399             $irc->yield(connect => { });
400             }
401              
402             # join some channels
403             sub irc_001 {
404             $irc->yield(join => $_) for @channels;
405             return;
406             }
407              
408             # the good old slap
409             sub irc_botcmd_slap {
410             my $nick = (split /!/, $_[ARG0])[0];
411             my ($where, $arg) = @_[ARG1, ARG2];
412             $irc->yield(ctcp => $where, "ACTION slaps $arg");
413             return;
414             }
415              
416             # non-blocking dns lookup
417             sub irc_botcmd_lookup {
418             my $nick = (split /!/, $_[ARG0])[0];
419             my ($where, $arg) = @_[ARG1, ARG2];
420             my ($type, $host) = $arg =~ /^(?:(\w+) )?(\S+)/;
421              
422             my $res = $dns->resolve(
423             event => 'dns_response',
424             host => $host,
425             type => $type,
426             context => {
427             where => $where,
428             nick => $nick,
429             },
430             );
431             $poe_kernel->yield(dns_response => $res) if $res;
432             return;
433             }
434              
435             sub dns_response {
436             my $res = $_[ARG0];
437             my @answers = map { $_->rdatastr } $res->{response}->answer() if $res->{response};
438              
439             $irc->yield(
440             'notice',
441             $res->{context}->{where},
442             $res->{context}->{nick} . (@answers
443             ? ": @answers"
444             : ': no answers for "' . $res->{host} . '"')
445             );
446              
447             return;
448             }
449              
450             =head1 DESCRIPTION
451              
452             POE::Component::IRC::Plugin::BotCommand is a
453             L plugin. It provides you with a
454             standard interface to define bot commands and lets you know when they are
455             issued. Commands are accepted as channel or private messages.
456              
457             The plugin will respond to the 'help' command by default, listing available
458             commands and information on how to use them. However, if you add a help
459             command yourself, that one will be used instead.
460              
461             =head1 METHODS
462              
463             =head2 C
464              
465             B<'Commands'>, a hash reference, with your commands as keys, and usage
466             information as values. If the usage string contains newlines, the plugin
467             will send one message for each line.
468              
469             If a command's value is a HASH ref like this:
470              
471             $irc->plugin_add('BotCommand', POE::Component::IRC::Plugin::BotCommand->new(
472             Commands => {
473             slap => {
474             info => 'Slap someone',
475             args => [qw(nickname)],
476             nickname => 'nickname to slap'
477             }
478             }
479             ));
480              
481             The args array reference is than used to validate number of arguments required
482             and to name arguments passed to event handler. Help is than generated from
483             C and other hash keys which represent arguments (they are optional).
484              
485             An optional C key can be specified inside the HASH ref to override the event handler.
486             The irc_botcmd_ prefix is not automatically prepended to the handler name when overriding it.
487              
488             An optional C key can be specified inside the HASH ref containing a array ref with alias names.
489             The aliases can be specified for help and to run the command.
490              
491             =head3 Accepting commands
492              
493             B<'In_channels'>, a boolean value indicating whether to accept commands in
494             channels. Default is true.
495              
496             B<'In_private'>, a boolean value indicating whether to accept commands in
497             private. Default is true.
498              
499             B<'Addressed'>, requires users to address the bot by name in order
500             to issue commands. Default is true.
501              
502             B<'Prefix'>, a string which all commands must be prefixed with (except in
503             channels when B<'Addressed'> is true). Default is '!'. You can set it to ''
504             to allow bare commands.
505              
506             B<'Bare_private'>, a boolean value indicating whether bare commands (without
507             the prefix) are allowed in private messages. Default is false.
508              
509             =head3 Authorization
510              
511             B<'Auth_sub'>, a subroutine reference which, if provided, will be called
512             for every command. The subroutine will be called in list context. If the
513             first value returned is true, the command will be processed as normal. If
514             the value is false, then no events will be generated, and an error message
515             will possibly be sent back to the user.
516              
517             You can override the default error message by returning a second value, an
518             array reference of (zero or more) strings. Each string will be sent as a
519             message to the user.
520              
521             Your subroutine will be called with the following arguments:
522              
523             =over 4
524              
525             =item 1. The IRC component object
526              
527             =item 2. The nick!user@host of the user
528              
529             =item 3. The place where the command was issued (the nickname of the user if
530             it was in private)
531              
532             =item 4. The name of the command
533              
534             =item 5. The command argument string
535              
536             =back
537              
538             B<'Ignore_unauthorized'>, if true, the plugin will ignore unauthorized
539             commands, rather than printing an error message upon receiving them. This is
540             only relevant if B<'Auth_sub'> is also supplied. Default is false.
541              
542             =head3 Help Command
543              
544             B<'Help_sub'>, a subroutine reference which, if provided, will be called upon
545             the end of the predefined help command. The subroutine will be called in list context.
546              
547             Your subroutine will be called with the following arguments:
548              
549             =over 4
550              
551             =item 1. The IRC component object
552              
553             =item 2. The command.
554              
555             =item 3. The resolved command(after alias processing).
556              
557             =item 4. The arguments.
558              
559             =item 5. The generated help text as array.
560              
561              
562             =back
563              
564              
565             =head3 Miscellaneous
566              
567             B<'Ignore_unknown'>, if true, the plugin will ignore undefined commands,
568             rather than printing a help message upon receiving them. Default is false.
569              
570             B<'Method'>, how you want help messages to be delivered. Valid options are
571             'notice' (the default) and 'privmsg'.
572              
573             B<'Eat'>, set to true to make the plugin hide
574             L|POE::Component::IRC/irc_public> events from other plugins
575             when they look like commands. Probably only useful when a B<'Prefix'> is
576             defined. Default is false.
577              
578             Returns a plugin object suitable for feeding to
579             L's C method.
580              
581             =head2 C
582              
583             Adds a new command. Takes two arguments, the name of the command, and a string
584             or hash reference containing its usage information (see C). Returns false
585             if the command has already been defined or no info or arguments are provided,
586             true otherwise.
587              
588             =head2 C
589              
590             Removes a command. Takes one argument, the name of the command. Returns false
591             if the command wasn't defined to begin with, true otherwise.
592              
593             =head2 C
594              
595             Takes no arguments. Returns a list of key/value pairs, the keys being the
596             command names and the values being the usage strings or hash references.
597              
598             =head2 C
599              
600             Takes one argument, a string to match against command aliases, if no matching
601             command can be found undef is returned.
602              
603             =head1 OUTPUT EVENTS
604              
605             =head2 C
606              
607             You will receive an event like this for every valid command issued. E.g. if
608             'slap' were a valid command, you would receive an C event
609             every time someone issued that command. It receives the following arguments:
610              
611             =over 4
612              
613             =item * C: the nick!hostmask of the user who issued the command.
614              
615             =item * C is the name of the channel in which the command was issued,
616             or the sender's nickname if this was a private message.
617              
618             =item * C: a string of arguments to the command, or hash reference with
619             arguments in case you defined command along with arguments, or undef if there
620             were no arguments
621              
622             =back
623              
624             =head1 AUTHOR
625              
626             Hinrik Ern SigurEsson, hinrik.sig@gmail.com
627              
628             =cut