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