File Coverage

blib/lib/POE/Component/IRC/Plugin.pm
Criterion Covered Total %
statement 12 12 100.0
branch n/a
condition n/a
subroutine 4 4 100.0
pod n/a
total 16 16 100.0


line stmt bran cond sub pod time code
1             package POE::Component::IRC::Plugin;
2             $POE::Component::IRC::Plugin::VERSION = '6.95';
3 79     79   506 use strict;
  79         162  
  79         3206  
4 79     79   403 use warnings FATAL => 'all';
  79         138  
  79         7008  
5              
6             require Exporter;
7 79     79   481 use base qw(Exporter);
  79         140  
  79         13693  
8             our @EXPORT_OK = qw(PCI_EAT_NONE PCI_EAT_CLIENT PCI_EAT_PLUGIN PCI_EAT_ALL);
9             our %EXPORT_TAGS = ( ALL => [@EXPORT_OK] );
10              
11             use constant {
12 79         14211 PCI_EAT_NONE => 1,
13             PCI_EAT_CLIENT => 2,
14             PCI_EAT_PLUGIN => 3,
15             PCI_EAT_ALL => 4,
16 79     79   561 };
  79         172  
17              
18             1;
19              
20             =encoding utf8
21              
22             =head1 NAME
23              
24             POE::Component::IRC::Plugin - Provides plugin constants and documentation for
25             L
26              
27             =head1 SYNOPSIS
28              
29             # A simple ROT13 'encryption' plugin
30              
31             package Rot13;
32              
33             use strict;
34             use warnings;
35             use POE::Component::IRC::Plugin qw( :ALL );
36              
37             # Plugin object constructor
38             sub new {
39             my $package = shift;
40             return bless {}, $package;
41             }
42              
43             sub PCI_register {
44             my ($self, $irc) = splice @_, 0, 2;
45              
46             $irc->plugin_register( $self, 'SERVER', qw(public) );
47             return 1;
48             }
49              
50             # This is method is mandatory but we don't actually have anything to do.
51             sub PCI_unregister {
52             return 1;
53             }
54              
55             sub S_public {
56             my ($self, $irc) = splice @_, 0, 2;
57              
58             # Parameters are passed as scalar-refs including arrayrefs.
59             my $nick = ( split /!/, ${ $_[0] } )[0];
60             my $channel = ${ $_[1] }->[0];
61             my $msg = ${ $_[2] };
62              
63             if (my ($rot13) = $msg =~ /^rot13 (.+)/) {
64             $rot13 =~ tr[a-zA-Z][n-za-mN-ZA-M];
65              
66             # Send a response back to the server.
67             $irc->yield( privmsg => $channel => $rot13 );
68             # We don't want other plugins to process this
69             return PCI_EAT_PLUGIN;
70             }
71              
72             # Default action is to allow other plugins to process it.
73             return PCI_EAT_NONE;
74             }
75              
76             =head1 DESCRIPTION
77              
78             POE::Component::IRC's plugin system has been released separately as
79             L. Gleaning at its documentation is
80             advised. The rest of this document mostly describes aspects that are
81             specific to POE::Component::IRC's use of Object::Pluggable.
82              
83             =head1 HISTORY
84              
85             Certain individuals in #PoE on MAGNet said we didn't need to bloat the
86             PoCo-IRC code...
87              
88             BinGOs, the current maintainer of the module, and I heartily agreed that this
89             is a wise choice.
90              
91             One example:
92              
93             Look at the magnificent new feature in 3.4 -> irc_whois replies! Yes, that is
94             a feature I bet most of us have been coveting for a while, as it definitely
95             makes our life easier. It was implemented in 30 minutes or so after a request,
96             the maintainer said. I replied by saying that it's a wonderful idea, but what
97             would happen if somebody else asked for a new feature? Maybe thatfeature is
98             something we all would love to have, so should it be put in the core? Plugins
99             allow the core to stay lean and mean, while delegating additional functionality
100             to outside modules. BinGOs' work with making PoCo-IRC inheritable is wonderful,
101             but what if there were 2 modules which have features that you would love to
102             have in your bot? Inherit from both? Imagine the mess...
103              
104             Here comes plugins to the rescue :)
105              
106             You could say Bot::Pluggable does the job, and so on, but if this feature were
107             put into the core, it would allow PoCo-IRC to be extended beyond our wildest
108             dreams, and allow the code to be shared amongst us all, giving us superior bug
109             smashing abilities.
110              
111             Yes, there are changes that most of us will moan when we go update our bots to
112             use the new C<$irc> object system, but what if we also used this opportunity to
113             improve PoCo-IRC even more and give it a lifespan until Perl8 or whatever comes
114             along? :)
115              
116             =head1 DESCRIPTION
117              
118             The plugin system works by letting coders hook into the two aspects of PoCo-IRC:
119              
120             =over
121              
122             =item *
123              
124             Data received from the server
125              
126             =item *
127              
128             User commands about to be sent to the server
129              
130             =back
131              
132             The goal of this system is to make PoCo-IRC so easy to extend, enabling it to
133             Take Over The World! *Just Kidding*
134              
135             The general architecture of using the plugins should be:
136              
137             # Import the stuff...
138             use POE;
139             use POE::Component::IRC;
140             use POE::Component::IRC::Plugin::ExamplePlugin;
141              
142             # Create our session here
143             POE::Session->create( ... );
144              
145             # Create the IRC session here
146             my $irc = POE::Component::IRC->spawn() or die "Oh noooo! $!";
147              
148             # Create the plugin
149             # Of course it could be something like $plugin = MyPlugin->new();
150             my $plugin = POE::Component::IRC::Plugin::ExamplePlugin->new( ... );
151              
152             # Hook it up!
153             $irc->plugin_add( 'ExamplePlugin', $plugin );
154              
155             # OOPS, we lost the plugin object!
156             my $pluginobj = $irc->plugin_get( 'ExamplePlugin' );
157              
158             # We want a list of plugins and objects
159             my $hashref = $irc->plugin_list();
160              
161             # Oh! We want a list of plugin aliases.
162             my @aliases = keys %{ $irc->plugin_list() };
163              
164             # Ah, we want to remove the plugin
165             $plugin = $irc->plugin_del( 'ExamplePlugin' );
166              
167             The plugins themselves will conform to the standard API described here. What
168             they can do is limited only by imagination and the IRC RFC's ;)
169              
170             # Import the constants
171             use POE::Component::IRC::Plugin qw( :ALL );
172              
173             # Our constructor
174             sub new {
175             ...
176             }
177              
178             # Required entry point for PoCo-IRC
179             sub PCI_register {
180             my ($self, $irc) = @_;
181             # Register events we are interested in
182             $irc->plugin_register( $self, 'SERVER', qw( 355 kick whatever) );
183              
184             # Return success
185             return 1;
186             }
187              
188             # Required exit point for PoCo-IRC
189             sub PCI_unregister {
190             my ($self, $irc) = @_;
191              
192             # PCI will automatically unregister events for the plugin
193              
194             # Do some cleanup...
195              
196             # Return success
197             return 1;
198             }
199              
200             # Registered events will be sent to methods starting with IRC_
201             # If the plugin registered for SERVER - irc_355
202             sub S_355 {
203             my($self, $irc, $line) = @_;
204              
205             # Remember, we receive pointers to scalars, so we can modify them
206             $$line = 'frobnicate!';
207              
208             # Return an exit code
209             return PCI_EAT_NONE;
210             }
211              
212             # Default handler for events that do not have a corresponding plugin
213             # method defined.
214             sub _default {
215             my ($self, $irc, $event) = splice @_, 0, 3;
216              
217             print "Default called for $event\n";
218              
219             # Return an exit code
220             return PCI_EAT_NONE;
221             }
222              
223             Plugins can even embed their own POE sessions if they need to do fancy stuff.
224             Below is a template for a plugin which does just that.
225              
226             package POE::Plugin::Template;
227              
228             use POE;
229             use POE::Component::IRC::Plugin qw( :ALL );
230              
231             sub new {
232             my $package = shift;
233             my $self = bless {@_}, $package;
234             return $self;
235             }
236              
237             sub PCI_register {
238             my ($self, $irc) = splice @_, 0, 2;
239              
240             # We store a ref to the $irc object so we can use it in our
241             # session handlers.
242             $self->{irc} = $irc;
243              
244             $irc->plugin_register( $self, 'SERVER', qw(blah blah blah) );
245              
246             POE::Session->create(
247             object_states => [
248             $self => [qw(_start _shutdown)],
249             ],
250             );
251              
252             return 1;
253             }
254              
255             sub PCI_unregister {
256             my ($self, $irc) = splice @_, 0, 2;
257             # Plugin is dying make sure our POE session does as well.
258             $poe_kernel->call( $self->{SESSION_ID} => '_shutdown' );
259             delete $self->{irc};
260             return 1;
261             }
262              
263             sub _start {
264             my ($kernel, $self) = @_[KERNEL, OBJECT];
265             $self->{SESSION_ID} = $_[SESSION]->ID();
266             # Make sure our POE session stays around. Could use aliases but that is so messy :)
267             $kernel->refcount_increment( $self->{SESSION_ID}, __PACKAGE__ );
268             return;
269             }
270              
271             sub _shutdown {
272             my ($kernel, $self) = @_[KERNEL, OBJECT];
273             $kernel->alarm_remove_all();
274             $kernel->refcount_decrement( $self->{SESSION_ID}, __PACKAGE__ );
275             return;
276             }
277              
278             =head1 EVENT TYPES
279              
280             =head2 SERVER hooks
281              
282             Hooks that are targeted toward data received from the server will get the exact
283             same arguments as if it was a normal event, look at the PoCo-IRC docs for more
284             information.
285              
286             NOTE: Server methods are identified in the plugin namespace by the subroutine
287             prefix of S_*. I.e. an irc_kick event handler would be:
288              
289             sub S_kick {}
290              
291             The only difference is instead of getting scalars, the hook will get a
292             reference to the scalar, to allow it to mangle the data. This allows the plugin
293             to modify data *before* they are sent out to registered sessions.
294              
295             They are required to return one of the L so PoCo-IRC
296             will know what to do.
297              
298             =head3 Names of potential hooks
299              
300             001
301             socketerr
302             connected
303             plugin_del
304             ...
305              
306             Keep in mind that they are always lowercased. Check out the
307             L section of
308             POE::Component::IRC's documentation for the complete list of events.
309              
310             =head2 USER hooks
311              
312             These type of hooks have two different argument formats. They are split between
313             data sent to the server, and data sent through DCC connections.
314              
315             NOTE: User methods are identified in the plugin namespace by the subroutine
316             prefix of U_*. I.e. an irc_kick event handler would be:
317              
318             sub U_kick {}
319              
320             Hooks that are targeted to user data have it a little harder. They will receive
321             a reference to the raw line about to be sent out. That means they will have to
322             parse it in order to extract data out of it.
323              
324             The reasoning behind this is that it is not possible to insert hooks in every
325             method in the C<$irc> object, as it will become unwieldy and not allow inheritance
326             to work.
327              
328             The DCC hooks have it easier, as they do not interact with the server, and will
329             receive references to the arguments specified in the DCC plugin
330             L regarding dcc commands.
331              
332             =head3 Names of potential hooks
333              
334             kick
335             dcc_chat
336             ison
337             privmsg
338             ...
339              
340             Keep in mind that they are always lowercased, and are extracted from the raw
341             line about to be sent to the irc server. To be able to parse the raw line, some
342             RFC reading is in order. These are the DCC events that are not given a raw
343             line, they are:
344              
345             dcc - $nick, $type, $file, $blocksize, $timeout
346             dcc_accept - $cookie, $myfile
347             dcc_resume - $cookie
348             dcc_chat - $cookie, @lines
349             dcc_close - $cookie
350              
351             =head2 _default
352              
353             If a plugin has registered for an event but doesn't have a hook method
354             defined for ir, component will attempt to call a plugin's C<_default> method.
355             The first parameter after the plugin and irc objects will be the handler name.
356              
357             sub _default {
358             my ($self, $irc, $event) = splice @_, 0, 3;
359              
360             # $event will be something like S_public or U_dcc, etc.
361             return PCI_EAT_NONE;
362             }
363              
364             The C<_default> handler is expected to return one of the exit codes so PoCo-IRC
365             will know what to do.
366              
367             =head1 EXIT CODES
368              
369             =head2 PCI_EAT_NONE
370              
371             This means the event will continue to be processed by remaining plugins and
372             finally, sent to interested sessions that registered for it.
373              
374             =head2 PCI_EAT_CLIENT
375              
376             This means the event will continue to be processed by remaining plugins but
377             it will not be sent to any sessions that registered for it. This means nothing
378             will be sent out on the wire if it was an USER event, beware!
379              
380             =head2 PCI_EAT_PLUGIN
381              
382             This means the event will not be processed by remaining plugins, it will go
383             straight to interested sessions.
384              
385             =head2 PCI_EAT_ALL
386              
387             This means the event will be completely discarded, no plugin or session will
388             see it. This means nothing will be sent out on the wire if it was an USER
389             event, beware!
390              
391             =head1 EXPORTS
392              
393             Exports the return constants for plugins to use in @EXPORT_OK
394             Also, the ':ALL' tag can be used to get all of them.
395              
396             =head1 SEE ALSO
397              
398             L
399              
400             L
401              
402             L
403              
404             L
405              
406             =head1 AUTHOR
407              
408             Apocalypse
409              
410             =cut