ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-IRC3/lib/Net/IRC3/Client/Connection.pm
Revision: 1.17
Committed: Sat Feb 24 10:24:55 2007 UTC (19 years, 7 months ago) by elmex
Branch: MAIN
Changes since 1.16: +16 -10 lines
Log Message:
added further tests and improved modules

File Contents

# User Rev Content
1 elmex 1.1 package Net::IRC3::Client::Connection;
2 elmex 1.4 use base "Net::IRC3::Connection";
3     use Net::IRC3::Util qw/prefix_nick/;
4 elmex 1.12 use strict;
5 elmex 1.17 no warnings;
6 elmex 1.1
7 elmex 1.4 our $DEBUG;
8 elmex 1.1
9     =head1 NAME
10    
11     Net::IRC3::Client::Connection - A highlevel IRC connection
12    
13     =head1 SYNOPSIS
14    
15 elmex 1.5 use AnyEvent;
16     use Net::IRC3::Client::Connection;
17 elmex 1.1
18 elmex 1.5 my $c = AnyEvent->condvar;
19 elmex 1.1
20 elmex 1.5 my $timer;
21     my $con = new Net::IRC3::Client::Connection;
22 elmex 1.1
23 elmex 1.5 $con->reg_cb (registered => sub { print "I'm in!\n"; 0 });
24     $con->reg_cb (disconnect => sub { print "I'm out!\n"; 0 });
25     $con->reg_cb (
26     sent => sub {
27     if ($_[2] eq 'PRIVMSG') {
28     print "Sent message!\n";
29     $timer = AnyEvent->timer (after => 1, cb => sub { $c->broadcast });
30     }
31     1
32     }
33     );
34    
35     $con->send_srv (PRIVMSG => "Hello there i'm the cool Net::IRC3 test script!", 'elmex');
36    
37     $con->connect ("localhost", 6667);
38     $con->register (qw/testbot testbot testbot/);
39 elmex 1.1
40 elmex 1.5 $c->wait;
41     undef $timer;
42    
43     $con->disconnect;
44 elmex 1.1
45     =head1 DESCRIPTION
46    
47 elmex 1.5 L<Net::IRC3::Client::Connection> is a highlevel client connection,
48     that manages all the stuff that noone wants to implement again and again
49     when handling with IRC. For example it PONGs the server or keeps track
50     of the users on a channel.
51 elmex 1.1
52 elmex 1.13 =head2 A NOTE TO CASE MANAGEMENT
53    
54     The case insensitivity of channelnames and nicknames can lead to headaches
55     when dealing with IRC in an automated client which tracks channels and nicknames.
56    
57     I tried to preserve the case in all channel and nicknames
58     Net::IRC3::Client::Connection passes to his user. But in the internal
59     structures i'm using lower case for the channel names.
60    
61     The returned hash from C<channel_list> for example has the lower case of the
62     joined channels as keys.
63    
64     But i tried to preserve the case in all events that are emitted.
65     Please keep this in mind when handling the events.
66    
67     For example a user might joins #TeSt and parts #test later.
68    
69 elmex 1.2 =head1 EVENTS
70    
71     The following events are emitted by L<Net::IRC3::Client::Connection>.
72     Use C<reg_cb> as described in L<Net::IRC3::Connection> to register to such an
73     event.
74 elmex 1.1
75     =over 4
76    
77 elmex 1.3 =item B<registered>
78    
79     Emitted when the connection got successfully registered.
80    
81 elmex 1.2 =item B<channel_add $channel @nicks>
82    
83     Emitted when C<@nicks> are added to the channel C<$channel>,
84     this happens for example when someone JOINs a channel or when you
85     get a RPL_NAMREPLY (see RFC2812).
86    
87     =item B<channel_remove $channel @nicks>
88    
89     Emitted when C<@nicks> are removed from the channel C<$channel>,
90     happens for example when they PART, QUIT or get KICKed.
91    
92 elmex 1.13 =item B<channel_change $channel $old_nick $new_nick $is_myself>
93    
94     Emitted when a nickname on a channel changes. This is emitted when a NICK
95     change occurs from C<$old_nick> to C<$new_nick> give the application a chance
96     to quickly analyze what channels were affected. C<$is_myself> is true when
97     youself was the one who changed the nick.
98    
99 elmex 1.2 =item B<join $nick $channel $is_myself>
100    
101     Emitted when C<$nick> enters the channel C<$channel> by JOINing.
102     C<$is_myself> is true if youself are the one who JOINs.
103    
104     =item B<part $nick $channel $is_myself $msg>
105    
106     Emitted when C<$nick> PARTs the channel C<$channel>.
107     C<$is_myself> is true if youself are the one who PARTs.
108     C<$msg> is the PART message.
109 elmex 1.1
110 elmex 1.13 =item B<nick_change $old_nick $new_nick $is_myself>
111 elmex 1.12
112     Emitted when C<$old_nick> is renamed to C<$new_nick>.
113     C<$is_myself> is true when youself was the one who changed the nick.
114    
115 elmex 1.3 =item B<quit $nick $msg>
116    
117     Emitted when the nickname C<$nick> QUITs with the message C<$msg>.
118    
119     =item B<publicmsg $channel $ircmsg>
120    
121     Emitted for NOTICE and PRIVMSG where the target C<$channel> is a channel.
122     C<$ircmsg> is the original IRC message hash like it is returned by C<parse_irc_msg>.
123    
124     =item B<privatemsg $nick $ircmsg>
125    
126     Emitted for NOTICE and PRIVMSG where the target C<$nick> (most of the time you) is a nick.
127     C<$ircmsg> is the original IRC message hash like it is returned by C<parse_irc_msg>.
128    
129 elmex 1.13 =item B<error $code $message @params>
130    
131     Emitted when any error occurs. C<$code> is the 3 digit error id string from RFC
132     2812 and C<$message> is a description of the error. C<@params> is the complete
133     parameter list of the error command, including the C<$message> at the end of
134     C<@params>.
135    
136     You may use Net::IRC3::Util::rfc_code_to_name to convert C<$code> to the error
137     name from the RFC 2812. eg.:
138    
139     rfc_code_to_name ('471') => 'ERR_CHANNELISFULL'
140    
141 elmex 1.2 =back
142    
143     =head1 METHODS
144    
145     =over 4
146 elmex 1.1
147 elmex 1.16 =item B<new>
148    
149     This constructor takes no arguments.
150    
151 elmex 1.1 =cut
152    
153 elmex 1.2 sub new {
154     my $this = shift;
155     my $class = ref($this) || $this;
156     my $self = $class->SUPER::new (@_);
157 elmex 1.1 $self->reg_cb (irc_001 => \&welcome_cb);
158     $self->reg_cb (irc_join => \&join_cb);
159 elmex 1.12 $self->reg_cb (irc_nick => \&nick_cb);
160 elmex 1.1 $self->reg_cb (irc_part => \&part_cb);
161 elmex 1.2 $self->reg_cb (irc_kick => \&kick_cb);
162     $self->reg_cb (irc_quit => \&quit_cb);
163 elmex 1.1 $self->reg_cb (irc_353 => \&namereply_cb);
164     $self->reg_cb (irc_366 => \&endofnames_cb);
165     $self->reg_cb (irc_ping => \&ping_cb);
166    
167     $self->reg_cb (irc_privmsg => \&privmsg_cb);
168     $self->reg_cb (irc_notice => \&privmsg_cb);
169    
170     $self->reg_cb ('irc_*' => \&debug_cb)
171     if $DEBUG;
172     $self->reg_cb ('irc_*' => \&anymsg_cb);
173    
174 elmex 1.16 $self->reg_cb (channel_remove => \&channel_remove_event_cb);
175     $self->reg_cb (channel_add => \&channel_add_event_cb);
176     $self->reg_cb (disconnect => \&disconnect_cb);
177 elmex 1.2
178 elmex 1.16 $self->reg_cb (irc_437 => \&change_nick_login_cb);
179     $self->reg_cb (irc_433 => \&change_nick_login_cb);
180 elmex 1.13
181 elmex 1.17 $self->{def_nick_change} = $self->{nick_change} =
182     sub {
183     my ($old_nick) = @_;
184     "${old_nick}_"
185     };
186 elmex 1.13
187 elmex 1.2 return $self;
188     }
189    
190     =item B<register ($nick, $user, $real)>
191    
192     Sends the IRC registration commands NICK and USER.
193    
194     =cut
195    
196     sub register {
197     my ($self, $nick, $user, $real) = @_;
198    
199 elmex 1.13 $self->{nick} = $nick;
200     $self->{user} = $user;
201     $self->{real} = $real;
202    
203 elmex 1.1 $self->send_msg (undef, "NICK", undef, $nick);
204     $self->send_msg (undef, "USER", $real || $nick, $user || $nick, "*", "0");
205 elmex 1.13 }
206    
207     =item B<set_nick_change_cb $callback>
208    
209     This method lets you modify the nickname renaming mechanism when registering
210     the connection. C<$callback> is called with the current nickname as first
211     argument when a ERR_NICKNAMEINUSE or ERR_UNAVAILRESOURCE error occurs on login.
212     The returnvalue of C<$callback> will then be used to change the nickname.
213    
214 elmex 1.17 If C<$callback> is not defined the default nick change callback will be used
215     again.
216    
217 elmex 1.13 The default callback appends '_' to the end of the nickname supplied in the
218     C<register> routine.
219    
220     If the callback returns the same nickname that was given it the connection
221     will be terminated.
222 elmex 1.1
223 elmex 1.13 =cut
224    
225     sub set_nick_change_cb {
226     my ($self, $cb) = @_;
227 elmex 1.17 $cb = $self->{def_nick_change} unless defined $cb;
228 elmex 1.13 $self->{nick_change} = $cb;
229 elmex 1.1 }
230    
231     =item B<nick ()>
232    
233     Returns the current nickname, under which this connection
234     is registered at the IRC server. It might be different from the
235     one that was passed to C<register> as a nick-collision might happened
236     on login.
237    
238     =cut
239    
240     sub nick { $_[0]->{nick} }
241    
242 elmex 1.17 =item B<registered ()>
243 elmex 1.13
244     Returns a true value when the connection has been registered successfull and
245     you can send commands.
246    
247     =cut
248    
249 elmex 1.17 sub registered { $_[0]->{registered} }
250 elmex 1.13
251 elmex 1.1 =item B<channel_list ()>
252    
253 elmex 1.13 This returns a hash reference. The keys are the currently joined channels in lower case.
254     The values are hash references which contain the joined nicks as key.
255    
256     NOTE: Future versions might preserve the case from the JOIN command to the channels.
257 elmex 1.1
258     =cut
259    
260     sub channel_list {
261     my ($self) = @_;
262 elmex 1.12 return $self->{channel_list} || {};
263 elmex 1.1 }
264    
265 elmex 1.5 =item B<send_srv ($command, $trailing, @params)>
266 elmex 1.1
267 elmex 1.5 This function sends an IRC message that is constructed by C<mk_msg (undef, $command, $trailing, @params)> (see L<Net::IRC3::Util>).
268 elmex 1.1 If the connection isn't yet registered (for example if the connection is slow) and hasn't got a
269     welcome (IRC command 001) from the server yet, the IRC message is queued until it gets a welcome.
270    
271     =cut
272    
273     sub send_srv {
274     my ($self, @msg) = @_;
275    
276 elmex 1.17 if ($self->registered) {
277 elmex 1.1 $self->send_msg (undef, @msg);
278    
279     } else {
280     push @{$self->{con_queue}}, \@msg;
281     }
282     }
283    
284     =item B<clear_srv_queue>
285    
286     Clears the server send queue.
287    
288     =cut
289    
290     sub clear_srv_queue {
291     my ($self) = @_;
292     $self->{con_queue} = [];
293     }
294    
295    
296 elmex 1.5 =item B<send_chan ($channel, $command, $trailing, @params))>
297    
298 elmex 1.13 This function sends a message (constructed by C<mk_msg (undef, $command,
299     $trailing, @params)> to the server, like C<send_srv> only that it will queue
300     the messages if it hasn't joined the channel C<$channel> yet. The queued
301     messages will be send once the connection successfully JOINed the C<$channel>.
302 elmex 1.1
303 elmex 1.12 C<$channel> will be lowercased so that any case that comes from the server matches.
304     (Yes, IRC handles upper and lower case as equal :-(
305    
306     Be careful with this, there are chances you might not join the channel you
307     wanted to join. You may wanted to join #bla and the server redirects that
308     and sends you that you joined #blubb. You may use C<clear_chan_queue> to
309     remove the queue after some timeout after joining, so that you don't end up
310     with a memory leak.
311    
312 elmex 1.1 =cut
313    
314     sub send_chan {
315     my ($self, $chan, @msg) = @_;
316    
317 elmex 1.14 if ($self->{channel_list}->{lc $chan}) {
318 elmex 1.1 $self->send_msg (undef, @msg);
319    
320     } else {
321     push @{$self->{chan_queue}->{lc $chan}}, \@msg;
322     }
323     }
324    
325     =item B<clear_chan_queue ($channel)>
326    
327     Clears the channel queue of the channel C<$channel>.
328    
329     =cut
330    
331     sub clear_chan_queue {
332     my ($self, $chan) = @_;
333     $self->{chan_queue}->{lc $chan} = [];
334     }
335    
336     ################################################################################
337 elmex 1.2 # Private utility functions
338     ################################################################################
339    
340     sub _was_me {
341     my ($self, $msg) = @_;
342 elmex 1.4 lc prefix_nick ($msg) eq lc $self->nick ()
343 elmex 1.2 }
344    
345     ################################################################################
346 elmex 1.1 # Callbacks
347     ################################################################################
348    
349 elmex 1.16 sub channel_remove_event_cb {
350 elmex 1.2 my ($self, $chan, @nicks) = @_;
351    
352     for my $nick (@nicks) {
353 elmex 1.12 if (lc ($nick) eq lc ($self->nick ())) {
354     delete $self->{chan_queue}->{lc $chan};
355 elmex 1.13 delete $self->{channel_list}->{lc $chan};
356 elmex 1.2 last;
357     } else {
358 elmex 1.13 delete $self->{channel_list}->{lc $chan}->{$nick};
359 elmex 1.2 }
360     }
361    
362     1;
363     }
364    
365 elmex 1.16 sub channel_add_event_cb {
366 elmex 1.2 my ($self, $chan, @nicks) = @_;
367    
368     for my $nick (@nicks) {
369 elmex 1.12 if (lc ($nick) eq lc ($self->nick ())) {
370     for (@{$self->{chan_queue}->{lc $chan}}) {
371 elmex 1.2 $self->send_msg (undef, @$_);
372     }
373 elmex 1.11 $self->clear_chan_queue ($chan);
374 elmex 1.2 }
375    
376 elmex 1.13 $self->{channel_list}->{lc $chan}->{$nick} = 1;
377 elmex 1.2 }
378    
379     1;
380     }
381    
382 elmex 1.1 sub anymsg_cb {
383     my ($self, $msg) = @_;
384    
385     my $cmd = lc $msg->{command};
386    
387 elmex 1.13 if ( $cmd ne "privmsg"
388 elmex 1.1 and $cmd ne "notice"
389     and $cmd ne "part"
390     and $cmd ne "join"
391 elmex 1.13 and not ($cmd >= 400 and $cmd <= 599)
392     )
393 elmex 1.1 {
394     $self->event (statmsg => $msg);
395 elmex 1.13 } elsif ($cmd >= 400 and $cmd <= 599) {
396     $self->event (error => $msg->{command}, $msg->{trailing}, @{$msg->{params}});
397 elmex 1.1 }
398    
399     1;
400     }
401    
402     sub privmsg_cb {
403     my ($self, $msg) = @_;
404    
405 elmex 1.3 my $targ = $msg->{params}->[0];
406     if ($targ =~ m/^(?:[#+&]|![A-Z0-9]{5})/) {
407     $self->event (publicmsg => $targ, $msg);
408 elmex 1.1
409     } else {
410 elmex 1.3 $self->event (privatemsg => $targ, $msg);
411 elmex 1.1 }
412    
413     1;
414     }
415    
416     sub welcome_cb {
417     my ($self, $msg) = @_;
418    
419 elmex 1.17 $self->{registered} = 1;
420 elmex 1.1
421     for (@{$self->{con_queue}}) {
422     $self->send_msg (undef, @$_);
423     }
424 elmex 1.11 $self->clear_srv_queue ();
425 elmex 1.1
426 elmex 1.12 $self->event ('registered');
427    
428 elmex 1.1 1;
429     }
430    
431     sub ping_cb {
432     my ($self, $msg) = @_;
433 elmex 1.8 $self->send_msg (undef, "PONG", $msg->{params}->[0]);
434 elmex 1.2
435     1;
436 elmex 1.1 }
437    
438 elmex 1.12 sub nick_cb {
439     my ($self, $msg) = @_;
440     my $nick = prefix_nick ($msg);
441     my $newnick = $msg->{params}->[0];
442     my $wasme = $self->_was_me ($msg);
443    
444 elmex 1.13 if ($wasme) { $self->{nick} = $newnick }
445 elmex 1.12
446 elmex 1.13 my @chans;
447 elmex 1.12
448 elmex 1.13 for my $channame (keys %{$self->{channel_list}}) {
449     my $chan = $self->{channel_list}->{$channame};
450 elmex 1.12 if (exists $chan->{$nick}) {
451     delete $chan->{$nick};
452     $chan->{$newnick} = 1;
453 elmex 1.13 push @chans, $channame;
454 elmex 1.12 }
455     }
456    
457 elmex 1.13 for (@chans) {
458     $self->event (channel_change => $_, $nick, $newnick, $wasme);
459     }
460 elmex 1.12 $self->event (nick_change => $nick, $newnick, $wasme);
461     }
462    
463 elmex 1.1 sub namereply_cb {
464     my ($self, $msg) = @_;
465 elmex 1.2 my @nicks = split / /, $msg->{trailing};
466     push @{$self->{_tmp_namereply}}, @nicks;
467 elmex 1.10
468     1;
469 elmex 1.1 }
470    
471     sub endofnames_cb {
472     my ($self, $msg) = @_;
473 elmex 1.12 my $chan = $msg->{params}->[1];
474 elmex 1.4 $self->event (channel_add => $chan, map { s/^[@\+]//; $_ } @{delete $self->{_tmp_namereply}});
475 elmex 1.10
476     1;
477 elmex 1.1 }
478    
479     sub join_cb {
480     my ($self, $msg) = @_;
481 elmex 1.2 my $chan = $msg->{params}->[0];
482 elmex 1.4 my $nick = prefix_nick ($msg);
483 elmex 1.1
484 elmex 1.12 $self->event (channel_add => $chan, $nick);
485 elmex 1.2 $self->event (join => $nick, $chan, $self->_was_me ($msg));
486 elmex 1.1
487 elmex 1.2 1;
488     }
489    
490     sub part_cb {
491     my ($self, $msg) = @_;
492     my $chan = $msg->{params}->[0];
493 elmex 1.4 my $nick = prefix_nick ($msg);
494 elmex 1.1
495 elmex 1.2 $self->event (part => $nick, $chan, $self->_was_me ($msg), $msg->{params}->[1]);
496     $self->event (channel_remove => $chan, $nick);
497 elmex 1.1
498 elmex 1.2 1;
499     }
500 elmex 1.1
501 elmex 1.2 sub kick_cb {
502     my ($self, $msg) = @_;
503     my $chan = $msg->{params}->[0];
504     my $kicked_nick = $msg->{params}->[1];
505 elmex 1.1
506 elmex 1.2 $self->event (channel_remove => $chan, $kicked_nick);
507 elmex 1.1
508     1;
509     }
510    
511 elmex 1.2 sub quit_cb {
512 elmex 1.1 my ($self, $msg) = @_;
513 elmex 1.4 my $nick = prefix_nick ($msg);
514 elmex 1.1
515 elmex 1.2 $self->event (quit => $nick, $msg->{params}->[1]);
516 elmex 1.1
517 elmex 1.12 for (keys %{$self->{channel_list}}) {
518 elmex 1.14 $self->event (channel_remove => $_, $nick)
519     if $self->{channel_list}->{$_}->{$nick};
520 elmex 1.1 }
521 elmex 1.10
522     1;
523 elmex 1.1 }
524    
525     sub debug_cb {
526     my ($self, $msg) = @_;
527     print "$self->{h}:$self->{p} > ";
528     my $par = delete $msg->{params};
529     print (join " ", map { $_ => $msg->{$_} } sort keys %$msg);
530     print " params:";
531     print (join ",", @$par);
532     print "\n";
533 elmex 1.10
534     1;
535 elmex 1.1 }
536    
537 elmex 1.16 sub change_nick_login_cb {
538 elmex 1.13 my ($self, $msg) = @_;
539    
540 elmex 1.17 unless ($self->registered) {
541 elmex 1.13 my $newnick = $self->{nick_change}->($self->nick);
542    
543     if (lc $newnick eq lc $self->{nick}) {
544     $self->disconnect;
545     return 0;
546     }
547    
548     $self->{nick} = $newnick;
549     $self->send_msg (undef, "NICK", undef, $newnick);
550     }
551    
552 elmex 1.17 not ($self->registered) # kill the cb when registered
553 elmex 1.13 }
554    
555 elmex 1.15 sub disconnect_cb {
556     my ($self) = @_;
557    
558     for (keys %{$self->{channel_list}}) {
559     $self->event (channel_remove => $_, $self->nick)
560     }
561    
562     1
563     }
564    
565 elmex 1.1 =back
566    
567     =head1 EXAMPLES
568    
569     See samples/netirc3cl and other samples in samples/ for some examples on how to use Net::IRC3::Client::Connection.
570    
571     =head1 AUTHOR
572    
573     Robin Redeker, C<< <elmex@ta-sa.org> >>
574    
575     =head1 SEE ALSO
576    
577 elmex 1.7 L<Net::IRC3::Connection>
578 elmex 1.1
579     RFC 2812 - Internet Relay Chat: Client Protocol
580    
581     =head1 COPYRIGHT & LICENSE
582    
583 elmex 1.6 Copyright 2006 Robin Redeker, all rights reserved.
584 elmex 1.1
585     This program is free software; you can redistribute it and/or modify it
586     under the same terms as Perl itself.
587    
588     =cut
589    
590     1;