1POE::Component::IRC::PlUusgeirn(C3o)ntributed Perl DocumPeOnEt:a:tCioomnponent::IRC::Plugin(3)
2
3
4
6 Provides plugin documentation for PoCo-IRC
7
9 0.08
10
11 Added EXAMPLES section.
12
13 0.07
14
15 The plugin sytem has changed to use L<POE::Component::IRC::Pipeline> now. See its documentation
16 for information about the underlying operations of the pipeline.
17
18 There's a new method, plugin_order, which returns an array reference of the plugins in the pipeline
19 in the order in which they are executed.
20
21 There's a new method, pipeline, which returns the POE::Component::IRC::Pipeline object so you can
22 deal with its finer-tuned controls yourself.
23
24 The _plugin_unregister_do method is no more.
25
26 0.06
27
28 Updated _plugin_process so that it runs plugin method calls in an 'eval'. Rogue plugins shouldn't be
29 able to crash the component now.
30
31 If a plugin doesn't have a event handler method defined now, the component will try to call a
32 _default() handler instead.
33
34 0.05
35
36 Realized that there would be collision between USER/SERVER methods, so made it distinct by using S_* and U_*
37 Clarified the documentation to stress that 'irc_' is not required for event names
38 Changed the description of the 2 new events to stress that they are sent *after* the action is done
39
40 0.04
41
42 Changed _plugin_register/unregister to non-private methods
43
44 0.03
45
46 As per perigrin's suggestion, added 2 new event types to monitor plugin add/del
47 Updated the name from PoCo-IRC-Plugins to PoCo-IRC-Plugin
48 Updated return value ( PCI_EAT_PLUGINS to PCI_EAT_PLUGIN )
49 Updated plugin_del to also accept the plugin object instead of a name
50
51 0.02
52
53 Modified plugin_del() so it returns the plugin object
54
55 0.01
56
57 Initial release
58
60 Certain individuals in #PoE on MAGNet said we didn't need to bloat the
61 PoCo-IRC code...
62
63 BinGOs, the current maintainer of the module, and I heartily agreed
64 that this is a wise choice.
65
66 One example:
67
68 Look at the magnificent new feature in 3.4 -> irc_whois replies! Yes, that is a feature I bet
69 most of us have been coveting for a while, as it definitely makes our life easier. It was
70 implemented in 30 minutes or so after a request, the maintainer said. I replied by saying that
71 it's a wonderful idea, but what would happen if somebody else asked for a new feature? Maybe that
72 feature is something we all would love to have, so should it be put in the core? Plugins allow the
73 core to stay lean and mean, while delegating additional functionality to outside modules. BinGOs' work
74 with making PoCo-IRC inheritable is wonderful, but what if there were 2 modules which have features
75 that you would love to have in your bot? Inherit from both? Imagine the mess...
76
77 Here comes plugins to the rescue :)
78
79 You could say Bot::Pluggable does the job, and so on, but if this fea‐
80 ture were put into the core, it would allow PoCo-IRC to be extended
81 beyond our wildest dreams, and allow the code to be shared amongst us
82 all, giving us superior bug smashing abilities.
83
84 Yes, there are changes that most of us will moan when we go update our
85 bots to use the new $irc object system, but what if we also used this
86 opportunity to improve PoCo-IRC even more and give it a lifespan until
87 Perl8 or whatever comes along? :)
88
90 This is the document coders/users should refer to when using/developing
91 plugins for POE::Component::IRC.
92
93 The plugin system works by letting coders hook into the two aspects of
94 PoCo-IRC:
95
96 Data received from the server
97 User commands about to be sent to the server
98
99 The goal of this system is to make PoCo-IRC so easy to extend, enabling
100 it to Take Over The World! *Just Kidding*
101
102 The general architecture of using the plugins should be:
103
104 # Import the stuff...
105 use POE;
106 use POE::Component::IRC;
107 use POE::Component::IRC::Plugin::ExamplePlugin;
108
109 # Create our session here
110 POE::Session->create( ... );
111
112 # Create the IRC session here
113 my $irc = POE::Component::IRC->spawn() or die 'Nooo!';
114
115 # Create the plugin
116 # Of course it could be something like $plugin = MyPlugin->new();
117 my $plugin = POE::Component::IRC::Plugin::ExamplePlugin->new( ... );
118
119 # Hook it up!
120 $irc->plugin_add( 'ExamplePlugin', $plugin );
121
122 # OOPS, we lost the plugin object!
123 my $pluginobj = $irc->plugin_get( 'ExamplePlugin' );
124
125 # We want a list of plugins and objects
126 my $hashref = $irc->plugin_list();
127
128 # Oh! We want a list of plugin aliases.
129 my @aliases = keys %{ $irc->plugin_list() };
130
131 # Ah, we want to remove the plugin
132 $plugin = $irc->plugin_del( 'ExamplePlugin' );
133
134 The plugins themselves will conform to the standard API described here.
135 What they can do is limited only by imagination and the IRC RFC's ;)
136
137 # Import the constants
138 use POE::Component::IRC::Plugin qw( :ALL );
139
140 # Our constructor
141 sub new {
142 ...
143 }
144
145 # Required entry point for PoCo-IRC
146 sub PCI_register {
147 my( $self, $irc ) = @_;
148
149 # Register events we are interested in
150 $irc->plugin_register( $self, 'SERVER', qw( 355 kick whatever) );
151
152 # Return success
153 return 1;
154 }
155
156 # Required exit point for PoCo-IRC
157 sub PCI_unregister {
158 my( $self, $irc ) = @_;
159
160 # PCI will automatically unregister events for the plugin
161
162 # Do some cleanup...
163
164 # Return success
165 return 1;
166 }
167
168 # Registered events will be sent to methods starting with IRC_
169 # If the plugin registered for SERVER - irc_355
170 sub S_355 {
171 my( $self, $irc, $line ) = @_;
172
173 # Remember, we receive pointers to scalars, so we can modify them
174 $$line = 'frobnicate!';
175
176 # Return an exit code
177 return PCI_EAT_NONE;
178 }
179
180 # Default handler for events that do not have a corresponding plugin method defined.
181 sub _default {
182 my( $self, $irc, $event ) = splice @_, 0, 3;
183
184 print "Default called for $event\n";
185
186 # Return an exit code
187 return PCI_EAT_NONE;
188 }
189
191 pipeline
192
193 This method returns (or creates) the pipeline object into which plugins are stored.
194
195 plugin_add
196
197 Accepts two arguments:
198 The alias for the plugin
199 The actual plugin object
200
201 The alias is there for the user to refer to it, as it is possible to have multiple
202 plugins of the same kind active in one PoCo-IRC object.
203
204 This method goes through the pipeline's push() method.
205
206 This method will call $plugin->PCI_register( $irc )
207
208 Returns the number of plugins now in the pipeline if plugin was initialized, undef
209 if not.
210
211 plugin_get
212
213 Accepts one argument:
214 The alias for the plugin
215
216 This method goes through the pipeline's get() method.
217
218 Returns the plugin object if it was found, undef if not.
219
220 plugin_del
221
222 Accepts one argument:
223 The alias for the plugin or the plugin object itself
224
225 This method goes through the pipeline's remove() method.
226
227 This method will call $plugin->PCI_unregister( $irc )
228
229 Returns the plugin object if the plugin was removed, undef if not.
230
231 plugin_list
232
233 Has no arguments.
234
235 Returns a hashref of plugin objects, keyed on alias, or an empty list if there are no
236 plugins loaded.
237
238 plugin_order
239
240 Has no arguments.
241
242 Returns an arrayref of plugin objects, in the order which they are encountered in the
243 pipeline.
244
245 plugin_register
246
247 Accepts the following arguments:
248 The plugin object
249 The type of the hook ( 'SERVER' or 'USER' )
250 The event name(s) to watch
251
252 The event names can be as many as possible, or an arrayref. They correspond
253 to the irc_* events listed in PoCo-IRC, and naturally, arbitrary events too.
254
255 You do not need to supply events with irc_ in front of them, just the names.
256
257 It is possible to register for all events by specifying 'all' as an event.
258
259 Returns 1 if everything checked out fine, undef if something's seriously wrong
260
261 plugin_unregister
262
263 Accepts the following arguments:
264 The plugin object
265 The type of the hook ( 'SERVER' or 'USER' )
266 The event name(s) to unwatch
267
268 The event names can be as many as possible, or an arrayref. They correspond
269 to the irc_* events listed in PoCo-IRC, and naturally, arbitrary events too.
270
271 You do not need to supply events with irc_ in front of them, just the names.
272
273 Returns 1 if all the event name(s) was unregistered, undef if some was not found
274
276 irc_plugin_add
277
278 This event will be triggered after a plugin is added. It receives two
279 arguments, the first being the plugin name, and the second being the
280 plugin object.
281
282 irc_plugin_del
283
284 This event will be triggered after a plugin is deleted. It receives two
285 arguments, the first being the plugin name, and the second being the
286 plugin object.
287
289 SERVER hooks
290
291 Hooks that are targeted toward data received from the server will get
292 the exact same arguments as if it was a normal event, look at the PoCo-
293 IRC docs for more information.
294
295 NOTE: Server methods are identified in the plugin namespace by the subroutine prefix
296 of S_*. I.e. an irc_kick event handler would be:
297
298 sub S_kick {}
299
300 The only difference is instead of getting scalars, the hook will get a
301 reference to the scalar, to allow it to mangle the data. This allows
302 the plugin to modify data *before* they are sent out to registered ses‐
303 sions.
304
305 They are required to return one of the exit codes so PoCo-IRC will know
306 what to do.
307
308 Names of potential hooks
309
310 001
311 socketerr
312 connected
313 plugin_del
314
315 Keep in mind that they are always lowercased, check out the POE::Compo‐
316 nent::IRC manpage and look at the Important Events section for the com‐
317 plete list of names.
318
319 USER hooks
320
321 These type of hooks have two different argument formats. They are split
322 between data sent to the server, and data sent through DCC connections.
323
324 NOTE: User methods are identified in the plugin namespace by the subroutine prefix
325 of U_*. I.e. an irc_kick event handler would be:
326
327 sub U_kick {}
328
329 Hooks that are targeted to user data have it a little harder. They will
330 receive a reference to the raw line about to be sent out. That means
331 they will have to parse it in order to extract data out of it.
332
333 The reasoning behind this is that it is not possible to insert hooks in
334 every method in the $irc object, as it will become unwieldy and not
335 allow inheritance to work.
336
337 The DCC hooks have it easier, as they do not interact with the server,
338 and will receive references to the arguments specified in the PoCo-IRC
339 pod regarding dcc commands.
340
341 Names of potential hooks
342
343 kick
344 dcc_chat
345 ison
346 privmsg
347
348 Keep in mind that they are always lowercased, and are extracted from
349 the raw line about to be sent to the irc server. To be able to parse
350 the raw line, some RFC reading is in order. These are the DCC events
351 that are not given a raw line, they are:
352
353 dcc - $nick, $type, $file, $blocksize
354 dcc_accept - $cookie, $myfile
355 dcc_resume - $cookie
356 dcc_chat - $cookie, @lines
357 dcc_close - $cookie
358
359 _default
360
361 If a plugin doesn't have a specific hook method defined for an event,
362 the component will attempt to call a plugin's _default() method. The
363 first parameter after the plugin and irc objects will be the handler
364 name.
365
366 sub _default {
367 my ($self,$irc,$event) = splice @_, 0, 3;
368
369 # $event will be something like S_public or U_dcc, etc.
370 return PCI_EAT_NONE;
371 }
372
373 The _default() handler is expected to return one of the exit codes so
374 PoCo-IRC will know what to do.
375
377 PCI_EAT_NONE
378
379 This means the event will continue to be processed by remaining plugins and
380 finally, sent to interested sessions that registered for it.
381
382 PCI_EAT_CLIENT
383
384 This means the event will continue to be processed by remaining plugins but
385 it will not be sent to any sessions that registered for it. This means nothing
386 will be sent out on the wire if it was an USER event, beware!
387
388 PCI_EAT_PLUGIN
389
390 This means the event will not be processed by remaining plugins, it will go
391 straight to interested sessions.
392
393 PCI_EAT_ALL
394
395 This means the event will be completely discarded, no plugin or session will see it. This
396 means nothing will be sent out on the wire if it was an USER event, beware!
397
399 See POE::Component::IRC::Pipeline
400
402 Exports the return constants for plugins to use in @EXPORT_OK
403 Also, the ':ALL' tag can be used to get all of them
404
406 A simple ROT13 'encryption' plugin
407 package Rot13;
408
409 use strict qw(subs vars refs); # Make sure we can't mess up
410 use warnings FATAL => 'all';
411 use POE::Component::IRC::Plugin qw( :ALL );
412
413 # Plugin object constructor
414 sub new {
415 my $package = shift;
416 return bless {}, $package;
417 }
418
419 sub PCI_register {
420 my ( $self, $irc ) = splice @_, 0, 2;
421
422 $irc->plugin_register( $self, 'SERVER', qw(public) );
423 return 1;
424 }
425
426 # This is method is mandatory but we don't actually have anything to do.
427 sub PCI_unregister {
428 return 1;
429 }
430
431 sub S_public {
432 my ( $self, $irc ) = splice @_, 0, 2;
433
434 # Parameters are passed as scalar-refs including arrayrefs.
435 my $nick = ( split /!/, ${ $_[0] } )[0];
436 my $channel = ${ $_[1] }->[0];
437 my $msg = ${ $_[2] };
438
439 if ( my ($rot13) = $msg =~ /^rot13 (.+)/ ) {
440 $rot13 =~ tr[a-zA-Z][n-za-mN-ZA-M];
441
442 # Send a response back to the server.
443 $irc->yield( privmsg => $channel => $rot13 );
444 return PCI_EAT_PLUGIN; # We don't want other plugins to process this
445 }
446
447 return PCI_EAT_NONE; # Default action is to allow other plugins to process it.
448 }
449
450 A template for a plugin with it's own POE::Session
451 package POE-Plugin-Template;
452
453 use POE;
454 use POE::Component::IRC::Plugin qw( :ALL );
455
456 sub new {
457 my $package = shift;
458 my $self = bless {@_}, $package;
459 return $self;
460 }
461
462 sub PCI_register {
463 my ( $self, $irc ) = splice @_, 0, 2;
464
465 # We store a ref to the $irc object so we can use it in our
466 # session handlers.
467 $self->{irc} = $irc;
468
469 $irc->plugin_register( $self, 'SERVER', qw(blah blah blah) );
470
471 $self->{SESSION_ID} = POE::Session->create(
472 object_states => [
473 $self => [qw(_start _shutdown)],
474 ],
475 )->ID();
476
477 return 1;
478 }
479
480 sub PCI_unregister {
481 my ( $self, $irc ) = splice @_, 0, 2;
482 # Plugin is dying make sure our POE session does as well.
483 $poe_kernel->call( $self->{SESSION_ID} => '_shutdown' );
484 delete $self->{irc};
485 return 1;
486 }
487
488 sub _start {
489 my ( $kernel, $self ) = @_[ KERNEL, OBJECT ];
490 $self->{SESSION_ID} = $_[SESSION]->ID();
491 # Make sure our POE session stays around. Could use aliases but that is so messy :)
492 $kernel->refcount_increment( $self->{SESSION_ID}, __PACKAGE__ );
493 undef;
494 }
495
496 sub _shutdown {
497 my ($kernel, $self) = @_[ KERNEL, OBJECT ];
498 $kernel->alarm_remove_all();
499 $kernel->refcount_decrement( $self->{SESSION_ID}, __PACKAGE__ );
500 undef;
501 }
502
504 POE::Component::IRC
505
506 POE::Component::IRC::Pipeline
507
508 POE::Session
509
511 Apocalypse <apocal@cpan.org>
512
514 The idea is heavily borrowed from X-Chat, BIG thanks goes out to the
515 genius that came up with the EAT_* system :)
516
517
518
519perl v5.8.8 2005-10-25 POE::Component::IRC::Plugin(3)