File Coverage

blib/lib/POEx/IRC/Client/Lite.pm
Criterion Covered Total %
statement 102 171 59.6
branch 16 38 42.1
condition 6 20 30.0
subroutine 31 52 59.6
pod 11 20 55.0
total 166 301 55.1


line stmt bran cond sub pod time code
1             package POEx::IRC::Client::Lite;
2             $POEx::IRC::Client::Lite::VERSION = '0.004001';
3 1     1   828 use strictures 2;
  1         8  
  1         54  
4              
5 1     1   240 use Carp 'confess';
  1         1  
  1         63  
6              
7 1     1   5 use POE;
  1         1  
  1         7  
8 1     1   304 use POEx::IRC::Backend;
  1         1  
  1         30  
9              
10 1     1   4 use IRC::Message::Object 'ircmsg';
  1         1  
  1         14  
11 1     1   719 use IRC::Toolkit::Case;
  1         1963  
  1         11  
12 1     1   389 use IRC::Toolkit::CTCP;
  1         2  
  1         8  
13              
14 1     1   226 use POE::Filter::IRCv3;
  1         1  
  1         25  
15              
16 1     1   3 use Scalar::Util 'blessed';
  1         2  
  1         54  
17              
18 1     1   5 use Types::Standard -all;
  1         1  
  1         14  
19              
20 1     1   30216 use MooX::Role::Pluggable::Constants;
  1         467  
  1         73  
21              
22              
23 1     1   6 use Moo;
  1         2  
  1         9  
24             with 'MooX::Role::POE::Emitter';
25              
26              
27             =for Pod::Coverage has_(\w+)
28              
29             =cut
30              
31              
32             has server => (
33             required => 1,
34             is => 'ro',
35             isa => Str,
36             writer => 'set_server',
37             );
38              
39             has nick => (
40             required => 1,
41             is => 'ro',
42             isa => Str,
43             writer => 'set_nick',
44             );
45              
46             after set_nick => sub {
47             my ($self, $nick) = @_;
48             if ($self->_has_conn && $self->conn->has_wheel) {
49             ## Try to change IRC nickname as well.
50             $self->nick($nick)
51             }
52             };
53              
54             has bindaddr => (
55             lazy => 1,
56             is => 'ro',
57             isa => Defined,
58             writer => 'set_bindaddr',
59             predicate => 'has_bindaddr',
60             builder => sub {
61 0     0   0 my ($self) = @_;
62 0 0 0     0 $self->has_ipv6 && $self->ipv6 ?
63             '::0' : '0.0.0.0'
64             },
65             );
66              
67             has ipv6 => (
68             lazy => 1,
69             is => 'ro',
70             isa => Bool,
71             writer => 'set_ipv6',
72             predicate => 'has_ipv6',
73 0     0   0 builder => sub { 0 },
74             );
75              
76             has ssl => (
77             lazy => 1,
78             is => 'ro',
79             isa => Bool,
80             writer => 'set_ssl',
81 1     1   1730 builder => sub { 0 },
82             );
83              
84             has ssl_opts => (
85             lazy => 1,
86             is => 'ro',
87             isa => Maybe[ArrayRef],
88             writer => 'set_ssl_opts',
89 1     1   1051 builder => sub { undef },
90             );
91              
92             has pass => (
93             lazy => 1,
94             is => 'ro',
95             isa => Str,
96             writer => 'set_pass',
97             predicate => 'has_pass',
98             clearer => 'clear_pass',
99 0     0   0 builder => sub { '' },
100             );
101              
102             has port => (
103             lazy => 1,
104             is => 'ro',
105             isa => Num,
106             writer => 'set_port',
107             predicate => 'has_port',
108 0     0   0 builder => sub { 6667 },
109             );
110              
111             has realname => (
112             lazy => 1,
113             is => 'ro',
114             isa => Str,
115             writer => 'set_realname',
116             predicate => 'has_realname',
117 1     1   1103 builder => sub { __PACKAGE__ },
118             );
119              
120             has reconnect => (
121             lazy => 1,
122             is => 'ro',
123             isa => Num,
124             writer => 'set_reconnect',
125 0     0   0 builder => sub { 120 },
126             );
127              
128             has username => (
129             lazy => 1,
130             is => 'ro',
131             isa => Str,
132             writer => 'set_username',
133             predicate => 'has_username',
134 0     0   0 builder => sub { 'ircplug' },
135             );
136              
137             ### Typically internal:
138             has backend => (
139             lazy => 1,
140             is => 'ro',
141             isa => InstanceOf['POEx::IRC::Backend'],
142             builder => sub {
143 1     1   810 my $filter = POE::Filter::IRCv3->new(colonify => 0);
144 1         58 POEx::IRC::Backend->new(filter_irc => $filter)
145             }
146              
147             );
148              
149             has conn => (
150             lazy => 1,
151             weak_ref => 1,
152             is => 'ro',
153             isa => Object,
154             writer => '_set_conn',
155             predicate => '_has_conn',
156             clearer => '_clear_conn',
157             );
158              
159              
160             sub BUILD {
161 1     1 0 33930 my ($self) = @_;
162              
163 0         0 $self->set_object_states(
164             [
165             $self => [ qw/
166             ircsock_input
167             ircsock_connector_open
168             ircsock_connector_failure
169             ircsock_disconnect
170             / ],
171             $self => {
172             emitter_started => '_emitter_started',
173             connect => '_connect',
174             disconnect => '_disconnect',
175             send => '_send',
176             privmsg => '_privmsg',
177             ctcp => '_ctcp',
178             notice => '_notice',
179             mode => '_mode',
180             join => '_join',
181             part => '_part',
182             },
183             (
184 1 50       32 $self->has_object_states ? @{ $self->object_states } : ()
185             ),
186             ],
187             );
188              
189 1         3046 $self->_start_emitter;
190             }
191              
192             sub _emitter_started {
193 1     1   15710 my ($kernel, $self) = @_[KERNEL, OBJECT];
194 1 50       9 $self->backend->spawn(
195             ( $self->ssl_opts ? (ssl_opts => $self->ssl_opts) : () ),
196             );
197 1         934 $kernel->post( $self->backend->session_id => 'register' );
198             }
199              
200             sub stop {
201 1     1 1 412733 my ($self) = @_;
202 1         77 $poe_kernel->post( $self->backend->session_id => 'shutdown' );
203 1         120 $self->_shutdown_emitter;
204             }
205              
206             ### ircsock_*
207              
208             sub ircsock_connector_open {
209 1     1 0 17128 my (undef, $self, $conn) = @_[KERNEL, OBJECT, ARG0];
210              
211 1         6 $self->_set_conn( $conn );
212              
213 1 50       544 if ($self->process( preregister => $conn ) == EAT_ALL) {
214 0         0 $self->_clear_conn;
215 0         0 $self->emit( irc_connector_killed => $conn );
216             return
217 0         0 }
218              
219 1         146 my @pre;
220 1 50 33     9 if ($self->has_pass && (my $pass = $self->pass)) {
221 0         0 push @pre, ircmsg(
222             command => 'pass',
223             params => [
224             $pass
225             ],
226             )
227             }
228             $self->send(
229 1         6 @pre,
230             ircmsg(
231             command => 'user',
232             params => [
233             $self->username,
234             '*', '*',
235             $self->realname
236             ],
237             ),
238             ircmsg(
239             command => 'nick',
240             params => [ $self->nick ],
241             ),
242             );
243              
244 1         162 $self->emit( irc_connected => $conn );
245             }
246              
247             sub ircsock_connector_failure {
248 0     0 0 0 my (undef, $self) = @_[KERNEL, OBJECT];
249             #my $connector = $_[ARG0];
250             #my ($op, $errno, $errstr) = @_[ARG1 .. ARG3];
251              
252 0 0       0 $self->_clear_conn if $self->_has_conn;
253              
254 0         0 $self->emit( irc_connector_failed => @_[ARG0 .. $#_] );
255            
256 0 0       0 $self->timer( $self->reconnect => 'connect') if $self->reconnect;
257             }
258              
259             sub ircsock_disconnect {
260 1     1 0 2822 my (undef, $self) = @_[KERNEL, OBJECT];
261 1         5 my ($conn, $str) = @_[ARG0, ARG1];
262            
263 1 50       17 $self->_clear_conn if $self->_has_conn;
264              
265 1         573 $self->emit( irc_disconnected => $str, $conn );
266             }
267              
268             sub ircsock_input {
269 3     3 0 2904 my (undef, $self, $ircev) = @_[KERNEL, OBJECT, ARG1];
270              
271 3 50       10 return unless $ircev->command;
272 3         20 $self->emit( 'irc_'.lc($ircev->command) => $ircev)
273             }
274              
275              
276             ### Our IRC-related handlers.
277              
278             sub N_irc_433 {
279             ## Nickname in use.
280 0     0 0 0 my (undef, $self) = splice @_, 0, 2;
281 0         0 my $ircev = ${ $_[0] };
  0         0  
282              
283 0   0     0 my $taken = $ircev->params->[1] || $self->nick;
284              
285 0         0 $self->send(
286             ircmsg(
287             command => 'nick',
288             params => [ $taken . '_' ],
289             )
290             );
291              
292 0         0 EAT_NONE
293             }
294              
295             sub N_irc_ping {
296 0     0 0 0 my (undef, $self) = splice @_, 0, 2;
297 0         0 my $ircev = ${ $_[0] };
  0         0  
298              
299 0         0 $self->send(
300             ircmsg(
301             command => 'pong',
302 0         0 params => [ @{ $ircev->params } ],
303             )
304             );
305              
306 0         0 EAT_NONE
307             }
308              
309             sub N_irc_privmsg {
310 2     2 0 600 my (undef, $self) = splice @_, 0, 2;
311 2         4 my $ircev = ${ $_[0] };
  2         3  
312              
313 2 100       8 if (my $ctcp_ev = ctcp_extract($ircev)) {
314 1         1015 $self->emit_now( 'irc_'.$ctcp_ev->command => $ctcp_ev );
315 1         590 return EAT_ALL
316             }
317              
318 1 50 33     762 if ($ircev->has_tags && $ircev->get_tag('intent') eq 'ACTION') {
319 0         0 $self->emit_now( irc_ctcp_action => $ircev );
320 0         0 return EAT_ALL
321             }
322              
323 1         22 my $prefix = substr $ircev->params->[0], 0, 1;
324             ## FIXME
325             ## parse isupports as we get them, attempt to find chan prefixes
326             ## attrib defaulting to following:
327 1 50       9 if (grep {; $_ eq $prefix } ('#', '&', '+') ) {
  3         6  
328 1         6 $self->emit_now( irc_public_msg => $ircev )
329             } else {
330 0         0 $self->emit_now( irc_private_msg => $ircev )
331             }
332              
333 1         468 EAT_ALL
334             }
335              
336             sub N_irc_notice {
337 0     0 0 0 my (undef, $self) = splice @_, 0, 2;
338 0         0 my $ircev = ${ $_[0] };
  0         0  
339              
340 0 0       0 if (my $ctcp_ev = ctcp_extract($ircev)) {
341 0         0 $self->emit_now( 'irc_'.$ctcp_ev->command => $ctcp_ev );
342 0         0 return EAT_ALL
343             }
344              
345             EAT_NONE
346 0         0 }
347              
348              
349              
350             ### Public
351              
352             ## Since the retval of yield() is $self, many of these can be chained:
353             ## $client->connect->join(@channels)->privmsg(
354             ## join(',', @channels), 'hello!'
355             ## );
356              
357             sub connect {
358 1     1 1 372 my $self = shift;
359 1         8 $self->yield( connect => @_ )
360             }
361              
362             sub _connect {
363 1     1   783 my (undef, $self) = @_[KERNEL, OBJECT];
364            
365 1 50       37 $self->backend->create_connector(
    50          
366             remoteaddr => $self->server,
367             remoteport => $self->port,
368             ssl => $self->ssl,
369             (
370             $self->has_ipv6 ? (ipv6 => $self->ipv6) : ()
371             ),
372             (
373             $self->has_bindaddr ? (bindaddr => $self->bindaddr) : ()
374             ),
375             );
376             }
377              
378             sub disconnect {
379 1     1 1 996129 my $self = shift;
380 1         12 $self->yield( disconnect => @_ )
381             }
382              
383             sub _disconnect {
384 1     1   499 my ($kernel, $self) = @_[KERNEL, OBJECT];
385 1   50     7 my $message = $_[ARG0] // 'Leaving';
386              
387 1         41 $self->backend->send(
388             ircmsg(
389             command => 'quit',
390             params => [ $message ],
391             ),
392             $self->conn->wheel_id
393             );
394              
395 1         690 $kernel->alarm('connect');
396              
397 1 50 33     126 $self->backend->disconnect( $self->conn->wheel->ID )
398             if $self->_has_conn and $self->conn->has_wheel;
399             }
400              
401             sub send_raw_line {
402 0     0 1 0 my ($self, $line) = @_;
403 0 0       0 confess "Expected a raw line" unless defined $line;
404 0         0 $self->send( ircmsg(raw_line => $line) );
405             }
406              
407             sub send {
408 4     4 1 4640 my $self = shift;
409 4         17 $self->yield( send => @_ )
410             }
411              
412             sub _send {
413 4     4   2685 my (undef, $self) = @_[KERNEL, OBJECT];
414 4         13 for my $outev (@_[ARG0 .. $#_]) {
415 5 50       224 if ($self->process( outgoing => $outev ) == EAT_ALL) {
416             next
417 0         0 }
418 5         717 $self->backend->send( $outev, $self->conn->wheel_id )
419             }
420             }
421              
422             ## Sugar, and POE-dispatchable counterparts.
423             sub notice {
424 0     0 1 0 my $self = shift;
425 0         0 $self->yield( notice => @_ )
426             }
427              
428             sub _notice {
429 0     0   0 my (undef, $self) = @_[KERNEL, OBJECT];
430 0         0 my ($target, @data) = @_[ARG0 .. $#_];
431 0         0 $self->send(
432             ircmsg(
433             command => 'notice',
434             params => [ $target, join ' ', @data ]
435             )
436             )
437             }
438              
439             sub privmsg {
440 0     0 1 0 my $self = shift;
441 0         0 $self->yield( privmsg => @_ )
442             }
443              
444             sub _privmsg {
445 0     0   0 my (undef, $self) = @_[KERNEL, OBJECT];
446 0         0 my ($target, @data) = @_[ARG0 .. $#_];
447 0         0 $self->send(
448             ircmsg(
449             command => 'privmsg',
450             params => [ $target, join ' ', @data ]
451             )
452             )
453             }
454              
455             sub ctcp {
456 0     0 1 0 my $self = shift;
457 0         0 $self->yield( ctcp => @_ )
458             }
459              
460             sub _ctcp {
461 0     0   0 my (undef, $self) = @_[KERNEL, OBJECT];
462 0         0 my ($type, $target, @data) = @_[ARG0 .. $#_];
463 0         0 my $line = join ' ', uc($type), @data;
464 0         0 my $quoted = ctcp_quote($line);
465 0         0 $self->send(
466             ircmsg(
467             command => 'privmsg',
468             params => [ $target, $quoted ]
469             )
470             )
471             }
472              
473             sub mode {
474 2     2 1 1879 my $self = shift;
475 2         8 $self->yield( mode => @_ )
476             }
477              
478             sub _mode {
479 3     3   458 my (undef, $self) = @_[KERNEL, OBJECT];
480 3         5 my ($target, $mode) = @_[ARG0, ARG1];
481              
482 3 100 66     23 if (blessed $mode && $mode->isa('IRC::Mode::Set')) {
483             ## FIXME tests for same
484             ## FIXME accept an opt to allow passing in MODES= ?
485             ## don't really want to parse/store isupport here
486             ## (stateful subclasses should worry about it)
487 1         5 for my $set ($mode->split_mode_set(4)) {
488 1         1817 $self->call( mode => $target, $set->mode_string )
489             }
490 1         111 return $self
491             }
492              
493             $self->send(
494 2         10 ircmsg(
495             command => 'mode',
496             params => [ $target, $mode ],
497             )
498             )
499             }
500              
501             sub join {
502 0     0 1   my $self = shift;
503 0           $self->yield( join => @_ )
504             }
505              
506             sub _join {
507 0     0     my (undef, $self) = @_[KERNEL, OBJECT];
508 0           my $join_to = CORE::join ',', @_[ARG0 .. $#_];
509 0           $self->send(
510             ircmsg(
511             command => 'join',
512             params => [ $join_to ],
513             )
514             )
515             }
516              
517             sub part {
518 0     0 1   my $self = shift;
519 0           $self->yield( part => @_ )
520             }
521              
522             sub _part {
523 0     0     my (undef, $self) = @_[KERNEL, OBJECT];
524 0           my ($channel, $msg) = @_[ARG0, ARG1];
525 0           $self->send(
526             ircmsg(
527             command => 'part',
528             params => [ $channel, $msg ],
529             )
530             );
531             }
532              
533             1;
534              
535             =pod
536              
537             =head1 NAME
538              
539             POEx::IRC::Client::Lite - Minimalist POE IRC interface
540              
541             =head1 SYNOPSIS
542              
543             package MyClient;
544             use POE;
545             use POEx::IRC::Client::Lite;
546             use IRC::Toolkit;
547              
548             our @channels = ( '#otw', '#eris' );
549              
550             POE::Session->create(
551             package_states => [
552             MyClient => [ qw/
553             _start
554             recv_irc_001
555             recv_irc_public_msg
556             recv_irc_ctcp_version
557             / ],
558             ],
559             );
560              
561             sub _start {
562             my ($kern, $heap) = @_[KERNEL, HEAP];
563              
564             $heap->{irc} = POEx::IRC::Client::Lite->new(
565             event_prefix => 'recv_',
566             server => "irc.perl.org",
567             nick => "MyNick",
568             username => "myuser",
569             );
570              
571             $heap->{irc}->connect;
572             }
573              
574             sub recv_irc_001 {
575             my ($kern, $heap) = @_[KERNEL, HEAP];
576              
577             $heap->{irc}->join(@channels)->privmsg(
578             join(',', @channels), "hello!"
579             );
580             }
581              
582             sub recv_irc_public_msg {
583             my ($kern, $heap) = @_[KERNEL, HEAP];
584             my $event = $_[ARG0];
585              
586             my ($target, $string) = @{ $event->params };
587             my $from = parse_user( $event->prefix );
588              
589             if (lc($string||'') eq 'hello') {
590             $heap->{irc}->privmsg($target, "hello there, $from")
591             }
592             }
593              
594             sub recv_irc_ctcp_version {
595             my ($kern, $heap) = @_[KERNEL, HEAP];
596             my $event = $_[ARG0];
597              
598             my $from = parse_user( $event->prefix );
599              
600             $heap->{irc}->notice( $from =>
601             ctcp_quote("VERSION a silly Client::Lite example")
602             );
603             }
604              
605             =head1 DESCRIPTION
606              
607             A very thin (but pluggable / extensible) IRC client library using
608             L and L on top of
609             L and L.
610              
611             No state is maintained; POEx::IRC::Client::Lite provides a
612             minimalist interface to IRC and serves as a base class for stateful clients.
613              
614             This is early development software pulled out of a much larger in-progress
615             project.
616             B<< See L for a more mature POE IRC client library. >>
617              
618             =head2 new
619              
620             my $irc = POEx::IRC::Client::Lite->new(
621             event_prefix => $prefix,
622             server => $server,
623             nick => $nickname,
624             username => $username,
625             );
626              
627             Create a new Client::Lite instance. Optional arguments are:
628              
629             =over
630              
631             =item bindaddr
632              
633             Local address to bind to.
634              
635             =item ipv6
636              
637             Boolean value indicating whether to prefer IPv6.
638              
639             =item port
640              
641             Remote port to use (defaults to 6667).
642              
643             =item ssl
644              
645             Boolean value indicating whether to (attempt to) connect via SSL.
646              
647             Requires L.
648              
649             =item ssl_opts
650              
651             An C containing SSL options passed along to L
652             via L; see L & L.
653              
654             Not required for basic SSL operation; setting L to a true value should
655             work for most users.
656              
657             =item reconnect
658              
659             Reconnection attempt delay, in seconds.
660              
661             B<< Automatic reconnection is only triggered when an outgoing connector fails!
662             >>
663              
664             You can trigger a reconnection in your own code by handling
665             L events. For example:
666              
667             sub irc_disconnected {
668             # Immediate reconnect; you may want to use a timer (to avoid being banned)
669             # Assuming our IRC component's object lives in our session's HEAP:
670             $_[HEAP]->{irc}->connect
671             }
672              
673             =back
674              
675             =head2 stop
676              
677             $irc->stop;
678              
679             Disconnect, stop the Emitter, and purge the plugin pipeline.
680              
681             =head2 IRC Methods
682              
683             IRC-related methods can be called via normal method dispatch or sent as a POE
684             event:
685              
686             ## These are equivalent:
687             $irc->send( $ircevent );
688             $irc->yield( 'send', $ircevent );
689             $poe_kernel->post( $irc->session_id, 'send', $ircevent );
690              
691             Methods that dispatch to IRC return C<$self>, so they can be chained:
692              
693             $irc->connect->join(@channels)->privmsg(
694             join(',', @channels),
695             'hello there!'
696             );
697              
698             =head3 connect
699              
700             $irc->connect;
701              
702             Attempt an outgoing connection.
703              
704             =head3 disconnect
705              
706             $irc->disconnect($message);
707              
708             Quit IRC and shut down the wheel.
709              
710             =head3 send
711              
712             use IRC::Message::Object 'ircmsg';
713             $irc->send(
714             ircmsg(
715             command => 'oper',
716             params => [ $user, $passwd ],
717             )
718             );
719              
720             ## ... or a raw HASH:
721             $irc->send(
722             {
723             command => 'oper',
724             params => [ $user, $passwd ],
725             }
726             )
727              
728             ## ... or a raw line:
729             $irc->send_raw_line('PRIVMSG avenj :some things');
730              
731             Use C to send an L or a compatible
732             HASH; this method will also take a list of events in either of those formats.
733              
734             =head3 send_raw_line
735              
736             Use C to send a single raw IRC line. This is rarely a good
737             idea; L provides an IRCv3-capable filter.
738              
739             =head3 set_nick
740              
741             $irc->set_nick( $new_nick );
742              
743             Attempt to change the current nickname.
744              
745             =head3 privmsg
746              
747             $irc->privmsg( $target, $string );
748              
749             Sends a PRIVMSG to the specified target.
750              
751             =head3 notice
752              
753             $irc->notice( $target, $string );
754              
755             Sends a NOTICE to the specified target.
756              
757             =head3 ctcp
758              
759             $irc->ctcp( $target, $type, @params );
760              
761             Encodes and sends a CTCP B to the target.
762             (To send a CTCP B, send a L that has been quoted via
763             L.)
764              
765             =head3 mode
766              
767             $irc->mode( $channel, $modestring );
768              
769             Sends a MODE for the specified target.
770              
771             Takes a channel name as a string and a mode change as either a string or an
772             L.
773              
774             =head3 join
775              
776             $irc->join( $channel );
777              
778             Attempts to join the specified channel.
779              
780             =head3 part
781              
782             $irc->part( $channel, $message );
783              
784             Attempts to leave the specified channel with an optional PART message.
785              
786             =head2 Attributes
787              
788             =head3 conn
789              
790             The L instance for our connection.
791              
792             =head3 nick
793              
794             The nickname we were spawned with.
795              
796             This class doesn't track nick changes; if our nick is changed later, ->nick()
797             is not updated.
798              
799             =head3 server
800              
801             The server we were instructed to connect to.
802              
803             =head1 Emitted Events
804              
805             =head2 IRC events
806              
807             All IRC events are emitted as 'irc_$cmd' e.g. 'irc_005' (ISUPPORT) or
808             'irc_mode' with a few notable exceptions, detailed below.
809              
810             C<$_[ARG0]> is the L.
811              
812             =head2 Special events
813              
814             =head3 irc_connected
815              
816             Emitted when a connection has been successfully opened.
817              
818             This does not indicate successful server registration, only that the
819             connection has been opened and registration details have been sent.
820              
821             C<$_[ARG0]> is the L object.
822              
823             =head3 irc_connector_failed
824              
825             Emitted if an outgoing connection could not be established.
826              
827             C<< @_[ARG0 .. ARG3] >> are the operation, errno, and error string passed in
828             by L; see L.
829              
830             =head3 irc_connector_killed
831              
832             Emitted if a connection is terminated during L.
833              
834             C<$_[ARG0]> is the L object.
835              
836             =head3 irc_private_message
837              
838             Emitted for PRIVMSG-type messages not covered by L.
839              
840             =head3 irc_public_message
841              
842             Emitted for PRIVMSG-type messages that appear to be destined for a channel
843             target.
844              
845             =head3 irc_ctcp_TYPE
846              
847             Emitted for incoming CTCP requests. TYPE is the request type, such as
848             'version'
849              
850             C<$_[ARG0]> is the L produced by
851             L.
852              
853             An example of sending a CTCP reply lives in L.
854             See L for CTCP-related helpers.
855              
856             =head3 irc_ctcpreply_TYPE
857              
858             Emitted for incoming CTCP replies.
859              
860             Mirrors the behavior of L
861              
862             =head3 irc_disconnected
863              
864             Emitted when an IRC connection has been disconnected at the backend.
865              
866             C<$_[ARG0]> is the disconnect string from L.
867              
868             C<$_[ARG1]> is the L that was disconnected.
869              
870             =head1 Pluggable Events
871              
872             These are events explicitly dispatched to plugins
873             via L;
874             see L and L for more on
875             making use of plugins.
876              
877             =head2 preregister
878              
879             Dispatched to plugins when an outgoing connection has been established,
880             but prior to registration.
881              
882             The first argument is the L object.
883              
884             Returning EAT_ALL (see L) to Client::Lite
885             will terminate the connection without registering.
886              
887             =head2 outgoing
888              
889             Dispatched to plugins prior to sending output.
890              
891             The first argument is the item being sent. Note that no sanity checks are
892             performed on the item(s) at this stage (this is done after items are passed to
893             the L instance) -- your plugin's handler could receive a
894             HASH, an L, a raw line, or something invalid.
895              
896             Returning EAT_ALL will skip sending the item.
897              
898             =head1 SEE ALSO
899              
900             L, a fully-featured POE IRC client library
901              
902             L
903              
904             L
905              
906             L
907              
908             L
909              
910             L
911              
912             =head1 AUTHOR
913              
914             Jon Portnoy
915              
916             =begin Pod::Coverage
917              
918             BUILD
919             N_(?i:[A-Z0-9_])+
920             ircsock_(?i:[A-Z_])+
921              
922             =end Pod::Coverage
923              
924             =cut