package POE::Component::IRC::Plugin::CPAN::LinksToDocs::No404s::Remember; use warnings; use strict; our $VERSION = '0.001'; use base 'POE::Component::IRC::Plugin::BasePoCoWrap'; use POE::Component::CPAN::LinksToDocs::No404s::Remember; sub _make_default_args { return ( response_event => 'irc_cpan_links_to_docs', trigger => qr/^(?:ur[il]\s*(?:for)?|perldoc)\s+(?=\S+)/i, max_length => 300, obj_args => {}, ); } sub _make_poco { my $self = shift; return POE::Component::CPAN::LinksToDocs::No404s::Remember->spawn( debug => $self->{debug}, obj_args => $self->{obj_args}, ); } sub _make_response_message { my $self = shift; my $in_ref = shift; my $response = ''; my @links = @{ $in_ref->{response} }; my $message_404 = quotemeta( $self->{obj_args}{message_404} || 'Not found' ); if ( @links > 1 and !$self->{no_filter} ) { @links = grep { !/^(?:Network error|$message_404)/ } @links; } while ( $self->{max_length} > ( length($links[0]) + length $response ) ) { $response .= ' ' . shift @links; defined $links[0] or last; } return [ substr $response, 1 ]; } sub _make_response_event { my $self = shift; my $in_ref = shift; return { tags => $in_ref->{tags}, response => $in_ref->{response}, map { $_ => $in_ref->{"_$_"} } qw( who channel message type ), } } sub _make_poco_call { my $self = shift; my $data_ref = shift; my %seen; my $tags = join q|,|, grep { not $seen{$_}++ } split q|,|, delete $data_ref->{what}; $self->{poco}->link_for( { event => '_poco_done', tags => $tags, map +( "_$_" => $data_ref->{$_} ), keys %$data_ref, } ); } 1; __END__ =head1 NAME POE::Component::IRC::Plugin::CPAN::LinksToDocs::No404s::Remember - link to http://search.cpan.org/ documentation from IRC (and check that all links lead to existing docs, remembering which ones work) =head1 SYNOPSIS use strict; use warnings; use POE qw(Component::IRC Component::IRC::Plugin::CPAN::LinksToDocs::No404s::Remember); my $irc = POE::Component::IRC->spawn( nick => 'DocBot', server => 'irc.freenode.net', port => 6667, ircname => 'Documentation Bot', ); POE::Session->create( package_states => [ main => [ qw(_start irc_001) ], ], ); $poe_kernel->run; sub _start { $irc->yield( register => 'all' ); $irc->plugin_add( 'DocLinks' => POE::Component::IRC::Plugin::CPAN::LinksToDocs::No404s::Remember->new ); $irc->yield( connect => {} ); } sub irc_001 { $_[KERNEL]->post( $_[SENDER] => join => '#zofbot' ); } DocBot, perldoc map http://perldoc.perl.org/functions/map.html DocBot, uri map,grep,File::Find,SomeWeirdModuleThatDoesn'tExist http://perldoc.perl.org/functions/map.html http://perldoc.perl.org/functions/grep.html http://search.cpan.org/perldoc?File::Find Not found =head1 DESCRIPTION This module is a L plugin which uses L for its base. It provides means to get links to documentation on L by giving the plugin predefined "tags" or names of modules on CPAN. It accepts input from public channel events, C messages as well as C (private messages); although that can be configured at will. For predefined "tags" see documentation for L module. B plugin filters out duplicate tags. In other words if the request is C you'll get only one link in return. B plugin stores links to modules (which weren't 404ed) in an SQLite file, you can change the name of that file via C argument. =head1 CONSTRUCTOR =head2 new # plain and simple $irc->plugin_add( 'DocLinks' => POE::Component::IRC::Plugin::CPAN::LinksToDocs::No404s::Remember->new ); # juicy flavor $irc->plugin_add( 'DocLinks' => POE::Component::IRC::Plugin::CPAN::LinksToDocs::No404s::Remember->new( auto => 1, response_event => 'irc_cpan_links_to_docs', banned => [ qr/aol\.com$/i ], addressed => 1, trigger => qr/^docs\s+(?=\S)/i, listen_for_input => [ qw(public notice privmsg) ], obj_args => { tags => { foos => 'bars' }, db_file => 'working_links.db', }, no_filter => 1, max_length => 300, eat => 1, debug => 0, ) ); The C method constructs and returns a new C object suitable to be fed to L's C method. The constructor takes a few arguments, but I. The possible arguments/values are as follows: =head3 auto ->new( auto => 0 ); B. Takes either true or false values, specifies whether or not the plugin should auto respond to requests. When the C argument is set to a true value plugin will respond to the requesting person with the results automatically. When the C argument is set to a false value plugin will not respond and you will have to listen to the events emited by the plugin to retrieve the results (see EMITED EVENTS section and C argument for details). B C<1>. =head3 response_event ->new( response_event => 'event_name_to_recieve_results' ); B. Takes a scalar string specifying the name of the event to emit when the results of the request are ready. See EMITED EVENTS section for more information. B C =head3 banned ->new( banned => [ qr/aol\.com$/i ] ); B. Takes an arrayref of regexes as a value. If the usermask of the person (or thing) making the request matches any of the regexes listed in the C arrayref, plugin will ignore the request. B C<[]> (no bans are set). =head3 trigger ->new( trigger => qr/^docs\s+(?=\S)/i ); B. Takes a regex as an argument. Messages matching this regex will be considered as requests. See also B option below which is enabled by default. B the trigger will be B from the message, therefore make sure your trigger doesn't match the actual data that needs to be processed. B C =head3 addressed ->new( addressed => 1 ); B. Takes either true or false values. When set to a true value all the public messages must be I. In other words, if your bot's nickname is C and your trigger is C you would make the request by saying C. When addressed mode is turned on, the bot's nickname, including any whitespace and common punctuation character will be removed before matching the C (see above). When C argument it set to a false value, public messages will only have to match C regex in order to make a request. Note: this argument has no effect on C and C requests. B C<1> =head3 listen_for_input ->new( listen_for_input => [ qw(public notice privmsg) ] ); B. Takes an arrayref as a value which can contain any of the three elements, namely C, C and C which indicate which kind of input plugin should respond to. When the arrayref contains C element, plugin will respond to requests sent from messages in public channels (see C argument above for specifics). When the arrayref contains C element plugin will respond to requests sent to it via C messages. When the arrayref contains C element, the plugin will respond to requests sent to it via C (private messages). You can specify any of these. In other words, setting C<( listen_for_input => [ qr(notice privmsg) ] )> will enable functionality only via C and C messages. B C<[ qw(public notice privmsg) ]> =head3 obj_args ->new( obj_args => { tags => { foos => 'bars' }, db_file => 'working_links.db', }, ) B. The C argument takes a hashref as a value which will be dereferenced into L constructor (in case you want to add custom tags, change the name of the db file , etc.). See documentation for L for possible arguments. B C<{}> (default constructor) =head3 no_filter ->new( no_filter => 1 ); B. By default plugin will filter the IRC message from all the network errors and 404s. If you want to prevent that and see a bunch of "Not found" or "Network error: blah" messages set C argument to a true value. B the C will still get full, non-filtered results even no matter of what the C argument is set to. B not set (filter all 404s and network errors) =head3 max_length ->new( max_length => 300 ); B. Specifies the maximum length of output sent to IRC (when C argument is turned on). B only the link(s) whose total length (including spaces that separate them) is less than C argument's value (see constructor) will be spoken in IRC, B of them will be returned in the response event. B C<300> =head3 eat ->new( eat => 0 ); B. If set to a false value plugin will return a C after responding. If eat is set to a true value, plugin will return a C after responding. See L documentation for more information if you are interested. B: C<1> =head3 debug ->new( debug => 1 ); B. Takes either a true or false value. When C argument is set to a true value some debugging information will be printed out. When C argument is set to a false value no debug info will be printed. B C<0>. =head1 EMITED EVENTS =head2 response_event $VAR1 = { 'who' => 'Zoffix!n=Zoffix@unaffiliated/zoffix', 'response' => [ 'http://perldoc.perl.org/functions/map.html', 'http://search.cpan.org/perldoc?Acme::BabyEater', 'http://search.cpan.org/perldoc?perlboot', 'http://search.cpan.org/perldoc?perltoot', 'http://search.cpan.org/perldoc?perltooc', 'http://search.cpan.org/perldoc?perlbot' ], 'type' => 'public', 'channel' => '#zofbot', 'message' => 'DocBot, uri map,Acme::BabyEater,OOP', 'tags' => 'map,Acme::BabyEater,OOP' }; The event handler set up to handle the event, name of which you've specified in the C argument to the constructor (it defaults to C) will recieve input every time request is completed. The input will come in C<$_[ARG0]> in a form of a hashref. The keys/values of that hashref are as follows: =head2 who { 'who' => 'Zoffix!n=Zoffix@unaffiliated/zoffix' } The usermask of the person who made the request. =head2 tags { 'tags' => 'map,Acme::BabyEater,OOP' } The user's message after stripping the trigger. =head2 type { 'type' => 'public' } The type of the request. This will be either C, C or C =head2 channel { 'channel' => '#zofbot' } The channel where the message came from (this will only make sense when the request came from a public channel as opposed to /notice or /msg) =head2 message { 'message' => 'DocBot, uri map,Acme::BabyEater,OOP' } The full message that the user has sent. =head2 response { 'response' => [ 'http://perldoc.perl.org/functions/map.html', 'http://search.cpan.org/perldoc?Acme::BabyEater', 'http://search.cpan.org/perldoc?perlboot', 'http://search.cpan.org/perldoc?perltoot', 'http://search.cpan.org/perldoc?perltooc', 'http://search.cpan.org/perldoc?perlbot' ], } The result of the request. B only the link(s) whose total length (including spaces that separate them) is less than C argument's value (see constructor) will be spoken in IRC, B of them will be returned in the response event. =head1 AUTHOR Zoffix Znet, C<< >> (L, L) =head1 BUGS Please report any bugs or feature requests to C, or through the web interface at L. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes. =head1 SUPPORT You can find documentation for this module with the perldoc command. perldoc POE::Component::IRC::Plugin::CPAN::LinksToDocs::No404s::Remember You can also look for information at: =over 4 =item * RT: CPAN's request tracker L =item * AnnoCPAN: Annotated CPAN documentation L =item * CPAN Ratings L =item * Search CPAN L =back =head1 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. =cut