File Coverage

blib/lib/Net/Async/IRC.pm
Criterion Covered Total %
statement 112 131 85.5
branch 20 40 50.0
condition 16 31 51.6
subroutine 22 27 81.4
pod 9 12 75.0
total 179 241 74.2


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2008-2017 -- leonerd@leonerd.org.uk
5              
6             package Net::Async::IRC;
7              
8 7     7   698678 use strict;
  7         15  
  7         222  
9 7     7   34 use warnings;
  7         11  
  7         355  
10              
11             our $VERSION = '0.11';
12              
13             # We need to use C3 MRO to make the ->isupport etc.. methods work properly
14 7     7   3749 use mro 'c3';
  7         5141  
  7         28  
15 7     7   288 use base qw( Net::Async::IRC::Protocol Protocol::IRC::Client );
  7         11  
  7         3404  
16              
17 7     7   18362 use Carp;
  7         13  
  7         422  
18              
19 7     7   33 use Socket qw( SOCK_STREAM );
  7         11  
  7         414  
20              
21 7     7   34 use constant HAVE_MSWIN32 => ( $^O eq "MSWin32" );
  7         7  
  7         9892  
22              
23             =head1 NAME
24              
25             C - use IRC with C
26              
27             =head1 SYNOPSIS
28              
29             use IO::Async::Loop;
30             use Net::Async::IRC;
31              
32             my $loop = IO::Async::Loop->new;
33              
34             my $irc = Net::Async::IRC->new(
35             on_message_text => sub {
36             my ( $self, $message, $hints ) = @_;
37              
38             print "$hints->{prefix_name} says: $hints->{text}\n";
39             },
40             );
41              
42             $loop->add( $irc );
43              
44             $irc->login(
45             nick => "MyName",
46             host => "irc.example.org",
47             )->get;
48              
49             $irc->do_PRIVMSG( target => "YourName", text => "Hello world!" );
50              
51             $loop->run;
52              
53             =head1 DESCRIPTION
54              
55             This object class implements an asynchronous IRC client, for use in programs
56             based on L.
57              
58             Most of the actual IRC message handling behaviour is implemented by the parent
59             class L.
60              
61             Most of the behaviour related to being an IRC client is implemented by the
62             parent class L.
63              
64             The following documentation may make mention of these above two parent
65             classes; the reader should make reference to them when required.
66              
67             =cut
68              
69             sub new
70             {
71 7     7 1 36424 my $class = shift;
72 7         43 my %args = @_;
73              
74 7         19 my $on_closed = delete $args{on_closed};
75              
76             return $class->SUPER::new(
77             %args,
78              
79             on_closed => sub {
80 0     0   0 my $self = shift;
81              
82 0 0       0 if( $self->{on_login_f} ) {
83 0         0 $_->fail( "Closed" ) for @{ $self->{on_login_f} };
  0         0  
84 0         0 undef $self->{on_login_f};
85             }
86              
87 0 0       0 $on_closed->( $self ) if $on_closed;
88             },
89 7         115 );
90             }
91              
92             sub _init
93             {
94 7     7   173 my $self = shift;
95 7         51 $self->SUPER::_init( @_ );
96              
97             $self->{user} = $ENV{LOGNAME} ||
98 7   33     3261 ( HAVE_MSWIN32 ? Win32::LoginName() : getpwuid($>) );
99              
100 7         47 $self->{realname} = "Net::Async::IRC client $VERSION";
101             }
102              
103             =head1 PARAMETERS
104              
105             The following named parameters may be passed to C or C:
106              
107             =over 8
108              
109             =item nick => STRING
110              
111             =item user => STRING
112              
113             =item realname => STRING
114              
115             Connection details. See also C, C.
116              
117             If C is not supplied, it will default to either C<$ENV{LOGNAME}> or the
118             current user's name as supplied by C or C.
119              
120             If unconnected, changing these properties will set the default values to use
121             when logging in.
122              
123             If logged in, changing the C property is equivalent to calling
124             C. Changing the other properties will not take effect until the
125             next login.
126              
127             =item use_caps => ARRAY of STRING
128              
129             Attempts to negotiate IRC v3.1 CAP at connect time. The array gives the names
130             of capabilities which will be requested, if the server supports them.
131              
132             =back
133              
134             =cut
135              
136             sub configure
137             {
138 9     9 1 9699 my $self = shift;
139 9         36 my %args = @_;
140              
141 9         24 for (qw( user realname use_caps )) {
142 27 100       99 $self->{$_} = delete $args{$_} if exists $args{$_};
143             }
144              
145 9 100       53 if( exists $args{nick} ) {
146 1         9 $self->_set_nick( delete $args{nick} );
147             }
148              
149 9         101 $self->SUPER::configure( %args );
150             }
151              
152             =head1 METHODS
153              
154             The following methods documented with a trailing call to C<< ->get >> return
155             L instances.
156              
157             =cut
158              
159             =head2 connect
160              
161             $irc = $irc->connect( %args )->get
162              
163             Connects to the IRC server. This method does not perform the complete IRC
164             login sequence; for that see instead the C method. The returned
165             L will yield the C<$irc> instance itself, to make chaining easier.
166              
167             =over 8
168              
169             =item host => STRING
170              
171             Hostname of the IRC server.
172              
173             =item service => STRING or NUMBER
174              
175             Optional. Port number or service name of the IRC server. Defaults to 6667.
176              
177             =back
178              
179             Any other arguments are passed into the underlying C
180             C method.
181              
182             $irc->connect( %args )
183              
184             The following additional arguments are used to provide continuations when not
185             returning a Future.
186              
187             =over 8
188              
189             =item on_connected => CODE
190              
191             Continuation to invoke once the connection has been established. Usually used
192             by the C method to perform the actual login sequence.
193              
194             $on_connected->( $irc )
195              
196             =item on_error => CODE
197              
198             Continuation to invoke in the case of an error preventing the connection from
199             taking place.
200              
201             $on_error->( $errormsg )
202              
203             =back
204              
205             =cut
206              
207             # TODO: Most of this needs to be moved into an abstract Net::Async::Connection role
208             sub connect
209             {
210 6     6 1 512 my $self = shift;
211 6         12 my %args = @_;
212              
213             # Largely for unit testing
214 6 100 66     16 return $self->{connect_f} ||= Future->new->done( $self ) if
215             $self->read_handle;
216              
217 2         21 my $on_error = delete $args{on_error};
218              
219 2   50     13 $args{service} ||= "6667";
220              
221             return $self->{connect_f} ||= $self->SUPER::connect(
222             %args,
223              
224             on_resolve_error => sub {
225 0     0   0 my ( $msg ) = @_;
226 0         0 chomp $msg;
227              
228 0 0       0 if( $args{on_resolve_error} ) {
    0          
229 0         0 $args{on_resolve_error}->( $msg );
230             }
231             elsif( $on_error ) {
232 0         0 $on_error->( "Cannot resolve - $msg" );
233             }
234             },
235              
236             on_connect_error => sub {
237 0 0   0   0 if( $args{on_connect_error} ) {
    0          
238 0         0 $args{on_connect_error}->( @_ );
239             }
240             elsif( $on_error ) {
241 0         0 $on_error->( "Cannot connect" );
242             }
243             },
244 2   33 0   53 )->on_fail( sub { undef $self->{connect_f} } );
  0         0  
245             }
246              
247             =head2 login
248              
249             $irc = $irc->login( %args )->get
250              
251             Logs in to the IRC network, connecting first using the C method if
252             required. Takes the following named arguments:
253              
254             =over 8
255              
256             =item nick => STRING
257              
258             =item user => STRING
259              
260             =item realname => STRING
261              
262             IRC connection details. Defaults can be set with the C or C
263             methods.
264              
265             =item pass => STRING
266              
267             Server password to connect with.
268              
269             =back
270              
271             Any other arguments that are passed, are forwarded to the C method if
272             it is required; i.e. if C is invoked when not yet connected to the
273             server.
274              
275             $irc->login( %args )
276              
277             The following additional arguments are used to provide continuations when not
278             returning a Future.
279              
280             =over 8
281              
282             =item on_login => CODE
283              
284             A continuation to invoke once login is successful.
285              
286             $on_login->( $irc )
287              
288             =back
289              
290             =cut
291              
292             sub login
293             {
294 4     4 1 1980 my $self = shift;
295 4         13 my %args = @_;
296              
297 4 50 66     25 my $nick = delete $args{nick} || $self->{nick} or croak "Need a login nick";
298 4 50 66     21 my $user = delete $args{user} || $self->{user} or croak "Need a login user";
299 4   66     18 my $realname = delete $args{realname} || $self->{realname};
300 4         6 my $pass = delete $args{pass};
301              
302 4 100       16 if( !defined $self->{nick} ) {
303 3         18 $self->_set_nick( $nick );
304             }
305              
306 4         67 my $on_login = delete $args{on_login};
307 4 50 66     25 !defined $on_login or ref $on_login eq "CODE" or
308             croak "Expected 'on_login' to be a CODE reference";
309              
310             return $self->{login_f} ||= $self->connect( %args )->then( sub {
311 4 100   4   444 $self->send_message( "CAP", undef, "LS" ) if $self->{use_caps};
312              
313 4 50       517 $self->send_message( "PASS", undef, $pass ) if defined $pass;
314 4         26 $self->send_message( "USER", undef, $user, "0", "*", $realname );
315 4         521 $self->send_message( "NICK", undef, $nick );
316              
317 4         244 my $f = $self->loop->new_future;
318              
319 4         1737 push @{ $self->{on_login_f} }, $f;
  4         22  
320 4 100       16 $f->on_done( $on_login ) if $on_login;
321              
322 4         24 return $f;
323 4   33 0   25 })->on_fail( sub { undef $self->{login_f} } );
  0         0  
324             }
325              
326             =head2 change_nick
327              
328             $irc->change_nick( $newnick )
329              
330             Requests to change the nick. If unconnected, the change happens immediately
331             to the stored defaults. If logged in, sends a C command to the server,
332             which may suceed or fail at a later point.
333              
334             =cut
335              
336             sub change_nick
337             {
338 3     3 1 23 my $self = shift;
339 3         3 my ( $newnick ) = @_;
340              
341 3 50       8 if( !$self->is_connected ) {
342 0         0 $self->_set_nick( $newnick );
343             }
344             else {
345 3         35 $self->send_message( "NICK", undef, $newnick );
346             }
347             }
348              
349             ############################
350             # Message handling methods #
351             ############################
352              
353             =head1 IRC v3.1 CAPABILITIES
354              
355             The following methods relate to IRC v3.1 capabilities negotiations.
356              
357             =cut
358              
359             sub on_message_cap_LS
360             {
361 1     1 0 10 my $self = shift;
362 1         2 my ( $message, $hints ) = @_;
363              
364 1         3 my $supported = $self->{caps_supported} = $hints->{caps};
365              
366 1         2 my @request = grep { $supported->{$_} } @{$self->{use_caps}};
  1         2  
  1         3  
367              
368 1 50       3 if( @request ) {
369 1         1 $self->{caps_enabled} = { map { $_ => undef } @request };
  1         3  
370 1         4 $self->send_message( "CAP", undef, "REQ", join( " ", @request ) );
371             }
372             else {
373 0         0 $self->send_message( "CAP", undef, "END" );
374             }
375              
376 1         130 return 1;
377             }
378              
379             *on_message_cap_ACK = *on_message_cap_NAK = \&_on_message_cap_reply;
380             sub _on_message_cap_reply
381             {
382 1     1   13 my $self = shift;
383 1         3 my ( $message, $hints ) = @_;
384 1         3 my $ack = $hints->{verb} eq "ACK";
385              
386 1         2 $self->{caps_enabled}{$_} = $ack for keys %{ $hints->{caps} };
  1         8  
387              
388             # Are any outstanding
389 1   50     2 !defined and return 1 for values %{ $self->{caps_enabled} };
  1         7  
390              
391 1         5 $self->send_message( "CAP", undef, "END" );
392 1         182 return 1;
393             }
394              
395             =head2 caps_supported
396              
397             $caps = $irc->caps_supported
398              
399             Returns a HASH whose keys give the capabilities listed by the server as
400             supported in its C response. If the server ignored the C
401             negotiation then this method returns C.
402              
403             =cut
404              
405             sub caps_supported
406             {
407 2     2 1 796 my $self = shift;
408 2         11 return $self->{caps_supported};
409             }
410              
411             =head2 cap_supported
412              
413             $supported = $irc->cap_supported( $cap )
414              
415             Returns a boolean indicating if the server supports the named capability.
416              
417             =cut
418              
419             sub cap_supported
420             {
421 1     1 1 2 my $self = shift;
422 1         4 my ( $cap ) = @_;
423 1         7 return !!$self->{caps_supported}{$cap};
424             }
425              
426             =head2 caps_enabled
427              
428             $caps = $irc->caps_enabled
429              
430             Returns a HASH whose keys give the capabilities successfully enabled by the
431             server as part of the C login sequence. If the server ignored the
432             C negotiation then this method returns C.
433              
434             =cut
435              
436             sub caps_enabled
437             {
438 2     2 1 2 my $self = shift;
439 2         9 return $self->{caps_enabled};
440             }
441              
442             =head2 cap_enabled
443              
444             $enabled = $irc->cap_enabled( $cap )
445              
446             Returns a boolean indicating if the client successfully enabled the named
447             capability.
448              
449             =cut
450              
451             sub cap_enabled
452             {
453 1     1 1 2 my $self = shift;
454 1         3 my ( $cap ) = @_;
455 1         4 return !!$self->{caps_enabled}{$cap};
456             }
457              
458             sub on_message_NICK
459             {
460 1     1 0 11 my $self = shift;
461 1         1 my ( $message, $hints ) = @_;
462              
463 1 50       5 if( $hints->{prefix_is_me} ) {
464 1         2 $self->_set_nick( $hints->{new_nick} );
465 1         18 return 1;
466             }
467              
468 0         0 return 0;
469             }
470              
471             sub on_message_RPL_WELCOME
472             {
473 4     4 0 41 my $self = shift;
474 4         7 my ( $message ) = @_;
475              
476             # set our nick to be what the server logged us in as
477 4         17 $self->_set_nick( $message->{args}[0] );
478              
479 4 50 33     56 if( $self->{on_login_f} and @{ $self->{on_login_f} } ) {
  4         18  
480 4         6 my @futures = @{ $self->{on_login_f} };
  4         10  
481 4         12 undef $self->{on_login_f};
482              
483 4         9 foreach my $f ( @futures ) {
484 4         19 $f->done( $self );
485             }
486             }
487              
488             # Don't eat it
489 4         177 return 0;
490             }
491              
492             =head1 MESSAGE-WRAPPING METHODS
493              
494             The following methods are all inherited from L but are
495             mentioned again for convenient. For further details see the documentation in
496             the parent module.
497              
498             In particular, each method returns a L instance.
499              
500             =cut
501              
502             =head2 do_PRIVMSG
503              
504             =head2 do_NOTICE
505              
506             $irc->do_PRIVMSG( target => $target, text => $text )->get
507              
508             $irc->do_NOTICE( target => $target, text => $text )->get
509              
510             Sends a C or C command.
511              
512             =cut
513              
514             =head1 SEE ALSO
515              
516             =over 4
517              
518             =item *
519              
520             L - Internet Relay Chat: Client Protocol
521              
522             =back
523              
524             =head1 AUTHOR
525              
526             Paul Evans
527              
528             =cut
529              
530             0x55AA;