Why not adopt me?
NAME
POE::Component::IRC::Plugin::CPAN::Info - PoCo::IRC plugin for accessing information about CPAN modules, distributions and authors.
SYNOPSIS
use strict;
use warnings;
use POE qw(Component::IRC Component::IRC::Plugin::CPAN::Info);
my @Channels = ( '#zofbot' );
my $irc = POE::Component::IRC->spawn(
nick => 'CPANInfoBot',
server => 'irc.freenode.net',
port => 6667,
ircname => 'CPAN module information bot',
) or die "Oh noes :( $!";
POE::Session->create(
package_states => [
main => [ qw( _start irc_001 ) ],
],
);
$poe_kernel->run();
sub _start {
$irc->yield( register => 'all' );
# register our plugin
$irc->plugin_add(
'CPANInfo' => POE::Component::IRC::Plugin::CPAN::Info->new
);
$irc->yield( connect => { } );
undef;
}
sub irc_001 {
my ( $kernel, $sender ) = @_[ KERNEL, SENDER ];
$kernel->post( $sender => join => $_ )
for @Channels;
undef;
}
DESCRIPTION
The module is a POE::Component::IRC plugin which uses POE::Component::IRC::Plugin for easy addon of the module.
The module provides interface for quering information about CPAN authors, (e.g. full name and email address), modules (e.g. version and description) and distributions (e.g. list of modules the distribution contains and author of the distribution).
*GASP* FEATURITIS?
Before I begin to present you my wonderful creation I feel like I should calm you down a bit. Yes, you, I can see you looking over at the "pageage" of this doc. For a second it may seem that POE::Component::IRC::Plugin::CPAN::Info suffers from featuritis with all those options in the contructor. But in fact, it doesn't. Plugin does (relatively) a lot, and all I did is make it possible for you to configure what it does. All settings have sensible defaults, and the very basics you need to start using the plugin is
$irc->plugin_add(
'CPANInfo' => POE::Component::IRC::Plugin::CPAN::Info->new
);
It's that easy.
DEFAULT COMMANDS
With default settings the plugin will respond to the following commands which are either sent by addressing the "bot", /notice'ing the "bot" or /msg'ing the "bot".
auth_email # Author's e-mail address
auth_name # Author's full name
mod_distname # Which distribution the module is in
mod_version # Module's version
mod_desc # Module's description
mod_chapter # Module's chapter
mod_dslip # Module's DSLIP code.
dist_version # Distribution's version
dist_file # Distribution's CPAN filename
dist_auth # Distribution's author
dist_desc # Distribution's description
dist_mods # List modules included in the distribution
dist_chapter # Chapter and subchapter of the distribution
help # lists available help system commands
# which can be triggered by using 'help $command'
help auth_email
help auth_name # and so on with all of the above commands.
Example:
<Zoffix> CPANInfoBot, dist_auth WWW-Search-Mininova
<CPANInfoBot> Zoffix, ZOFFIX
Before I continue, if you are planing to reconfigure those commands (which I call triggers) I want you to glance at the command list above, at their names in particular. Every command is in the form foo_bar
where foo
is the "category" of the command and bar
is the command name, of course nothing is stopping you from making the trigger to be foobar
but the reason I am telling you this is because the commands above also indicate the "category" (the part before _
) and the command (the part after _
) in the trigers
hashref. Thus to change trigger for mod_desc
command to mod_description
you would specify
->new( triggers => { mod => { desc => qr/^description\s+/i } } );
But I am getting ahead of myself.. more on this later.
CONTRUCTOR
# "Vanilla" plugin
$irc->plugin_add(
'CPANInfo' => POE::Component::IRC::Plugin::CPAN::Info->new
);
# the "Juicy Options" flavor
my $cpan_info_plugin = POE::Component::IRC::Plugin::CPAN::Info->new(
mirror => 'http://cpan.perl.org/',
path => 'cpan_sqlite_info',
freshen_interval => 86400,
send_events => 1,
got_info_event => 'cpaninfo_got_info',
no_result_event => 'cpaninfo_no_result',
response_event => 'cpaninfo_response',
respond_no_result => 1,
no_result_responses => [ 'No clue', 'No idea' ],
show_help => 1,
listen_for_help => [ qw(public notice privmsg) ],
listen_for_input => [ qw(public notice privmsg) ],
max_modules_limit => 10,
max_modules_length => 300,
max_output_length => 600,
output_line_length => 300,
debug => 1,
triggers => {
dist_cat => qr/(?=.)/, # no dist_cat regex
dist => {
auth => qr/^author\s+/i,
},
mod_cat => qr/^mod(?:|ule)?_?/i,
auth_cat => qr/^auth(?:or)?_?/i,
auth => {
email => qr/^(?:e-|e)?mail\s+/i,
},
},
help => {
help_re => qr/^cpan_help\s+/i,
dist_cat => 'd',
dist => {
auth => 'author',
}
mod_cat => 'm',
auth_cat => '',
},
ua_args => {
timeout => 50,
agent => 'CpanInfoPoco',
},
);
$irc->plugin_add( 'CPANInfo' => $cpan_info_plugin );
The contructor returns an object suitable to be fed to POE::Component::IRC's plugin_add()
method. It may take a lot of arguments, luckly all of them are optional with sensible defaults. The possible options are as follows:
mirror
POE::Component::CPAN::SQLite::Info->spawn( mirror => 'http://cpan.org' );
The component needs three files from CPAN mirrors, and it fetches those automatically, namely those are
$CPAN/modules/03modlist.data.gz
$CPAN/modules/02packages.details.txt.gz
$CPAN/authors/01mailrc.txt.gz.
The mirror
argument specifies what CPAN mirror to get those files from. Defaults to: http://cpan.perl.org
path
->new( path => '/tmp' );
When component fetches the needed files it will mirror them locally. By specifying the path
argument you can tell the component where to store those. The component will create two directories inside the one you've specified, namely 'authors' and 'modules'. This argument defaults to 'cpan_sqlite_info'
directory inside the current directory.
freshen_interval
->new( freshen_interval => 86400 );
The freshen_interval
specifies (in seconds) how often should the component retrieve a fresh copy of CPAN files (described in mirror
option above). If an error occured during fetching of the files, the component will retry in freshen_interval
or 30 seconds, whichever is sooner. Defaults to: 86400
(one day)
send_events
->new( send_events => 1 );
Specifies whether or not the component should emit any events which are described below. When set to a true value the plugin will emit the events, otherwise won't. Techinally it is possible to disable any native plugin output (see listen_for_input
argument below) and respond only by listening to the events it sends. Defaults to: 1
got_info_event
->new( got_info_event => 'cpaninfo_got_info' );
Upon successful retrieval of the files and successful proccessing of those the component will emit the event specified by got_info_event
argument. The handler will recieve the output of Perl's time()
function as the only argument of ARG0
which will indicate the time at which the event was sent. Generally, on slow boxes the processing of the files can take some time (it's all non-blocking, don't worry) thus if you are just starting the component, it won't have data readily available until you recieve the first got_info_event
. Defaults to: cpaninfo_got_info
no_result_event
->new( no_result_event => 'cpaninfo_no_result' );
When plugin will see a matching command, but doesn't have any data available for the request (e.g. calling mod_desc
on non-existent module) the plugin will send the event specified by no_result_event
argument. Defaults to: cpaninfo_no_result
response_event
->new( response_event => 'cpaninfo_response' );
When plugin will have a response ready (e.g. response to the request about some module's author), the event specified by response_event
will be sent out. Defaults to: cpaninfo_response
respond_no_result
->new( respond_no_result => 0 );
If a trigger for a particular command matched (see triggers
below) but there wasn't any information available for the request the component may respond with a predefined "no clue" response (see no_result_responses
below) or be quiet. When respond_no_result
is set to a false value, the component will not respond when the requested information is missing, otherwise it will randomly choose one of the no_result_responses
(see below) and reply with that. Note: this doesn't affect the cases when triggers (see triggers
below) don't match, it only affects the cases when a particular command matched but data is not available such as asking for a version of a non-existant module. Defaults to: 1
no_result_responses
->new( no_result_responses => [ 'No clue', 'No idea', 'Waddayawant?' ] );
If the trigger for a command matched (see triggers
below) but the data is not available (e.g. asking for a version of a non-existant module) and respond_no_result
option (see above) is set to a true value. The component will respond with one of the randomly chosen responses. Those responses are defined by the no_result_responses
argument which takes an arrayref of possible responses. Defaults to: [ 'No clue', 'No idea' ]
show_help
->new( show_help => 1 )
The plugin has a built in "help system" to refresh the memory about available commands (no, you don't actually have to keep this doc open all the time :) ). The details are explained in HELP MESSAGES section. The show_help
key to the contructor enables or disables the help system. When show_help
argument is set to a true value, plugin will respond to help inquiries, otherwise the help system will be off. Defaults to: 1
listen_for_help
->new( listen_for_help => [ qw(public notice privmsg) ] );
Plugin listens for three types of messages: public messages that appear in the channel (although it makes sure that those messages prefixed with your bot's nick), /notice messages and /msg (privmsg
) messages. The details are explained in HELP MESSAGES section. The listen_for_help
argument takes an arrayref which tell it which of those three types of messages to return the help for (if asked). The message types are as follows:
- public
-
Public messages from channels with bot's nick prepended:
<Zoffix> CPANInfoBot, auth_name Zoffix <CPANInfoBot> Zoffix, Zoffix Znet
- notice
-
Messages sent via /notice
- privmsg
-
Messages set via private messages ( /msg )
In other words, if you want your users to use help only via /notice'ing and /msg'ing you'd specify:
->new( listen_for_help => [ qw(notice privmsg) ] );
listen_for_input
->new( listen_for_input => [ qw(public notice privmsg) ] );
Same as listen_for_help
(see right above). Except this one controls global "listening". In other words if you did something along the lines of:
->new(
listen_for_input => [ qw(public) ],
listen_for_help => [ qw(notice privmsg) ],
);
Your users would be able to use plugin's commands in the channel but would NOT be able to use help at all, because the plugin would ignore qw(notice privmsg)
messages sent to it because listen_for_input
doesn't contain those elements.
On the contrary:
->new(
listen_for_input => [ qw(public notice privmsg) ],
listen_for_help => [ qw(notice privmsg) ],
);
Would allow the users to use the bot in the channel, via /notice and /msg but the help would be available only via /notice and /msg.
max_modules_limit
->new( max_modules_limit => 5 );
The dist_mods
command lists all the modules included in the distribution. As you can probably imagine, some dists contain enough modules to spam the channel any day with this command. The two arguments, max_modules_limit
and max_modules_length
(see below) can help you deal with that. The max_modules_limit
takes a scalar as an argument and acts in the following way: if the distribution contains more than max_modules_limit
, do NOT list them, but instead respond with Uses $that_many modules...
. If the distribution contains less than max_modules_limit
modules in it, respond with list of their names. Yes, you may set max_modules_limit
to 0
and have the component always respond with the quantity. Alternatively, you may set it to a large value and set max_modules_length
(see below) to chop the long lists Defaults to: 5
max_modules_length
->new( max_modules_length => 300 );
Along with max_modules_limit
(see above) you can specify the maximum length of the dist_mods
output. If the output exceeds max_modules_length
characters in length it will be chopped off and the total number of modules in the distribution will be prepended. Defaults to: 300
max_output_length
->new( max_output_length => 600 );
This argument controls the maximum length of the output, but see also max_output_length_pub
argument below. If any ouput is longer than max_output_length
characters it will be chopped off with ...
appended. Note: if this argument is set to a lower value than max_modules_length
(see above), then output from dist_mods
will be chopped up to max_output_length
(kind of an "override"). Defaults to: 600
max_output_length_pub
->new( max_output_length_pub => 400 );
This argument is the same as max_output_length
(see right above) with the exception that it applies only to public messages (i.e. the output to public channels). Thus, you might want to set lower output length for channel output as there are more people and flooding the channel is not nice, but allow longer messages to /notice and /msg requests with max_output_length
argument. Defaults to: 400
output_line_length
->new( output_line_length => 300 );
The output_line_length
argument controls the number of characters per line of the output, in other words, if you the plugin about to output 500 character message, but output_line_length
is set to 300
the plugin will break the output up into two messages and send one 300 character message followed by a 200 character message. This argument ensures your bot will not be dropped from the network for "Excess Flood"
. Defaults to: 300
banned
->new( banned => [ qr/\Q*!*\@spammer.com/, qr/^Spammer/i ] );
Takes an arrayref of regex references. Any user who's mask matches any of the regexes specified in banned
argument will be ignored by the plugin.
debug
->new( debug => 1 );
When debug
argument is set to a true value plugin will print out a bit of debugging information. Defaults to: 0
triggers
->new (
triggers => {
dist_cat => qr/(?=.)/, # no dist_cat regex
dist => {
auth => qr/^author\s+/i,
},
mod_cat => qr/^mod(?:|ule)?_?/i,
auth_cat => qr/^auth(?:or)?_?/i,
auth => {
email => qr/^(?:e-|e)?mail\s+/i,
},
help_re => qr/^cpan_help\s+/i,
}
);
Takes an hashref as an argument. See TRIGGERS section below for information.
help
->new(
help => {
dist_cat => 'd',
dist => {
auth => 'author',
}
mod_cat => 'm',
auth_cat => '',
},
);
Takes a hasref as an argument. See HELP MESSAGES section below for information.
ua_args
->new(
ua_args => {
timeout => 50,
agent => 'CpanInfoPoco',
},
);
Takes a hashref of arguments, those will be passed to LWP::UserAgent's contrustor. Defaults to: whatever LWP::UserAgent's constructor defaults are, except timeout
which defaults to 30
.
TRIGGERS
The output of the plugin is triggered. Even though all those triggers have defaults you can change each and everyone you like (or don't like in this case :) ).
Triggers are broken down into three categories: dist
, mod
and auth
which are for distribution related commands, module related commands and author related commands respectively. When the category trigger is matched, it is removed from the input and attempt to match command trigger is made. This means that if you want your trigger for dist_mods
command to be dist_modules
You'd set the command trigger to be qr/^modules/;
because by default the category trigger is qr/^dist_/i
, which will be removed. The idea of category triggers is to save some typing if you are setting up a lot of triggers yourself. In case you want each command trigger to differ that much you should set category trigger to qr/(?=.)/
which will match all the time (proving we have input). Why not an empty qr//
? Read perlop
When changing triggers, keep in mind that there is a built in help system (see HELP MESSAGES section below) which you will need to change as well because it will tell the users about default triggers not the ones you've set up.
The following hashref is what the contructor's triggers
argument takes, it represents default triggers set up on the plugin. If you want to change only one trigger just specify it as ->new( triggers =
{ mod => { desc => qr/^description\s+/i } } ); no need to repeat every triggier, the rest will be left at the defaults.
{
mod_cat => qr/ ^ mod_ /xi,
mod => {
distname => qr/ ^ distname \s+ /xi,
version => qr/ ^ version \s+ /xi,
desc => qr/ ^ desc \s+ /xi,
chapter => qr/ ^ chapter \s+ /xi,
dslip => qr/ ^ dslip \s+ /xi,
},
auth_cat => qr/ ^auth_ /xi,
auth => {
email => qr/ ^ email \s+ /xi,
name => qr/ ^ name \s+ /xi,
},
dist_cat => qr/ ^ dist_ /xi,
dist => {
version => qr/ ^ version \s+ /xi,
file => qr/ ^ file \s+ /xi,
auth => qr/ ^ auth \s+ /xi,
desc => qr/ ^ desc \s+ /xi,
mods => qr/ ^ mods \s+ /xi,
chapter => qr/ ^ chapter \s+ /xi,
},
};
HELP MESSAGES
The component has a built in help system (which is can be distabled). The hashref presented below is what the contructor's help
argument takes, it represents default triggers set up on the plugin. If you want to change only one trigger just specify it as ->new( triggers =
{ mod => { desc => 'description' } } ); no need to repeat every triggier, the rest will be left at the defaults. Note: as opposed to triggers
hashref, the help
hashref contains a bunch of strings, NOT regex references.
The only key that takes a qr//
is a help_re
, this key determines the help system trigger, as with other triggers (see TRIGGERS section above) the trigger will be removed before matching agains help system commands. The commands are matched in the following fashion: if it starts with a category prefix, remove it and see if it contains the command now. In other words, with the default settings, message containing help mod_distname
would return help for plugin's mod_distname
command because qr/^help\s*/i
would remove the help
from the beginning, remove category prefix (mod_cat
) which is mod_
and match distname
value set for the mod => { distname }
key. The match is performed case insensitively.
If when help_re
trigger matched and removed the input is empty plugin will list all available commands, with will be in the form $mod_cat
$command
As with triggers, mod_cat
, dist_cat
and auth_cat
values represent the category prefix to save you typing while defining category commands. If you wish, you may set category prefixes to an empty string and define commands with full command values. In other words, both of these will give help for mod_distname
command:
->new( help => {
mod_cat => '',
mod => { distname => 'mod_distname' },
},
);
# these two are the same, but different effect on other help commands
->new( help => {
mod_cat => 'mod_',
mod => { distname => 'distname' },
},
);
Here is a hashref with the possible contructor's help
argument's keys and their default values.
{
help_re => qr/^help\s*/i,
mod_cat => 'mod_',
mod => {
distname => 'distname',
version => 'version',
desc => 'desc',
chapter => 'chapter',
dslip => 'dslip',
},
auth_cat => 'auth_',
auth => {
email => 'email',
name => 'name',
},
dist_cat => 'dist_',
dist => {
version => 'version',
file => 'file',
auth => 'auth',
desc => 'desc',
mods => 'mods',
chapter => 'chapter',
},
};
EMITED EVENTS
The plugin emits three different events (if enabled, and by default it is). The names of the events may be configured with: got_info_event
no_result_event
and response_event
arguments to the contructor.
output from got_info_event
The got_info_event
event will be sent out each time the plugin successfully parses CPAN data files. On a slow box this process may take a while (though it's non-blocking), therefore you won't be able to inquire the plugin about any data until you recieve at least one got_info_event
event. The event handler will recieve the output of Perl's time()
function in it's ARG0
argument which will be the time at which the event was sent.
output from no_result_event
$VAR1 = {
'what' => 'CPAN2_, mod_version Fake',
'who' => 'Zoffix!n=Zoffix@unaffiliated/zoffix',
'time' => 1202211629,
'type' => 'public',
'channel' => '#zofbot'
};
The the handler for the event specified by no_result_event
will recieve events whenever the a particular command matches but there is no data available. For example, when request for mod_version
is made asking for the version of a non-existant module.
output from response_event
$VAR1 = {
'what' => 'CPAN2_, mod_version Carp',
'who' => 'Zoffix!n=Zoffix@unaffiliated/zoffix',
'response' => '1.08',
'time' => 1202210405,
'type' => 'public',
'channel' => '#zofbot'
};
The handler set up for the event specified by respose_event
will recieve event whenever a command request was made which produced useful output.
AUTHOR
Zoffix Znet, <zoffix at cpan.org>
BUGS
Please report any bugs or feature requests to bug-poe-component-irc-plugin-cpan-info at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=POE-Component-IRC-Plugin-CPAN-Info. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc POE::Component::IRC::Plugin::CPAN::Info
You can also look for information at:
RT: CPAN's request tracker
http://rt.cpan.org/NoAuth/Bugs.html?Dist=POE-Component-IRC-Plugin-CPAN-Info
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
http://cpanratings.perl.org/d/POE-Component-IRC-Plugin-CPAN-Info
Search CPAN
http://search.cpan.org/dist/POE-Component-IRC-Plugin-CPAN-Info
ACKNOWLEDGEMENTS
COPYRIGHT & LICENSE
Copyright 2008 Zoffix Znet, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.