package Net::IRC3::Client::Connection; use base Net::IRC3::Connection; our $DEBUG = 0; =head1 NAME Net::IRC3::Client::Connection - A highlevel IRC connection =head1 SYNOPSIS use Net::IRC3::Client; my $irc3 = new Net::IRC3::Client; my $con = $irc3->connect ("test.not.at.irc.net", 6667); $con->register ("fancybot", "fancybot", "Mr. Fancybot"); ... =head1 DESCRIPTION L itself is a simple building block for an IRC client. It manages connections and parses and constructs IRC messages. L is I simple, if you don't want to care about all the other things that a client still has to do (like replying to PINGs and remembering who is on a channel), I recommend to read the L page instead. =head1 EVENTS The following events are emitted by L. Use C as described in L to register to such an event. =over 4 =item B Emitted when C<@nicks> are added to the channel C<$channel>, this happens for example when someone JOINs a channel or when you get a RPL_NAMREPLY (see RFC2812). =item B Emitted when C<@nicks> are removed from the channel C<$channel>, happens for example when they PART, QUIT or get KICKed. =item B Emitted when C<$nick> enters the channel C<$channel> by JOINing. C<$is_myself> is true if youself are the one who JOINs. =item B Emitted when C<$nick> PARTs the channel C<$channel>. C<$is_myself> is true if youself are the one who PARTs. C<$msg> is the PART message. =back =head1 METHODS =over 4 =cut sub new { my $this = shift; my $class = ref($this) || $this; my $self = $class->SUPER::new (@_); $self->reg_cb (irc_001 => \&welcome_cb); $self->reg_cb (irc_join => \&join_cb); $self->reg_cb (irc_part => \&part_cb); $self->reg_cb (irc_kick => \&kick_cb); $self->reg_cb (irc_quit => \&quit_cb); $self->reg_cb (irc_353 => \&namereply_cb); $self->reg_cb (irc_366 => \&endofnames_cb); $self->reg_cb (irc_ping => \&ping_cb); $self->reg_cb (irc_privmsg => \&privmsg_cb); $self->reg_cb (irc_notice => \&privmsg_cb); $self->reg_cb ('irc_*' => \&debug_cb) if $DEBUG; $self->reg_cb ('irc_*' => \&anymsg_cb); $self->reg_cb (channel_remove => \&channel_remove_event); $self->reg_cb (channel_add => \&channel_add_event); return $self; } =item B Sends the IRC registration commands NICK and USER. =cut sub register { my ($self, $nick, $user, $real) = @_; $self->send_msg (undef, "NICK", undef, $nick); $self->send_msg (undef, "USER", $real || $nick, $user || $nick, "*", "0"); $self->{nick} = $nick; $self->{user} = $user; $self->{real} = $real; } =item B Returns the current nickname, under which this connection is registered at the IRC server. It might be different from the one that was passed to C as a nick-collision might happened on login. =cut sub nick { $_[0]->{nick} } =item B This returns a hash reference. The keys are the currently joined channels. The values are hash references which contain the joined nicks as key. =cut sub channel_list { my ($self) = @_; return $self->{channels}; } =item B This function sends an IRC message that is constructed by C (see L). If the connection isn't yet registered (for example if the connection is slow) and hasn't got a welcome (IRC command 001) from the server yet, the IRC message is queued until it gets a welcome. =cut sub send_srv { my ($self, @msg) = @_; if ($self->{connected}) { $self->send_msg (undef, @msg); } else { push @{$self->{con_queue}}, \@msg; } } =item B Clears the server send queue. =cut sub clear_srv_queue { my ($self) = @_; $self->{con_queue} = []; } =item B This function sends a message to server, like C only that it will queue the messages if it hasn't joined the channel C<$channel> yet. The queued messages will be send once the connection successfully joined the C<$channel>. =cut sub send_chan { my ($self, $chan, @msg) = @_; if ($self->{channels}->{lc $chan}) { $self->send_msg (undef, @msg); } else { push @{$self->{chan_queue}->{lc $chan}}, \@msg; } } =item B Clears the channel queue of the channel C<$channel>. =cut sub clear_chan_queue { my ($self, $chan) = @_; $self->{chan_queue}->{lc $chan} = []; } ################################################################################ # Private utility functions ################################################################################ sub _was_me { my ($self, $msg) = @_; lc Net::IRC3::prefix_nick ($msg) eq lc $self->nick () } ################################################################################ # Callbacks ################################################################################ sub channel_remove_event { my ($self, $chan, @nicks) = @_; $chan = lc $chan; for my $nick (@nicks) { $nick = lc $nick; if ($nick eq lc $self->nick ()) { delete $self->{chan_queue}->{$chan}; delete $self->{channels}->{$chan}; last; } else { delete $self->{channels}->{$chan}->{$nick}; } } 1; } sub channel_add_event { my ($self, $chan, @nicks) = @_; $chan = lc $chan; for my $nick (@nicks) { $nick = lc $nick; if ($nick eq lc $self->nick ()) { for (@{$self->{chan_queue}->{$chan}}) { $self->send_msg (undef, @$_); } } $self->{channels}->{$chan}->{$nick} = 1; } 1; } sub anymsg_cb { my ($self, $msg) = @_; my $cmd = lc $msg->{command}; if ( $cmd ne "privmsg" and $cmd ne "notice" and $cmd ne "part" and $cmd ne "join" ) { $self->event (statmsg => $msg); } 1; } sub privmsg_cb { my ($self, $msg) = @_; if ($msg->{params}->[0] =~ m/^[#!]/) { $self->event (chanmsg => $msg->{params}->[0], $msg); } else { $self->event (querymsg => $msg->{params}->[0], $msg); } 1; } sub welcome_cb { my ($self, $msg) = @_; $self->{connected} = 1; $self->event ('registered'); for (@{$self->{con_queue}}) { $self->send_msg (undef, @$_); } 1; } sub ping_cb { my ($self, $msg) = @_; $self->send_srv ("PONG", $msg->{params}->[0]); 1; } sub namereply_cb { my ($self, $msg) = @_; my @nicks = split / /, $msg->{trailing}; push @{$self->{_tmp_namereply}}, @nicks; } sub endofnames_cb { my ($self, $msg) = @_; my $chan = lc $msg->{params}->[1]; $self->event (channel_add => $chan, @{delete $self->{_tmp_namereply}}); } sub join_cb { my ($self, $msg) = @_; my $chan = $msg->{params}->[0]; my $nick = Net::IRC3::prefix_nick ($msg); $self->event (join => $nick, $chan, $self->_was_me ($msg)); $self->event (channel_add => $chan, $nick); 1; } sub part_cb { my ($self, $msg) = @_; my $chan = $msg->{params}->[0]; my $nick = Net::IRC3::prefix_nick ($msg); $self->event (part => $nick, $chan, $self->_was_me ($msg), $msg->{params}->[1]); $self->event (channel_remove => $chan, $nick); 1; } sub kick_cb { my ($self, $msg) = @_; my $chan = $msg->{params}->[0]; my $kicked_nick = $msg->{params}->[1]; $self->event (channel_remove => $chan, $kicked_nick); 1; } sub quit_cb { my ($self, $msg) = @_; my $nick = Net::IRC3::prefix_nick ($msg); $self->event (quit => $nick, $msg->{params}->[1]); for (keys %{$self->{channels}}) { $self->event (channel_remove => $_, $nick); } } sub debug_cb { my ($self, $msg) = @_; print "$self->{h}:$self->{p} > "; my $par = delete $msg->{params}; print (join " ", map { $_ => $msg->{$_} } sort keys %$msg); print " params:"; print (join ",", @$par); print "\n"; } =back =head1 EXAMPLES See samples/netirc3cl and other samples in samples/ for some examples on how to use Net::IRC3::Client::Connection. =head1 AUTHOR Robin Redeker, C<< >> =head1 SEE ALSO L L L RFC 2812 - Internet Relay Chat: Client Protocol =head1 COPYRIGHT & LICENSE Copyright 2006 Robin Redker, all rights reserved. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut 1;