ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/cvsroot/Net-IRC3/lib/Net/IRC3/Client/Connection.pm
Revision: 1.19
Committed: Sun Feb 25 10:03:11 2007 UTC (19 years, 8 months ago) by elmex
Branch: MAIN
Changes since 1.18: +12 -1 lines
Log Message:
- fixed a bug where channel_add was called with already
  joined nicks, these duplicates are now prevented.

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.19 sub _filter_new_nicks_from_channel {
383     my ($self, $chan, @nicks) = @_;
384     grep { not exists $self->{channel_list}->{lc $chan}->{$_} } @nicks;
385     }
386    
387 elmex 1.1 sub anymsg_cb {
388     my ($self, $msg) = @_;
389    
390     my $cmd = lc $msg->{command};
391    
392 elmex 1.13 if ( $cmd ne "privmsg"
393 elmex 1.1 and $cmd ne "notice"
394     and $cmd ne "part"
395     and $cmd ne "join"
396 elmex 1.13 and not ($cmd >= 400 and $cmd <= 599)
397     )
398 elmex 1.1 {
399     $self->event (statmsg => $msg);
400 elmex 1.13 } elsif ($cmd >= 400 and $cmd <= 599) {
401     $self->event (error => $msg->{command}, $msg->{trailing}, @{$msg->{params}});
402 elmex 1.1 }
403    
404     1;
405     }
406    
407     sub privmsg_cb {
408     my ($self, $msg) = @_;
409    
410 elmex 1.3 my $targ = $msg->{params}->[0];
411     if ($targ =~ m/^(?:[#+&]|![A-Z0-9]{5})/) {
412     $self->event (publicmsg => $targ, $msg);
413 elmex 1.1
414     } else {
415 elmex 1.3 $self->event (privatemsg => $targ, $msg);
416 elmex 1.1 }
417    
418     1;
419     }
420    
421     sub welcome_cb {
422     my ($self, $msg) = @_;
423    
424 elmex 1.17 $self->{registered} = 1;
425 elmex 1.1
426     for (@{$self->{con_queue}}) {
427     $self->send_msg (undef, @$_);
428     }
429 elmex 1.11 $self->clear_srv_queue ();
430 elmex 1.1
431 elmex 1.12 $self->event ('registered');
432    
433 elmex 1.1 1;
434     }
435    
436     sub ping_cb {
437     my ($self, $msg) = @_;
438 elmex 1.8 $self->send_msg (undef, "PONG", $msg->{params}->[0]);
439 elmex 1.2
440     1;
441 elmex 1.1 }
442    
443 elmex 1.12 sub nick_cb {
444     my ($self, $msg) = @_;
445     my $nick = prefix_nick ($msg);
446     my $newnick = $msg->{params}->[0];
447     my $wasme = $self->_was_me ($msg);
448    
449 elmex 1.13 if ($wasme) { $self->{nick} = $newnick }
450 elmex 1.12
451 elmex 1.13 my @chans;
452 elmex 1.12
453 elmex 1.13 for my $channame (keys %{$self->{channel_list}}) {
454     my $chan = $self->{channel_list}->{$channame};
455 elmex 1.12 if (exists $chan->{$nick}) {
456     delete $chan->{$nick};
457     $chan->{$newnick} = 1;
458 elmex 1.13 push @chans, $channame;
459 elmex 1.12 }
460     }
461    
462 elmex 1.13 for (@chans) {
463     $self->event (channel_change => $_, $nick, $newnick, $wasme);
464     }
465 elmex 1.12 $self->event (nick_change => $nick, $newnick, $wasme);
466     }
467    
468 elmex 1.1 sub namereply_cb {
469     my ($self, $msg) = @_;
470 elmex 1.2 my @nicks = split / /, $msg->{trailing};
471     push @{$self->{_tmp_namereply}}, @nicks;
472 elmex 1.10
473     1;
474 elmex 1.1 }
475    
476     sub endofnames_cb {
477     my ($self, $msg) = @_;
478 elmex 1.12 my $chan = $msg->{params}->[1];
479 elmex 1.19 $self->event (
480     channel_add => $chan,
481     $self->_filter_new_nicks_from_channel (
482     $chan,
483     map { s/^[@\+%&]//; $_ } @{delete $self->{_tmp_namereply}}
484     )
485     );
486 elmex 1.10
487     1;
488 elmex 1.1 }
489    
490     sub join_cb {
491     my ($self, $msg) = @_;
492 elmex 1.2 my $chan = $msg->{params}->[0];
493 elmex 1.4 my $nick = prefix_nick ($msg);
494 elmex 1.1
495 elmex 1.12 $self->event (channel_add => $chan, $nick);
496 elmex 1.2 $self->event (join => $nick, $chan, $self->_was_me ($msg));
497 elmex 1.1
498 elmex 1.2 1;
499     }
500    
501     sub part_cb {
502     my ($self, $msg) = @_;
503     my $chan = $msg->{params}->[0];
504 elmex 1.4 my $nick = prefix_nick ($msg);
505 elmex 1.1
506 elmex 1.2 $self->event (part => $nick, $chan, $self->_was_me ($msg), $msg->{params}->[1]);
507     $self->event (channel_remove => $chan, $nick);
508 elmex 1.1
509 elmex 1.2 1;
510     }
511 elmex 1.1
512 elmex 1.2 sub kick_cb {
513     my ($self, $msg) = @_;
514     my $chan = $msg->{params}->[0];
515     my $kicked_nick = $msg->{params}->[1];
516 elmex 1.1
517 elmex 1.2 $self->event (channel_remove => $chan, $kicked_nick);
518 elmex 1.1
519     1;
520     }
521    
522 elmex 1.2 sub quit_cb {
523 elmex 1.1 my ($self, $msg) = @_;
524 elmex 1.4 my $nick = prefix_nick ($msg);
525 elmex 1.1
526 elmex 1.2 $self->event (quit => $nick, $msg->{params}->[1]);
527 elmex 1.1
528 elmex 1.12 for (keys %{$self->{channel_list}}) {
529 elmex 1.14 $self->event (channel_remove => $_, $nick)
530     if $self->{channel_list}->{$_}->{$nick};
531 elmex 1.1 }
532 elmex 1.10
533     1;
534 elmex 1.1 }
535    
536     sub debug_cb {
537     my ($self, $msg) = @_;
538     print "$self->{h}:$self->{p} > ";
539     my $par = delete $msg->{params};
540     print (join " ", map { $_ => $msg->{$_} } sort keys %$msg);
541     print " params:";
542     print (join ",", @$par);
543     print "\n";
544 elmex 1.10
545     1;
546 elmex 1.1 }
547    
548 elmex 1.16 sub change_nick_login_cb {
549 elmex 1.13 my ($self, $msg) = @_;
550    
551 elmex 1.17 unless ($self->registered) {
552 elmex 1.13 my $newnick = $self->{nick_change}->($self->nick);
553    
554     if (lc $newnick eq lc $self->{nick}) {
555     $self->disconnect;
556     return 0;
557     }
558    
559     $self->{nick} = $newnick;
560     $self->send_msg (undef, "NICK", undef, $newnick);
561     }
562    
563 elmex 1.17 not ($self->registered) # kill the cb when registered
564 elmex 1.13 }
565    
566 elmex 1.15 sub disconnect_cb {
567     my ($self) = @_;
568    
569     for (keys %{$self->{channel_list}}) {
570     $self->event (channel_remove => $_, $self->nick)
571     }
572    
573     1
574     }
575    
576 elmex 1.1 =back
577    
578     =head1 EXAMPLES
579    
580     See samples/netirc3cl and other samples in samples/ for some examples on how to use Net::IRC3::Client::Connection.
581    
582     =head1 AUTHOR
583    
584     Robin Redeker, C<< <elmex@ta-sa.org> >>
585    
586     =head1 SEE ALSO
587    
588 elmex 1.7 L<Net::IRC3::Connection>
589 elmex 1.1
590     RFC 2812 - Internet Relay Chat: Client Protocol
591    
592     =head1 COPYRIGHT & LICENSE
593    
594 elmex 1.6 Copyright 2006 Robin Redeker, all rights reserved.
595 elmex 1.1
596     This program is free software; you can redistribute it and/or modify it
597     under the same terms as Perl itself.
598    
599     =cut
600    
601     1;