ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-IRC3/lib/Net/IRC3/Client/Connection.pm
Revision: 1.23
Committed: Tue Mar 6 21:44:53 2007 UTC (19 years, 7 months ago) by elmex
Branch: MAIN
Changes since 1.22: +34 -0 lines
Log Message:
Implemented topic handling and fixed a bug in nick-change detection
and fixed a bug in prefix matching.

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.23 =item B<channel_topic $channel $topic $who>
100    
101     This is emitted when the topic for a channel is discovered. C<$channel>
102     is the channel for which C<$topic> is the current topic now.
103     Which is set by C<$who>. C<$who> might be undefined when it's not known
104     who set the channel topic.
105    
106 elmex 1.2 =item B<join $nick $channel $is_myself>
107    
108     Emitted when C<$nick> enters the channel C<$channel> by JOINing.
109     C<$is_myself> is true if youself are the one who JOINs.
110    
111     =item B<part $nick $channel $is_myself $msg>
112    
113     Emitted when C<$nick> PARTs the channel C<$channel>.
114     C<$is_myself> is true if youself are the one who PARTs.
115     C<$msg> is the PART message.
116 elmex 1.1
117 elmex 1.13 =item B<nick_change $old_nick $new_nick $is_myself>
118 elmex 1.12
119     Emitted when C<$old_nick> is renamed to C<$new_nick>.
120     C<$is_myself> is true when youself was the one who changed the nick.
121    
122 elmex 1.3 =item B<quit $nick $msg>
123    
124     Emitted when the nickname C<$nick> QUITs with the message C<$msg>.
125    
126     =item B<publicmsg $channel $ircmsg>
127    
128     Emitted for NOTICE and PRIVMSG where the target C<$channel> is a channel.
129     C<$ircmsg> is the original IRC message hash like it is returned by C<parse_irc_msg>.
130    
131     =item B<privatemsg $nick $ircmsg>
132    
133     Emitted for NOTICE and PRIVMSG where the target C<$nick> (most of the time you) is a nick.
134     C<$ircmsg> is the original IRC message hash like it is returned by C<parse_irc_msg>.
135    
136 elmex 1.21 =item B<error $code $message $ircmsg>
137 elmex 1.13
138     Emitted when any error occurs. C<$code> is the 3 digit error id string from RFC
139 elmex 1.21 2812 and C<$message> is a description of the error. C<$ircmsg> is the complete
140     error irc message.
141 elmex 1.13
142     You may use Net::IRC3::Util::rfc_code_to_name to convert C<$code> to the error
143     name from the RFC 2812. eg.:
144    
145     rfc_code_to_name ('471') => 'ERR_CHANNELISFULL'
146    
147 elmex 1.2 =back
148    
149     =head1 METHODS
150    
151     =over 4
152 elmex 1.1
153 elmex 1.16 =item B<new>
154    
155     This constructor takes no arguments.
156    
157 elmex 1.1 =cut
158    
159 elmex 1.2 sub new {
160     my $this = shift;
161     my $class = ref($this) || $this;
162     my $self = $class->SUPER::new (@_);
163 elmex 1.1 $self->reg_cb (irc_001 => \&welcome_cb);
164     $self->reg_cb (irc_join => \&join_cb);
165 elmex 1.12 $self->reg_cb (irc_nick => \&nick_cb);
166 elmex 1.1 $self->reg_cb (irc_part => \&part_cb);
167 elmex 1.2 $self->reg_cb (irc_kick => \&kick_cb);
168     $self->reg_cb (irc_quit => \&quit_cb);
169 elmex 1.1 $self->reg_cb (irc_353 => \&namereply_cb);
170     $self->reg_cb (irc_366 => \&endofnames_cb);
171     $self->reg_cb (irc_ping => \&ping_cb);
172    
173     $self->reg_cb (irc_privmsg => \&privmsg_cb);
174     $self->reg_cb (irc_notice => \&privmsg_cb);
175    
176     $self->reg_cb ('irc_*' => \&debug_cb)
177     if $DEBUG;
178     $self->reg_cb ('irc_*' => \&anymsg_cb);
179    
180 elmex 1.16 $self->reg_cb (channel_remove => \&channel_remove_event_cb);
181     $self->reg_cb (channel_add => \&channel_add_event_cb);
182     $self->reg_cb (disconnect => \&disconnect_cb);
183 elmex 1.2
184 elmex 1.16 $self->reg_cb (irc_437 => \&change_nick_login_cb);
185     $self->reg_cb (irc_433 => \&change_nick_login_cb);
186 elmex 1.13
187 elmex 1.23 $self->reg_cb (irc_332 => \&rpl_topic_cb);
188     $self->reg_cb (irc_topic => \&topic_change_cb);
189    
190 elmex 1.17 $self->{def_nick_change} = $self->{nick_change} =
191     sub {
192     my ($old_nick) = @_;
193     "${old_nick}_"
194     };
195 elmex 1.13
196 elmex 1.2 return $self;
197     }
198    
199     =item B<register ($nick, $user, $real)>
200    
201     Sends the IRC registration commands NICK and USER.
202    
203     =cut
204    
205     sub register {
206     my ($self, $nick, $user, $real) = @_;
207    
208 elmex 1.13 $self->{nick} = $nick;
209     $self->{user} = $user;
210     $self->{real} = $real;
211    
212 elmex 1.1 $self->send_msg (undef, "NICK", undef, $nick);
213     $self->send_msg (undef, "USER", $real || $nick, $user || $nick, "*", "0");
214 elmex 1.13 }
215    
216     =item B<set_nick_change_cb $callback>
217    
218     This method lets you modify the nickname renaming mechanism when registering
219     the connection. C<$callback> is called with the current nickname as first
220     argument when a ERR_NICKNAMEINUSE or ERR_UNAVAILRESOURCE error occurs on login.
221     The returnvalue of C<$callback> will then be used to change the nickname.
222    
223 elmex 1.17 If C<$callback> is not defined the default nick change callback will be used
224     again.
225    
226 elmex 1.13 The default callback appends '_' to the end of the nickname supplied in the
227     C<register> routine.
228    
229     If the callback returns the same nickname that was given it the connection
230     will be terminated.
231 elmex 1.1
232 elmex 1.13 =cut
233    
234     sub set_nick_change_cb {
235     my ($self, $cb) = @_;
236 elmex 1.17 $cb = $self->{def_nick_change} unless defined $cb;
237 elmex 1.13 $self->{nick_change} = $cb;
238 elmex 1.1 }
239    
240     =item B<nick ()>
241    
242     Returns the current nickname, under which this connection
243     is registered at the IRC server. It might be different from the
244     one that was passed to C<register> as a nick-collision might happened
245     on login.
246    
247     =cut
248    
249     sub nick { $_[0]->{nick} }
250    
251 elmex 1.17 =item B<registered ()>
252 elmex 1.13
253     Returns a true value when the connection has been registered successfull and
254     you can send commands.
255    
256     =cut
257    
258 elmex 1.17 sub registered { $_[0]->{registered} }
259 elmex 1.13
260 elmex 1.1 =item B<channel_list ()>
261    
262 elmex 1.13 This returns a hash reference. The keys are the currently joined channels in lower case.
263     The values are hash references which contain the joined nicks as key.
264    
265     NOTE: Future versions might preserve the case from the JOIN command to the channels.
266 elmex 1.1
267     =cut
268    
269     sub channel_list {
270     my ($self) = @_;
271 elmex 1.12 return $self->{channel_list} || {};
272 elmex 1.1 }
273    
274 elmex 1.5 =item B<send_srv ($command, $trailing, @params)>
275 elmex 1.1
276 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>).
277 elmex 1.1 If the connection isn't yet registered (for example if the connection is slow) and hasn't got a
278     welcome (IRC command 001) from the server yet, the IRC message is queued until it gets a welcome.
279    
280     =cut
281    
282     sub send_srv {
283     my ($self, @msg) = @_;
284    
285 elmex 1.17 if ($self->registered) {
286 elmex 1.1 $self->send_msg (undef, @msg);
287    
288     } else {
289     push @{$self->{con_queue}}, \@msg;
290     }
291     }
292    
293     =item B<clear_srv_queue>
294    
295     Clears the server send queue.
296    
297     =cut
298    
299     sub clear_srv_queue {
300     my ($self) = @_;
301     $self->{con_queue} = [];
302     }
303    
304    
305 elmex 1.5 =item B<send_chan ($channel, $command, $trailing, @params))>
306    
307 elmex 1.13 This function sends a message (constructed by C<mk_msg (undef, $command,
308     $trailing, @params)> to the server, like C<send_srv> only that it will queue
309     the messages if it hasn't joined the channel C<$channel> yet. The queued
310     messages will be send once the connection successfully JOINed the C<$channel>.
311 elmex 1.1
312 elmex 1.12 C<$channel> will be lowercased so that any case that comes from the server matches.
313     (Yes, IRC handles upper and lower case as equal :-(
314    
315     Be careful with this, there are chances you might not join the channel you
316     wanted to join. You may wanted to join #bla and the server redirects that
317     and sends you that you joined #blubb. You may use C<clear_chan_queue> to
318     remove the queue after some timeout after joining, so that you don't end up
319     with a memory leak.
320    
321 elmex 1.1 =cut
322    
323     sub send_chan {
324     my ($self, $chan, @msg) = @_;
325    
326 elmex 1.14 if ($self->{channel_list}->{lc $chan}) {
327 elmex 1.1 $self->send_msg (undef, @msg);
328    
329     } else {
330     push @{$self->{chan_queue}->{lc $chan}}, \@msg;
331     }
332     }
333    
334     =item B<clear_chan_queue ($channel)>
335    
336     Clears the channel queue of the channel C<$channel>.
337    
338     =cut
339    
340     sub clear_chan_queue {
341     my ($self, $chan) = @_;
342     $self->{chan_queue}->{lc $chan} = [];
343     }
344    
345     ################################################################################
346 elmex 1.2 # Private utility functions
347     ################################################################################
348    
349     sub _was_me {
350     my ($self, $msg) = @_;
351 elmex 1.4 lc prefix_nick ($msg) eq lc $self->nick ()
352 elmex 1.2 }
353    
354     ################################################################################
355 elmex 1.1 # Callbacks
356     ################################################################################
357    
358 elmex 1.16 sub channel_remove_event_cb {
359 elmex 1.2 my ($self, $chan, @nicks) = @_;
360    
361     for my $nick (@nicks) {
362 elmex 1.12 if (lc ($nick) eq lc ($self->nick ())) {
363     delete $self->{chan_queue}->{lc $chan};
364 elmex 1.13 delete $self->{channel_list}->{lc $chan};
365 elmex 1.2 last;
366     } else {
367 elmex 1.13 delete $self->{channel_list}->{lc $chan}->{$nick};
368 elmex 1.2 }
369     }
370    
371     1;
372     }
373    
374 elmex 1.16 sub channel_add_event_cb {
375 elmex 1.2 my ($self, $chan, @nicks) = @_;
376    
377     for my $nick (@nicks) {
378 elmex 1.12 if (lc ($nick) eq lc ($self->nick ())) {
379     for (@{$self->{chan_queue}->{lc $chan}}) {
380 elmex 1.2 $self->send_msg (undef, @$_);
381     }
382 elmex 1.11 $self->clear_chan_queue ($chan);
383 elmex 1.2 }
384    
385 elmex 1.13 $self->{channel_list}->{lc $chan}->{$nick} = 1;
386 elmex 1.2 }
387    
388     1;
389     }
390    
391 elmex 1.19 sub _filter_new_nicks_from_channel {
392     my ($self, $chan, @nicks) = @_;
393     grep { not exists $self->{channel_list}->{lc $chan}->{$_} } @nicks;
394     }
395    
396 elmex 1.1 sub anymsg_cb {
397     my ($self, $msg) = @_;
398    
399     my $cmd = lc $msg->{command};
400    
401 elmex 1.13 if ( $cmd ne "privmsg"
402 elmex 1.1 and $cmd ne "notice"
403     and $cmd ne "part"
404     and $cmd ne "join"
405 elmex 1.13 and not ($cmd >= 400 and $cmd <= 599)
406     )
407 elmex 1.1 {
408     $self->event (statmsg => $msg);
409 elmex 1.13 } elsif ($cmd >= 400 and $cmd <= 599) {
410 elmex 1.21 $self->event (error => $msg->{command}, $msg->{trailing}, $msg);
411 elmex 1.1 }
412    
413     1;
414     }
415    
416     sub privmsg_cb {
417     my ($self, $msg) = @_;
418    
419 elmex 1.3 my $targ = $msg->{params}->[0];
420     if ($targ =~ m/^(?:[#+&]|![A-Z0-9]{5})/) {
421     $self->event (publicmsg => $targ, $msg);
422 elmex 1.1
423     } else {
424 elmex 1.3 $self->event (privatemsg => $targ, $msg);
425 elmex 1.1 }
426    
427     1;
428     }
429    
430     sub welcome_cb {
431     my ($self, $msg) = @_;
432    
433 elmex 1.17 $self->{registered} = 1;
434 elmex 1.1
435     for (@{$self->{con_queue}}) {
436     $self->send_msg (undef, @$_);
437     }
438 elmex 1.11 $self->clear_srv_queue ();
439 elmex 1.1
440 elmex 1.12 $self->event ('registered');
441    
442 elmex 1.1 1;
443     }
444    
445     sub ping_cb {
446     my ($self, $msg) = @_;
447 elmex 1.8 $self->send_msg (undef, "PONG", $msg->{params}->[0]);
448 elmex 1.2
449     1;
450 elmex 1.1 }
451    
452 elmex 1.12 sub nick_cb {
453     my ($self, $msg) = @_;
454     my $nick = prefix_nick ($msg);
455     my $newnick = $msg->{params}->[0];
456     my $wasme = $self->_was_me ($msg);
457    
458 elmex 1.13 if ($wasme) { $self->{nick} = $newnick }
459 elmex 1.12
460 elmex 1.13 my @chans;
461 elmex 1.12
462 elmex 1.13 for my $channame (keys %{$self->{channel_list}}) {
463     my $chan = $self->{channel_list}->{$channame};
464 elmex 1.12 if (exists $chan->{$nick}) {
465     delete $chan->{$nick};
466     $chan->{$newnick} = 1;
467 elmex 1.23
468 elmex 1.13 push @chans, $channame;
469 elmex 1.12 }
470     }
471    
472 elmex 1.13 for (@chans) {
473     $self->event (channel_change => $_, $nick, $newnick, $wasme);
474     }
475 elmex 1.12 $self->event (nick_change => $nick, $newnick, $wasme);
476 elmex 1.23
477     1;
478 elmex 1.12 }
479    
480 elmex 1.1 sub namereply_cb {
481     my ($self, $msg) = @_;
482 elmex 1.2 my @nicks = split / /, $msg->{trailing};
483     push @{$self->{_tmp_namereply}}, @nicks;
484 elmex 1.10
485     1;
486 elmex 1.1 }
487    
488     sub endofnames_cb {
489     my ($self, $msg) = @_;
490 elmex 1.12 my $chan = $msg->{params}->[1];
491 elmex 1.20 my @nicks =
492 elmex 1.19 $self->_filter_new_nicks_from_channel (
493 elmex 1.20 $chan, map { s/^[@\+%&]//; $_ } @{delete $self->{_tmp_namereply}}
494     );
495    
496     $self->event (channel_add => $chan, @nicks) if @nicks;
497 elmex 1.10
498     1;
499 elmex 1.1 }
500    
501     sub join_cb {
502     my ($self, $msg) = @_;
503 elmex 1.2 my $chan = $msg->{params}->[0];
504 elmex 1.4 my $nick = prefix_nick ($msg);
505 elmex 1.1
506 elmex 1.12 $self->event (channel_add => $chan, $nick);
507 elmex 1.2 $self->event (join => $nick, $chan, $self->_was_me ($msg));
508 elmex 1.1
509 elmex 1.2 1;
510     }
511    
512     sub part_cb {
513     my ($self, $msg) = @_;
514     my $chan = $msg->{params}->[0];
515 elmex 1.4 my $nick = prefix_nick ($msg);
516 elmex 1.1
517 elmex 1.2 $self->event (part => $nick, $chan, $self->_was_me ($msg), $msg->{params}->[1]);
518     $self->event (channel_remove => $chan, $nick);
519 elmex 1.1
520 elmex 1.2 1;
521     }
522 elmex 1.1
523 elmex 1.2 sub kick_cb {
524     my ($self, $msg) = @_;
525     my $chan = $msg->{params}->[0];
526     my $kicked_nick = $msg->{params}->[1];
527 elmex 1.1
528 elmex 1.2 $self->event (channel_remove => $chan, $kicked_nick);
529 elmex 1.1
530     1;
531     }
532    
533 elmex 1.2 sub quit_cb {
534 elmex 1.1 my ($self, $msg) = @_;
535 elmex 1.4 my $nick = prefix_nick ($msg);
536 elmex 1.1
537 elmex 1.2 $self->event (quit => $nick, $msg->{params}->[1]);
538 elmex 1.1
539 elmex 1.12 for (keys %{$self->{channel_list}}) {
540 elmex 1.14 $self->event (channel_remove => $_, $nick)
541     if $self->{channel_list}->{$_}->{$nick};
542 elmex 1.1 }
543 elmex 1.10
544     1;
545 elmex 1.1 }
546    
547     sub debug_cb {
548     my ($self, $msg) = @_;
549     print "$self->{h}:$self->{p} > ";
550 elmex 1.22 print (join " ", map { $_ => $msg->{$_} } grep { $_ ne 'params' } sort keys %$msg);
551 elmex 1.1 print " params:";
552 elmex 1.21 print (join ",", @{$msg->{params}});
553 elmex 1.1 print "\n";
554 elmex 1.10
555     1;
556 elmex 1.1 }
557    
558 elmex 1.16 sub change_nick_login_cb {
559 elmex 1.13 my ($self, $msg) = @_;
560    
561 elmex 1.17 unless ($self->registered) {
562 elmex 1.13 my $newnick = $self->{nick_change}->($self->nick);
563    
564     if (lc $newnick eq lc $self->{nick}) {
565     $self->disconnect;
566     return 0;
567     }
568    
569     $self->{nick} = $newnick;
570     $self->send_msg (undef, "NICK", undef, $newnick);
571     }
572    
573 elmex 1.17 not ($self->registered) # kill the cb when registered
574 elmex 1.13 }
575    
576 elmex 1.15 sub disconnect_cb {
577     my ($self) = @_;
578    
579     for (keys %{$self->{channel_list}}) {
580     $self->event (channel_remove => $_, $self->nick)
581     }
582    
583     1
584     }
585    
586 elmex 1.23 sub rpl_topic_cb {
587     my ($self, $msg) = @_;
588     my $chan = $msg->{params}->[1];
589     my $topic = $msg->{trailing};
590    
591     $self->event (channel_topic => $chan, $topic);
592    
593     1
594     }
595    
596     sub topic_change_cb {
597     my ($self, $msg) = @_;
598     my $who = prefix_nick ($msg);
599     my $chan = $msg->{params}->[0];
600     my $topic = $msg->{trailing};
601    
602     $self->event (channel_topic => $chan, $topic, $who);
603    
604     1
605     }
606    
607 elmex 1.1 =back
608    
609     =head1 EXAMPLES
610    
611     See samples/netirc3cl and other samples in samples/ for some examples on how to use Net::IRC3::Client::Connection.
612    
613     =head1 AUTHOR
614    
615     Robin Redeker, C<< <elmex@ta-sa.org> >>
616    
617     =head1 SEE ALSO
618    
619 elmex 1.7 L<Net::IRC3::Connection>
620 elmex 1.1
621     RFC 2812 - Internet Relay Chat: Client Protocol
622    
623     =head1 COPYRIGHT & LICENSE
624    
625 elmex 1.6 Copyright 2006 Robin Redeker, all rights reserved.
626 elmex 1.1
627     This program is free software; you can redistribute it and/or modify it
628     under the same terms as Perl itself.
629    
630     =cut
631    
632     1;