ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Net-IRC3/lib/Net/IRC3/Client/Connection.pm
Revision: 1.14
Committed: Wed Jan 17 18:25:09 2007 UTC (19 years, 9 months ago) by elmex
Branch: MAIN
Changes since 1.13: +3 -12 lines
Log Message:
fixed a bug in QUIT command handling where too many channel_remove events
were generated.
added chat buffer to jsonsrv and some buffer attributes to jsonclient

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