1package POE::Component::IRC; 2our $AUTHORITY = 'cpan:HINRIK'; 3$POE::Component::IRC::VERSION = '6.93'; 4use strict; 5use warnings FATAL => 'all'; 6use Carp; 7use POE qw(Wheel::SocketFactory Wheel::ReadWrite Driver::SysRW 8 Filter::Line Filter::Stream Filter::Stackable); 9use POE::Filter::IRCD; 10use POE::Filter::IRC::Compat; 11use POE::Component::IRC::Constants qw(:ALL); 12use POE::Component::IRC::Plugin qw(:ALL); 13use POE::Component::IRC::Plugin::DCC; 14use POE::Component::IRC::Plugin::ISupport; 15use POE::Component::IRC::Plugin::Whois; 16use Socket qw(AF_INET SOCK_STREAM unpack_sockaddr_in inet_ntoa inet_aton); 17use base qw(POE::Component::Syndicator); 18 19our ($GOT_SSL, $GOT_CLIENT_DNS, $GOT_SOCKET6, $GOT_ZLIB); 20 21BEGIN { 22 eval { 23 require POE::Component::SSLify; 24 import POE::Component::SSLify qw( Client_SSLify SSLify_ContextCreate ); 25 $GOT_SSL = 1; 26 }; 27 eval { 28 require POE::Component::Client::DNS; 29 $GOT_CLIENT_DNS = 1 if $POE::Component::Client::DNS::VERSION >= 0.99; 30 }; 31 eval { 32 require POE::Filter::Zlib::Stream; 33 $GOT_ZLIB = 1 if $POE::Filter::Zlib::Stream::VERSION >= 1.96; 34 }; 35 # Socket6 provides AF_INET6 where earlier Perls' Socket don't. 36 eval { 37 Socket->import(qw(AF_INET6 unpack_sockaddr_in6 inet_ntop)); 38 $GOT_SOCKET6 = 1; 39 }; 40 if (!$GOT_SOCKET6) { 41 eval { 42 require Socket6; 43 Socket6->import(qw(AF_INET6 unpack_sockaddr_in6 inet_ntop)); 44 $GOT_SOCKET6 = 1; 45 }; 46 if (!$GOT_SOCKET6) { 47 # provide a dummy sub so code compiles 48 *AF_INET6 = sub { ~0 }; 49 } 50 } 51} 52 53# BINGOS: I have bundled up all the stuff that needs changing 54# for inherited classes into _create. This gets called from 'spawn'. 55# $self->{OBJECT_STATES_ARRAYREF} contains event mappings to methods that have 56# the same name, gets passed to POE::Session->create as $self => [ ]; 57# $self->{OBJECT_STATES_HASHREF} contains event mappings to methods, where the 58# event and the method have diferent names. 59# $self->{IRC_CMDS} contains the traditional %irc_commands, mapping commands 60# to events and the priority that the command has. 61sub _create { 62 my ($self) = @_; 63 64 $self->{IRC_CMDS} = { 65 rehash => [ PRI_HIGH, 'noargs', ], 66 die => [ PRI_HIGH, 'noargs', ], 67 restart => [ PRI_HIGH, 'noargs', ], 68 quit => [ PRI_NORMAL, 'oneoptarg', ], 69 version => [ PRI_HIGH, 'oneoptarg', ], 70 time => [ PRI_HIGH, 'oneoptarg', ], 71 trace => [ PRI_HIGH, 'oneoptarg', ], 72 admin => [ PRI_HIGH, 'oneoptarg', ], 73 info => [ PRI_HIGH, 'oneoptarg', ], 74 away => [ PRI_HIGH, 'oneoptarg', ], 75 users => [ PRI_HIGH, 'oneoptarg', ], 76 lusers => [ PRI_HIGH, 'oneoptarg', ], 77 locops => [ PRI_HIGH, 'oneoptarg', ], 78 operwall => [ PRI_HIGH, 'oneoptarg', ], 79 wallops => [ PRI_HIGH, 'oneoptarg', ], 80 motd => [ PRI_HIGH, 'oneoptarg', ], 81 who => [ PRI_HIGH, 'oneoptarg', ], 82 nick => [ PRI_HIGH, 'onlyonearg', ], 83 oper => [ PRI_HIGH, 'onlytwoargs', ], 84 invite => [ PRI_HIGH, 'onlytwoargs', ], 85 squit => [ PRI_HIGH, 'onlytwoargs', ], 86 kill => [ PRI_HIGH, 'onlytwoargs', ], 87 privmsg => [ PRI_NORMAL, 'privandnotice', ], 88 privmsglo => [ PRI_NORMAL+1, 'privandnotice', ], 89 privmsghi => [ PRI_NORMAL-1, 'privandnotice', ], 90 notice => [ PRI_NORMAL, 'privandnotice', ], 91 noticelo => [ PRI_NORMAL+1, 'privandnotice', ], 92 noticehi => [ PRI_NORMAL-1, 'privandnotice', ], 93 squery => [ PRI_NORMAL, 'privandnotice', ], 94 join => [ PRI_HIGH, 'oneortwo', ], 95 summon => [ PRI_HIGH, 'oneortwo', ], 96 sconnect => [ PRI_HIGH, 'oneandtwoopt', ], 97 whowas => [ PRI_HIGH, 'oneandtwoopt', ], 98 stats => [ PRI_HIGH, 'spacesep', ], 99 links => [ PRI_HIGH, 'spacesep', ], 100 mode => [ PRI_HIGH, 'spacesep', ], 101 servlist => [ PRI_HIGH, 'spacesep', ], 102 cap => [ PRI_HIGH, 'spacesep', ], 103 part => [ PRI_HIGH, 'commasep', ], 104 names => [ PRI_HIGH, 'commasep', ], 105 list => [ PRI_HIGH, 'commasep', ], 106 whois => [ PRI_HIGH, 'commasep', ], 107 ctcp => [ PRI_HIGH, 'ctcp', ], 108 ctcpreply => [ PRI_HIGH, 'ctcp', ], 109 ping => [ PRI_HIGH, 'oneortwo', ], 110 pong => [ PRI_HIGH, 'oneortwo', ], 111 }; 112 113 my %event_map = map {($_ => $self->{IRC_CMDS}->{$_}->[CMD_SUB])} 114 keys %{ $self->{IRC_CMDS} }; 115 116 $self->{OBJECT_STATES_HASHREF} = { 117 %event_map, 118 quote => 'sl', 119 }; 120 121 $self->{OBJECT_STATES_ARRAYREF} = [qw( 122 syndicator_started 123 _parseline 124 _sock_down 125 _sock_failed 126 _sock_up 127 _socks_proxy_connect 128 _socks_proxy_response 129 debug 130 connect 131 _resolve_addresses 132 _do_connect 133 _quit_timeout 134 _send_login 135 _got_dns_response 136 ison 137 kick 138 remove 139 nickserv 140 shutdown 141 sl 142 sl_login 143 sl_high 144 sl_delayed 145 sl_prioritized 146 topic 147 userhost 148 )]; 149 150 return; 151} 152 153# BINGOS: the component can now configure itself via _configure() from 154# either spawn() or connect() 155## no critic (Subroutines::ProhibitExcessComplexity) 156sub _configure { 157 my ($self, $args) = @_; 158 my $spawned = 0; 159 160 if (ref $args eq 'HASH' && keys %{ $args }) { 161 $spawned = delete $args->{spawned}; 162 $self->{use_localaddr} = delete $args->{localaddr}; 163 @{ $self }{ keys %{ $args } } = values %{ $args }; 164 } 165 166 if ($ENV{POCOIRC_DEBUG}) { 167 $self->{debug} = 1; 168 $self->{plugin_debug} = 1; 169 } 170 171 if ($self->{debug}) { 172 $self->{ircd_filter}->debug(1); 173 $self->{ircd_compat}->debug(1); 174 } 175 176 if ($self->{useipv6} && !$GOT_SOCKET6) { 177 warn "'useipv6' option specified, but Socket6 was not found\n"; 178 } 179 180 if ($self->{usessl} && !$GOT_SSL) { 181 warn "'usessl' option specified, but POE::Component::SSLify was not found\n"; 182 } 183 184 $self->{dcc}->nataddr($self->{nataddr}) if exists $self->{nataddr}; 185 $self->{dcc}->dccports($self->{dccports}) if exists $self->{dccports}; 186 187 $self->{port} = 6667 if !$self->{port}; 188 $self->{msg_length} = 450 if !defined $self->{msg_length}; 189 190 if ($self->{use_localaddr}) { 191 $self->{localaddr} = $self->{use_localaddr} 192 . ($self->{localport} ? (':'.$self->{localport}) : ''); 193 } 194 195 # Make sure that we have reasonable defaults for all the attributes. 196 # The "IRC*" variables are ircII environment variables. 197 if (!defined $self->{nick}) { 198 $self->{nick} = $ENV{IRCNICK} || eval { scalar getpwuid($>) } 199 || $ENV{USER} || $ENV{LOGNAME} || 'WankerBot'; 200 } 201 202 if (!defined $self->{username}) { 203 $self->{username} = eval { scalar getpwuid($>) } || $ENV{USER} 204 || $ENV{LOGNAME} || 'foolio'; 205 } 206 207 if (!defined $self->{ircname}) { 208 $self->{ircname} = $ENV{IRCNAME} || eval { (getpwuid $>)[6] } 209 || 'Just Another Perl Hacker'; 210 } 211 212 if (!defined $self->{server} && !$spawned) { 213 die "No IRC server specified\n" if !$ENV{IRCSERVER}; 214 $self->{server} = $ENV{IRCSERVER}; 215 } 216 217 if (defined $self->{webirc}) { 218 if (!ref $self->{webirc} ne 'HASH') { 219 die "webirc param expects a hashref"; 220 } 221 for my $expect_key (qw(pass user host ip)) { 222 if (!exists $self->{webirc}{$expect_key}) { 223 die "webirc value is missing key '$expect_key'"; 224 } 225 } 226 } 227 228 return; 229} 230 231sub debug { 232 my ($self, $switch) = @_[OBJECT, ARG0]; 233 234 $self->{debug} = $switch; 235 $self->{ircd_filter}->debug( $switch ); 236 $self->{ircd_compat}->debug( $switch ); 237 return; 238} 239 240# Parse a message from the IRC server and generate the appropriate 241# event(s) for listening sessions. 242sub _parseline { 243 my ($session, $self, $ev) = @_[SESSION, OBJECT, ARG0]; 244 245 return if !$ev->{name}; 246 $self->send_event(irc_raw => $ev->{raw_line} ) if $self->{raw}; 247 248 # record our nickname 249 if ( $ev->{name} eq '001' ) { 250 $self->{INFO}{RealNick} = ( split / /, $ev->{raw_line} )[2]; 251 } 252 253 $ev->{name} = 'irc_' . $ev->{name}; 254 $self->send_event( $ev->{name}, @{$ev->{args}} ); 255 256 if ($ev->{name} =~ /^irc_ctcp_(.+)$/) { 257 $self->send_event(irc_ctcp => $1 => @{$ev->{args}}); 258 } 259 260 return; 261} 262 263# Internal function called when a socket is closed. 264sub _sock_down { 265 my ($kernel, $self) = @_[KERNEL, OBJECT]; 266 267 # Destroy the RW wheel for the socket. 268 delete $self->{socket}; 269 delete $self->{localaddr}; 270 $self->{connected} = 0; 271 272 # Stop any delayed sends. 273 $self->{send_queue} = [ ]; 274 $self->{send_time} = 0; 275 $kernel->delay( sl_delayed => undef ); 276 277 # Reset the filters if necessary 278 $self->_compress_uplink( 0 ); 279 $self->_compress_downlink( 0 ); 280 $self->{ircd_compat}->chantypes( [ '#', '&' ] ); 281 $self->{ircd_compat}->identifymsg(0); 282 283 # post a 'irc_disconnected' to each session that cares 284 $self->send_event(irc_disconnected => $self->{server} ); 285 return; 286} 287 288sub disconnect { 289 my ($self) = @_; 290 $self->yield('_sock_down'); 291 return; 292} 293 294# Internal function called when a socket fails to be properly opened. 295sub _sock_failed { 296 my ($self, $op, $errno, $errstr) = @_[OBJECT, ARG0..ARG2]; 297 298 delete $self->{socketfactory}; 299 $self->send_event(irc_socketerr => "$op error $errno: $errstr" ); 300 return; 301} 302 303# Internal function called when a connection is established. 304sub _sock_up { 305 my ($kernel, $self, $session, $socket) = @_[KERNEL, OBJECT, SESSION, ARG0]; 306 307 # We no longer need the SocketFactory wheel. Scrap it. 308 delete $self->{socketfactory}; 309 310 # Remember what IP address we're connected through, for multihomed boxes. 311 my $localaddr; 312 if ($GOT_SOCKET6) { 313 eval { 314 $localaddr = (unpack_sockaddr_in6( getsockname $socket ))[1]; 315 $localaddr = inet_ntop( AF_INET6, $localaddr ); 316 }; 317 } 318 319 if ( !$localaddr ) { 320 $localaddr = (unpack_sockaddr_in( getsockname $socket ))[1]; 321 $localaddr = inet_ntoa($localaddr); 322 } 323 324 $self->{localaddr} = $localaddr; 325 326 if ( $self->{socks_proxy} ) { 327 $self->{socket} = POE::Wheel::ReadWrite->new( 328 Handle => $socket, 329 Driver => POE::Driver::SysRW->new(), 330 Filter => POE::Filter::Stream->new(), 331 InputEvent => '_socks_proxy_response', 332 ErrorEvent => '_sock_down', 333 ); 334 335 if ( !$self->{socket} ) { 336 $self->send_event(irc_socketerr => 337 "Couldn't create ReadWrite wheel for SOCKS socket" ); 338 return; 339 } 340 341 my $packet; 342 if ( _ip_is_ipv4( $self->{server} ) ) { 343 # SOCKS 4 344 $packet = pack ('CCn', 4, 1, $self->{port}) . 345 inet_aton($self->{server}) . ($self->{socks_id} || '') . (pack 'x'); 346 } 347 else { 348 # SOCKS 4a 349 $packet = pack ('CCn', 4, 1, $self->{port}) . 350 inet_aton('0.0.0.1') . ($self->{socks_id} || '') . (pack 'x') . 351 $self->{server} . (pack 'x'); 352 } 353 354 $self->{socket}->put( $packet ); 355 return; 356 } 357 358 # ssl! 359 if ($GOT_SSL and $self->{usessl}) { 360 eval { 361 my ($ctx); 362 363 if( $self->{sslctx} ) 364 { 365 $ctx = $self->{sslctx}; 366 } 367 elsif( $self->{sslkey} && $self->{sslcert} ) 368 { 369 $ctx = SSLify_ContextCreate( $self->{sslkey}, $self->{sslcert} ); 370 } 371 else 372 { 373 $ctx = undef; 374 } 375 376 $socket = Client_SSLify($socket, undef, undef, $ctx); 377 }; 378 379 if ($@) { 380 chomp $@; 381 warn "Couldn't use an SSL socket: $@\n"; 382 $self->{usessl} = 0; 383 } 384 } 385 386 if ( $self->{compress} ) { 387 $self->_compress_uplink(1); 388 $self->_compress_downlink(1); 389 } 390 391 # Create a new ReadWrite wheel for the connected socket. 392 $self->{socket} = POE::Wheel::ReadWrite->new( 393 Handle => $socket, 394 Driver => POE::Driver::SysRW->new(), 395 InputFilter => $self->{srv_filter}, 396 OutputFilter => $self->{out_filter}, 397 InputEvent => '_parseline', 398 ErrorEvent => '_sock_down', 399 ); 400 401 if ($self->{socket}) { 402 $self->{connected} = 1; 403 } 404 else { 405 $self->send_event(irc_socketerr => "Couldn't create ReadWrite wheel for IRC socket"); 406 return; 407 } 408 409 # Post a 'irc_connected' event to each session that cares 410 $self->send_event(irc_connected => $self->{server} ); 411 412 # CONNECT if we're using a proxy 413 if ($self->{proxy}) { 414 # The original proxy code, AFAIK, did not actually work 415 # with an HTTP proxy. 416 $self->call( 417 'sl_login', 418 'CONNECT ' . $self->{server} . ':' . $self->{port} . " HTTP/1.0\n\n", 419 ); 420 421 # KLUDGE: Also, the original proxy code assumes the connection 422 # is instantaneous Since this is not always the case, mess with 423 # the queueing so that the sent text is delayed... 424 $self->{send_time} = time() + 10; 425 } 426 427 $kernel->yield('_send_login'); 428 return; 429} 430 431sub _socks_proxy_response { 432 my ($kernel, $self, $session, $input) = @_[KERNEL, OBJECT, SESSION, ARG0]; 433 434 if (length $input != 8) { 435 $self->send_event( 436 'irc_socks_failed', 437 'Mangled response from SOCKS proxy', 438 $input, 439 ); 440 $self->disconnect(); 441 return; 442 } 443 444 my @resp = unpack 'CCnN', $input; 445 if (@resp != 4 || $resp[0] ne '0' || $resp[1] !~ /^(?:90|91|92|93)$/) { 446 $self->send_event( 447 'irc_socks_failed', 448 'Mangled response from SOCKS proxy', 449 $input, 450 ); 451 $self->disconnect(); 452 return; 453 } 454 455 if ( $resp[1] eq '90' ) { 456 $kernel->call($session => '_socks_proxy_connect'); 457 $self->{connected} = 1; 458 $self->send_event( 'irc_connected', $self->{server} ); 459 $kernel->yield('_send_login'); 460 } 461 else { 462 $self->send_event( 463 'irc_socks_rejected', 464 $resp[1], 465 $self->{socks_proxy}, 466 $self->{socks_port}, 467 $self->{socks_id}, 468 ); 469 $self->disconnect(); 470 } 471 472 return; 473} 474 475sub _socks_proxy_connect { 476 my ($kernel, $self) = @_[KERNEL, OBJECT]; 477 $self->{socket}->event( InputEvent => '_parseline' ); 478 $self->{socket}->set_input_filter( $self->{srv_filter} ); 479 $self->{socket}->set_output_filter( $self->{out_filter} ); 480 return; 481} 482 483sub _send_login { 484 my ($kernel, $self, $session) = @_[KERNEL, OBJECT, SESSION]; 485 486 # Now that we're connected, attempt to log into the server. 487 488 # for servers which support CAP, it's customary to start with that 489 $kernel->call($session, 'sl_login', 'CAP REQ :identify-msg'); 490 $kernel->call($session, 'sl_login', 'CAP REQ :multi-prefix'); 491 $kernel->call($session, 'sl_login', 'CAP LS'); 492 $kernel->call($session, 'sl_login', 'CAP END'); 493 494 # If we were told to use WEBIRC to spoof our host/IP, do so: 495 if (defined $self->{webirc}) { 496 $kernel->call($session => sl_login => 'WEBIRC ' 497 . join " ", @{$self->{webirc}}{qw(pass user ip host)} 498 ); 499 } 500 501 if (defined $self->{password}) { 502 $kernel->call($session => sl_login => 'PASS ' . $self->{password}); 503 } 504 $kernel->call($session => sl_login => 'NICK ' . $self->{nick}); 505 $kernel->call( 506 $session, 507 'sl_login', 508 'USER ' . 509 join(' ', $self->{username}, 510 (defined $self->{bitmode} ? $self->{bitmode} : 8), 511 '*', 512 ':' . $self->{ircname} 513 ), 514 ); 515 516 # If we have queued data waiting, its flush loop has stopped 517 # while we were disconnected. Start that up again. 518 $kernel->delay(sl_delayed => 0); 519 520 return; 521} 522 523# Set up the component's IRC session. 524sub syndicator_started { 525 my ($kernel, $session, $sender, $self, $alias) 526 = @_[KERNEL, SESSION, SENDER, OBJECT, ARG0, ARG1 .. $#_]; 527 528 # Send queue is used to hold pending lines so we don't flood off. 529 # The count is used to track the number of lines sent at any time. 530 $self->{send_queue} = [ ]; 531 $self->{send_time} = 0; 532 533 $self->{ircd_filter} = POE::Filter::IRCD->new(debug => $self->{debug}); 534 $self->{ircd_compat} = POE::Filter::IRC::Compat->new(debug => $self->{debug}); 535 536 my $srv_filters = [ 537 POE::Filter::Line->new( 538 InputRegexp => '\015?\012', 539 OutputLiteral => '\015\012', 540 ), 541 $self->{ircd_filter}, 542 $self->{ircd_compat}, 543 ]; 544 545 $self->{srv_filter} = POE::Filter::Stackable->new(Filters => $srv_filters); 546 $self->{out_filter} = POE::Filter::Stackable->new(Filters => [ 547 POE::Filter::Line->new( OutputLiteral => "\015\012" ), 548 ]); 549 550 # Plugin 'irc_whois' and 'irc_whowas' support 551 $self->plugin_add('Whois_' . $self->session_id(), 552 POE::Component::IRC::Plugin::Whois->new() 553 ); 554 555 $self->{isupport} = POE::Component::IRC::Plugin::ISupport->new(); 556 $self->plugin_add('ISupport_' . $self->session_id(), $self->{isupport}); 557 $self->{dcc} = POE::Component::IRC::Plugin::DCC->new(); 558 $self->plugin_add('DCC_' . $self->session_id(), $self->{dcc}); 559 560 return 1; 561} 562 563# The handler for commands which have N arguments, separated by commas. 564sub commasep { 565 my ($kernel, $self, $state, @args) = @_[KERNEL, OBJECT, STATE, ARG0 .. $#_]; 566 my $args; 567 568 if ($state eq 'whois' and @args > 1 ) { 569 $args = shift @args; 570 $args .= ' ' . join ',', @args; 571 } 572 elsif ( $state eq 'part' and @args > 1 ) { 573 my $chantypes = join('', @{ $self->isupport('CHANTYPES') || ['#', '&']}); 574 my $message; 575 if ($args[-1] =~ / +/ || $args[-1] !~ /^[$chantypes]/) { 576 $message = pop @args; 577 } 578 $args = join(',', @args); 579 $args .= " :$message" if defined $message; 580 } 581 else { 582 $args = join ',', @args; 583 } 584 585 my $pri = $self->{IRC_CMDS}->{$state}->[CMD_PRI]; 586 $state = uc $state; 587 $state .= " $args" if defined $args; 588 $kernel->yield(sl_prioritized => $pri, $state ); 589 590 return; 591} 592 593# Get variables in order for openning a connection 594sub connect { 595 my ($kernel, $self, $session, $sender, $args) 596 = @_[KERNEL, OBJECT, SESSION, SENDER, ARG0]; 597 598 if ($args) { 599 my %arg; 600 %arg = @{ $args } if ref $args eq 'ARRAY'; 601 %arg = %{ $args } if ref $args eq 'HASH'; 602 $arg{ lc $_ } = delete $arg{$_} for keys %arg; 603 $self->_configure( \%arg ); 604 } 605 606 if ( $self->{resolver} && $self->{res_addresses} 607 && @{ $self->{res_addresses} } ) { 608 push @{ $self->{res_addresses} }, $self->{server}; 609 $self->{resolved_server} = shift @{ $self->{res_addresses} }; 610 } 611 612 # try and use non-blocking resolver if needed 613 if ( $self->{resolver} && !_ip_get_version( $self->{server} ) 614 && !$self->{nodns} ) { 615 $kernel->yield( 616 '_resolve_addresses', 617 $self->{server}, 618 ( $self->{useipv6} && $GOT_SOCKET6 ? 'AAAA' : 'A' ), 619 ); 620 } 621 else { 622 $kernel->yield('_do_connect'); 623 } 624 625 $self->{INFO}{RealNick} = $self->{nick}; 626 return; 627} 628 629sub _resolve_addresses { 630 my ($kernel, $self, $hostname, $type) = @_[KERNEL, OBJECT, ARG0 .. ARG1]; 631 632 my $response = $self->{resolver}->resolve( 633 event => '_got_dns_response', 634 host => $hostname, 635 type => $type, 636 context => { }, 637 ); 638 639 $kernel->yield(_got_dns_response => $response) if $response; 640 return; 641} 642 643# open the connection 644sub _do_connect { 645 my ($kernel, $self, $session) = @_[KERNEL, OBJECT, SESSION]; 646 my $domain = AF_INET; 647 648 # Disconnect if we're already logged into a server. 649 $kernel->call($session => 'quit') if $self->{socket}; 650 651 if ($self->{socks_proxy} && !$self->{socks_port}) { 652 $self->{socks_port} = 1080; 653 } 654 655 for my $address (qw(socks_proxy proxy server resolved_server use_localaddr)) { 656 next if !$self->{$address} || !_ip_is_ipv6( $self->{$address} ); 657 if (!$GOT_SOCKET6) { 658 warn "IPv6 address specified for '$address' but Socket6 not found\n"; 659 return; 660 } 661 $domain = AF_INET6; 662 } 663 664 $self->{socketfactory} = POE::Wheel::SocketFactory->new( 665 SocketDomain => $domain, 666 SocketType => SOCK_STREAM, 667 SocketProtocol => 'tcp', 668 RemoteAddress => $self->{socks_proxy} || $self->{proxy} || $self->{resolved_server} || $self->{server}, 669 RemotePort => $self->{socks_port} || $self->{proxyport} || $self->{port}, 670 SuccessEvent => '_sock_up', 671 FailureEvent => '_sock_failed', 672 ($self->{use_localaddr} ? (BindAddress => $self->{use_localaddr}) : ()), 673 ); 674 675 return; 676} 677 678# got response from POE::Component::Client::DNS 679sub _got_dns_response { 680 my ($kernel, $self, $response) = @_[KERNEL, OBJECT, ARG0]; 681 682 my $type = uc $response->{type}; 683 my $net_dns_packet = $response->{response}; 684 my $net_dns_errorstring = $response->{error}; 685 $self->{res_addresses} = [ ]; 686 687 if (!defined $net_dns_packet) { 688 $self->send_event(irc_socketerr => $net_dns_errorstring ); 689 return; 690 } 691 692 my @net_dns_answers = $net_dns_packet->answer; 693 694 for my $net_dns_answer (@net_dns_answers) { 695 next if $net_dns_answer->type !~ /^A/; 696 push @{ $self->{res_addresses} }, $net_dns_answer->rdatastr; 697 } 698 699 if ( !@{ $self->{res_addresses} } && $type eq 'AAAA') { 700 $kernel->yield(_resolve_addresses => $self->{server}, 'A'); 701 return; 702 } 703 704 if ( !@{ $self->{res_addresses} } ) { 705 $self->send_event(irc_socketerr => 'Unable to resolve ' . $self->{server}); 706 return; 707 } 708 709 if ( my $address = shift @{ $self->{res_addresses} } ) { 710 $self->{resolved_server} = $address; 711 $kernel->yield('_do_connect'); 712 return; 713 } 714 715 $self->send_event(irc_socketerr => 'Unable to resolve ' . $self->{server}); 716 return; 717} 718 719# Send a CTCP query or reply, with the same syntax as a PRIVMSG event. 720sub ctcp { 721 my ($kernel, $state, $self, $to) = @_[KERNEL, STATE, OBJECT, ARG0]; 722 my $message = join ' ', @_[ARG1 .. $#_]; 723 724 if (!defined $to || !defined $message) { 725 warn "The '$state' event requires two arguments\n"; 726 return; 727 } 728 729 # CTCP-quote the message text. 730 ($message) = @{$self->{ircd_compat}->put([ $message ])}; 731 732 # Should we send this as a CTCP request or reply? 733 $state = $state eq 'ctcpreply' ? 'notice' : 'privmsg'; 734 735 $kernel->yield($state, $to, $message); 736 return; 737} 738 739# The way /notify is implemented in IRC clients. 740sub ison { 741 my ($kernel, @nicks) = @_[KERNEL, ARG0 .. $#_]; 742 my $tmp = 'ISON'; 743 744 if (!@nicks) { 745 warn "The 'ison' event requires one or more nicknames\n"; 746 return; 747 } 748 749 # We can pass as many nicks as we want, as long as it's shorter than 750 # the maximum command length (510). If the list we get is too long, 751 # w'll break it into multiple ISON commands. 752 while (@nicks) { 753 my $nick = shift @nicks; 754 if (length($tmp) + length($nick) >= 509) { 755 $kernel->yield(sl_high => $tmp); 756 $tmp = 'ISON'; 757 } 758 $tmp .= " $nick"; 759 } 760 761 $kernel->yield(sl_high => $tmp); 762 return; 763} 764 765# Tell the IRC server to forcibly remove a user from a channel. 766sub kick { 767 my ($kernel, $chan, $nick) = @_[KERNEL, ARG0, ARG1]; 768 my $message = join '', @_[ARG2 .. $#_]; 769 770 if (!defined $chan || !defined $nick) { 771 warn "The 'kick' event requires at least two arguments\n"; 772 return; 773 } 774 775 $nick .= " :$message" if defined $message; 776 $kernel->yield(sl_high => "KICK $chan $nick"); 777 return; 778} 779 780# Tell the IRC server to forcibly remove a user from a channel. Freenode extension 781sub remove { 782 my ($kernel, $chan, $nick) = @_[KERNEL, ARG0, ARG1]; 783 my $message = join '', @_[ARG2 .. $#_]; 784 785 if (!defined $chan || !defined $nick) { 786 warn "The 'remove' event requires at least two arguments\n"; 787 return; 788 } 789 790 $nick .= " :$message" if defined $message; 791 $kernel->yield(sl_high => "REMOVE $chan $nick"); 792 return; 793} 794 795# Interact with NickServ 796sub nickserv { 797 my ($kernel, $self, $state) = @_[KERNEL, OBJECT, STATE]; 798 my $args = join ' ', @_[ARG0 .. $#_]; 799 800 my $command = 'NICKSERV'; 801 my $version = $self->server_version(); 802 $command = 'NS' if defined $version && $version =~ /ratbox/i; 803 $command .= " $args" if defined $args; 804 805 $kernel->yield(sl_high => $command); 806 return; 807} 808 809# Set up a new IRC component. Deprecated. 810sub new { 811 my ($package, $alias) = splice @_, 0, 2; 812 croak "$package options should be an even-sized list" if @_ & 1; 813 my %options = @_; 814 815 if (!defined $alias) { 816 croak 'Not enough arguments to POE::Component::IRC::new()'; 817 } 818 819 carp "Use of ${package}->new() is deprecated, please use spawn()"; 820 821 my $self = $package->spawn ( alias => $alias, options => \%options ); 822 return $self; 823} 824 825# Set up a new IRC component. New interface. 826sub spawn { 827 my ($package) = shift; 828 croak "$package requires an even number of arguments" if @_ & 1; 829 my %params = @_; 830 831 $params{ lc $_ } = delete $params{$_} for keys %params; 832 delete $params{options} if ref $params{options} ne 'HASH'; 833 834 my $self = bless { }, $package; 835 $self->_create(); 836 837 if ($ENV{POCOIRC_DEBUG}) { 838 $params{debug} = 1; 839 $params{plugin_debug} = 1; 840 } 841 842 my $options = delete $params{options}; 843 my $alias = delete $params{alias}; 844 my $plugin_debug = delete $params{plugin_debug}; 845 846 $self->_syndicator_init( 847 prefix => 'irc_', 848 reg_prefix => 'PCI_', 849 types => [SERVER => 'S', USER => 'U'], 850 alias => $alias, 851 register_signal => 'POCOIRC_REGISTER', 852 shutdown_signal => 'POCOIRC_SHUTDOWN', 853 object_states => [ 854 $self => delete $self->{OBJECT_STATES_HASHREF}, 855 $self => delete $self->{OBJECT_STATES_ARRAYREF}, 856 ], 857 ($plugin_debug ? (debug => 1) : () ), 858 (ref $options eq 'HASH' ? ( options => $options ) : ()), 859 ); 860 861 $params{spawned} = 1; 862 $self->_configure(\%params); 863 864 if (!$params{nodns} && $GOT_CLIENT_DNS && !$self->{resolver}) { 865 $self->{resolver} = POE::Component::Client::DNS->spawn( 866 Alias => 'resolver' . $self->session_id() 867 ); 868 $self->{mydns} = 1; 869 } 870 871 return $self; 872} 873 874# The handler for all IRC commands that take no arguments. 875sub noargs { 876 my ($kernel, $state, $arg) = @_[KERNEL, STATE, ARG0]; 877 my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI]; 878 879 if (defined $arg) { 880 warn "The '$state' event takes no arguments\n"; 881 return; 882 } 883 884 $state = uc $state; 885 $kernel->yield(sl_prioritized => $pri, $state); 886 return; 887} 888 889# The handler for commands that take one required and two optional arguments. 890sub oneandtwoopt { 891 my ($kernel, $state) = @_[KERNEL, STATE]; 892 my $arg = join '', @_[ARG0 .. $#_]; 893 my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI]; 894 895 $state = 'connect' if $state eq 'sconnect'; 896 $state = uc $state; 897 if (defined $arg) { 898 $arg = ':' . $arg if $arg =~ /\x20/; 899 $state .= " $arg"; 900 } 901 902 $kernel->yield(sl_prioritized => $pri, $state); 903 return; 904} 905 906# The handler for commands that take at least one optional argument. 907sub oneoptarg { 908 my ($kernel, $state) = @_[KERNEL, STATE]; 909 my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI]; 910 $state = uc $state; 911 912 if (defined $_[ARG0]) { 913 my $arg = join '', @_[ARG0 .. $#_]; 914 $arg = ':' . $arg if $arg =~ /\x20/; 915 $state .= " $arg"; 916 } 917 918 $kernel->yield(sl_prioritized => $pri, $state); 919 return; 920} 921 922# The handler for commands which take one required and one optional argument. 923sub oneortwo { 924 my ($kernel, $state, $one) = @_[KERNEL, STATE, ARG0]; 925 my $two = join '', @_[ARG1 .. $#_]; 926 my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI]; 927 928 if (!defined $one) { 929 warn "The '$state' event requires at least one argument\n"; 930 return; 931 } 932 933 $state = uc( $state ) . " $one"; 934 $state .= " $two" if defined $two; 935 $kernel->yield(sl_prioritized => $pri, $state); 936 return; 937} 938 939# Handler for commands that take exactly one argument. 940sub onlyonearg { 941 my ($kernel, $state) = @_[KERNEL, STATE]; 942 my $arg = join '', @_[ARG0 .. $#_]; 943 my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI]; 944 945 if (!defined $arg) { 946 warn "The '$state' event requires one argument\n"; 947 return; 948 } 949 950 $state = uc $state; 951 $arg = ':' . $arg if $arg =~ /\x20/; 952 $state .= " $arg"; 953 $kernel->yield(sl_prioritized => $pri, $state); 954 return; 955} 956 957# Handler for commands that take exactly two arguments. 958sub onlytwoargs { 959 my ($kernel, $state, $one) = @_[KERNEL, STATE, ARG0]; 960 my ($two) = join '', @_[ARG1 .. $#_]; 961 my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI]; 962 963 if (!defined $one || !defined $two) { 964 warn "The '$state' event requires two arguments\n"; 965 return; 966 } 967 968 $state = uc $state; 969 $two = ':' . $two if $two =~ /\x20/; 970 $state .= " $one $two"; 971 $kernel->yield(sl_prioritized => $pri, $state); 972 return; 973} 974 975# Handler for privmsg or notice events. 976sub privandnotice { 977 my ($kernel, $state, $to, $msg) = @_[KERNEL, STATE, ARG0, ARG1]; 978 my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI]; 979 980 $state =~ s/privmsglo/privmsg/; 981 $state =~ s/privmsghi/privmsg/; 982 $state =~ s/noticelo/notice/; 983 $state =~ s/noticehi/notice/; 984 985 if (!defined $to || !defined $msg) { 986 warn "The '$state' event requires two arguments\n"; 987 return; 988 } 989 990 $to = join ',', @$to if ref $to eq 'ARRAY'; 991 $state = uc $state; 992 993 $kernel->yield(sl_prioritized => $pri, "$state $to :$msg"); 994 return; 995} 996 997# Tell the IRC session to go away. 998sub shutdown { 999 my ($kernel, $self, $sender, $session) = @_[KERNEL, OBJECT, SENDER, SESSION]; 1000 return if $self->{_shutdown}; 1001 $self->{_shutdown} = $sender->ID(); 1002 1003 if ($self->logged_in()) { 1004 my ($msg, $timeout) = @_[ARG0, ARG1]; 1005 $msg = '' if !defined $msg; 1006 $timeout = 5 if !defined $timeout; 1007 $msg = ":$msg" if $msg =~ /\x20/; 1008 my $cmd = "QUIT $msg"; 1009 $kernel->call($session => sl_high => $cmd); 1010 $kernel->delay('_quit_timeout', $timeout); 1011 $self->{_waiting} = 1; 1012 } 1013 elsif ($self->connected()) { 1014 $self->disconnect(); 1015 } 1016 else { 1017 $self->_shutdown(); 1018 } 1019 1020 return; 1021} 1022 1023sub _quit_timeout { 1024 my ($self) = $_[OBJECT]; 1025 $self->disconnect(); 1026 return; 1027} 1028 1029sub _shutdown { 1030 my ($self) = @_; 1031 1032 $self->_syndicator_destroy($self->{_shutdown}); 1033 delete $self->{$_} for qw(socketfactory dcc wheelmap); 1034 $self->{resolver}->shutdown() if $self->{resolver} && $self->{mydns}; 1035 return; 1036} 1037 1038# Send a line of login-priority IRC output. These are things which 1039# must go first. 1040sub sl_login { 1041 my ($kernel, $self) = @_[KERNEL, OBJECT]; 1042 my $arg = join ' ', @_[ARG0 .. $#_]; 1043 $kernel->yield(sl_prioritized => PRI_LOGIN, $arg ); 1044 return; 1045} 1046 1047# Send a line of high-priority IRC output. Things like channel/user 1048# modes, kick messages, and whatever. 1049sub sl_high { 1050 my ($kernel, $self) = @_[KERNEL, OBJECT]; 1051 my $arg = join ' ', @_[ARG0 .. $#_]; 1052 $kernel->yield(sl_prioritized => PRI_HIGH, $arg ); 1053 return; 1054} 1055 1056# Send a line of normal-priority IRC output to the server. PRIVMSG 1057# and other random chatter. Uses sl() for compatibility with existing 1058# code. 1059sub sl { 1060 my ($kernel, $self) = @_[KERNEL, OBJECT]; 1061 my $arg = join ' ', @_[ARG0 .. $#_]; 1062 $kernel->yield(sl_prioritized => PRI_NORMAL, $arg ); 1063 return; 1064} 1065 1066# Prioritized sl(). This keeps the queue ordered by priority, low to 1067# high in the UNIX tradition. It also throttles transmission 1068# following the hybrid ircd's algorithm, so you can't accidentally 1069# flood yourself off. Thanks to Raistlin for explaining how ircd 1070# throttles messages. 1071sub sl_prioritized { 1072 my ($kernel, $self, $priority, @args) = @_[KERNEL, OBJECT, ARG0, ARG1]; 1073 1074 if (my ($event) = $args[0] =~ /^(\w+)/ ) { 1075 # Let the plugin system process this 1076 return 1 if $self->send_user_event($event, \@args) == PCI_EAT_ALL; 1077 } 1078 else { 1079 warn "Unable to extract the event name from '$args[0]'\n"; 1080 } 1081 1082 my $msg = $args[0]; 1083 my $now = time(); 1084 $self->{send_time} = $now if $self->{send_time} < $now; 1085 1086 # if we find a newline in the message, take that to be the end of it 1087 $msg =~ s/[\015\012].*//s; 1088 1089 if (bytes::length($msg) > $self->{msg_length} - bytes::length($self->nick_name())) { 1090 $msg = bytes::substr($msg, 0, $self->{msg_length} - bytes::length($self->nick_name())); 1091 } 1092 1093 if (!$self->{flood} && @{ $self->{send_queue} }) { 1094 my $i = @{ $self->{send_queue} }; 1095 $i-- while ($i && $priority < $self->{send_queue}->[$i-1]->[MSG_PRI]); 1096 splice( @{ $self->{send_queue} }, $i, 0, [ $priority, $msg ] ); 1097 } 1098 elsif ( !$self->{flood} && $self->{send_time} - $now >= 10 1099 || !defined $self->{socket} ) { 1100 push( @{$self->{send_queue}}, [ $priority, $msg ] ); 1101 $kernel->delay( sl_delayed => $self->{send_time} - $now - 10 ); 1102 } 1103 else { 1104 warn ">>> $msg\n" if $self->{debug}; 1105 $self->send_event(irc_raw_out => $msg) if $self->{raw}; 1106 $self->{send_time} += 2 + length($msg) / 120; 1107 $self->{socket}->put($msg); 1108 } 1109 1110 return; 1111} 1112 1113# Send delayed lines to the ircd. We manage a virtual "send time" 1114# that progresses into the future based on hybrid ircd's rules every 1115# time a message is sent. Once we find it ten or more seconds into 1116# the future, we wait for the realtime clock to catch up. 1117sub sl_delayed { 1118 my ($kernel, $self) = @_[KERNEL, OBJECT]; 1119 1120 return if !defined $self->{socket}; 1121 1122 my $now = time(); 1123 $self->{send_time} = $now if $self->{send_time} < $now; 1124 1125 while (@{ $self->{send_queue} } && ($self->{send_time} - $now < 10)) { 1126 my $arg = (shift @{$self->{send_queue}})->[MSG_TEXT]; 1127 warn ">>> $arg\n" if $self->{debug}; 1128 $self->send_event(irc_raw_out => $arg) if $self->{raw}; 1129 $self->{send_time} += 2 + length($arg) / 120; 1130 $self->{socket}->put($arg); 1131 } 1132 1133 if (@{ $self->{send_queue} }) { 1134 $kernel->delay( sl_delayed => $self->{send_time} - $now - 10 ); 1135 } 1136 1137 return; 1138} 1139 1140# The handler for commands which have N arguments, separated by spaces. 1141sub spacesep { 1142 my ($kernel, $state) = @_[KERNEL, STATE]; 1143 my $args = join ' ', @_[ARG0 .. $#_]; 1144 my $pri = $_[OBJECT]->{IRC_CMDS}->{$state}->[CMD_PRI]; 1145 1146 $state = uc $state; 1147 $state .= " $args" if defined $args; 1148 $kernel->yield(sl_prioritized => $pri, $state ); 1149 return; 1150} 1151 1152# Set or query the current topic on a channel. 1153sub topic { 1154 my ($kernel, $chan, @args) = @_[KERNEL, ARG0..$#_]; 1155 my $topic; 1156 $topic = join '', @args if @args; 1157 1158 if (defined $topic) { 1159 $chan .= " :"; 1160 $chan .= $topic if length $topic; 1161 } 1162 1163 $kernel->yield(sl_prioritized => PRI_NORMAL, "TOPIC $chan"); 1164 return; 1165} 1166 1167# Asks the IRC server for some random information about particular nicks. 1168sub userhost { 1169 my ($kernel, @nicks) = @_[KERNEL, ARG0 .. $#_]; 1170 1171 if (!@nicks) { 1172 warn "The 'userhost' event requires at least one nickname\n"; 1173 return; 1174 } 1175 1176 # According to the RFC, you can only send 5 nicks at a time. 1177 while (@nicks) { 1178 $kernel->yield( 1179 'sl_prioritized', 1180 PRI_HIGH, 1181 'USERHOST ' . join(' ', splice(@nicks, 0, 5)), 1182 ); 1183 } 1184 1185 return; 1186} 1187 1188# Non-event methods 1189 1190sub server { 1191 my ($self) = @_; 1192 return $self->{server}; 1193} 1194 1195sub port { 1196 my ($self) = @_; 1197 return $self->{port}; 1198} 1199 1200sub server_name { 1201 my ($self) = @_; 1202 return $self->{INFO}{ServerName}; 1203} 1204 1205sub server_version { 1206 my ($self) = @_; 1207 return $self->{INFO}{ServerVersion}; 1208} 1209 1210sub localaddr { 1211 my ($self) = @_; 1212 return $self->{localaddr}; 1213} 1214 1215sub nick_name { 1216 my ($self) = @_; 1217 return $self->{INFO}{RealNick}; 1218} 1219 1220sub send_queue { 1221 my ($self) = @_; 1222 1223 if (defined $self->{send_queue} && ref $self->{send_queue} eq 'ARRAY' ) { 1224 return scalar @{ $self->{send_queue} }; 1225 } 1226 return; 1227} 1228 1229sub raw_events { 1230 my ($self, $value) = @_; 1231 return $self->{raw} if !defined $value; 1232 $self->{raw} = $value; 1233 return; 1234} 1235 1236sub connected { 1237 my ($self) = @_; 1238 return $self->{connected}; 1239} 1240 1241sub logged_in { 1242 my ($self) = @_; 1243 return 1 if $self->{INFO}{LoggedIn}; 1244 return; 1245} 1246 1247sub _compress_uplink { 1248 my ($self, $value) = @_; 1249 1250 return if !$GOT_ZLIB; 1251 return $self->{uplink} if !defined $value; 1252 1253 if ($value) { 1254 $self->{out_filter}->unshift( POE::Filter::Zlib::Stream->new() ) if !$self->{uplink}; 1255 $self->{uplink} = 1; 1256 } 1257 else { 1258 $self->{out_filter}->shift() if $self->{uplink}; 1259 $self->{uplink} = 0; 1260 } 1261 1262 return $self->{uplink}; 1263} 1264 1265sub _compress_downlink { 1266 my ($self, $value) = @_; 1267 1268 return if !$GOT_ZLIB; 1269 return $self->{downlink} if !defined $value; 1270 1271 if ($value) { 1272 $self->{srv_filter}->unshift( POE::Filter::Zlib::Stream->new() ) if !$self->{downlink}; 1273 $self->{downlink} = 1; 1274 } 1275 else { 1276 $self->{srv_filter}->shift() if $self->{uplink}; 1277 $self->{downlink} = 0; 1278 } 1279 1280 return $self->{downlink}; 1281} 1282 1283sub S_001 { 1284 my ($self, $irc) = splice @_, 0, 2; 1285 $self->{INFO}{ServerName} = ${ $_[0] }; 1286 $self->{INFO}{LoggedIn} = 1; 1287 return PCI_EAT_NONE; 1288} 1289 1290sub S_004 { 1291 my ($self, $irc) = splice @_, 0, 2; 1292 my $args = ${ $_[2] }; 1293 $self->{INFO}{ServerVersion} = $args->[1]; 1294 return PCI_EAT_NONE; 1295} 1296 1297sub S_error { 1298 my ($self, $irc) = splice @_, 0, 2; 1299 $self->{INFO}{LoggedIn} = 0; 1300 return PCI_EAT_NONE; 1301} 1302 1303sub S_disconnected { 1304 my ($self, $irc) = splice @_, 0, 2; 1305 $self->{INFO}{LoggedIn} = 0; 1306 1307 if ($self->{_waiting}) { 1308 $poe_kernel->delay('_quit_timeout'); 1309 delete $self->{_waiting}; 1310 } 1311 1312 $self->_shutdown() if $self->{_shutdown}; 1313 return PCI_EAT_NONE; 1314} 1315 1316sub S_shutdown { 1317 my ($self, $irc) = splice @_, 0, 2; 1318 $self->{INFO}{LoggedIn} = 0; 1319 return PCI_EAT_NONE; 1320} 1321 1322# Automatically replies to a PING from the server. Do not confuse this 1323# with CTCP PINGs, which are a wholly different animal that evolved 1324# much later on the technological timeline. 1325sub S_ping { 1326 my ($self, $irc) = splice @_, 0, 2; 1327 my $arg = ${ $_[0] }; 1328 $irc->yield(sl_login => "PONG :$arg"); 1329 return PCI_EAT_NONE; 1330} 1331 1332# NICK messages for the purposes of determining our current nickname 1333sub S_nick { 1334 my ($self, $irc) = splice @_, 0, 2; 1335 my $nick = ( split /!/, ${ $_[0] } )[0]; 1336 my $new = ${ $_[1] }; 1337 $self->{INFO}{RealNick} = $new if ( $nick eq $self->{INFO}{RealNick} ); 1338 return PCI_EAT_NONE; 1339} 1340 1341# tell POE::Filter::IRC::Compat to handle IDENTIFY-MSG 1342sub S_290 { 1343 my ($self, $irc) = splice @_, 0, 2; 1344 my $text = ${ $_[1] }; 1345 $self->{ircd_compat}->identifymsg(1) if $text eq 'IDENTIFY-MSG'; 1346 return PCI_EAT_NONE; 1347} 1348 1349sub S_cap { 1350 my ($self, $irc) = splice @_, 0, 2; 1351 my $cmd = ${ $_[0] }; 1352 1353 if ($cmd eq 'ACK') { 1354 my $list = ${ $_[1] } eq '*' ? ${ $_[2] } : ${ $_[1] }; 1355 my @enabled = split / /, $list; 1356 1357 if (grep { $_ =~ /^=?identify-msg$/ } @enabled) { 1358 $self->{ircd_compat}->identifymsg(1); 1359 } 1360 if (grep { $_ =~ /^-identify-msg$/ } @enabled) { 1361 $self->{ircd_compat}->identifymsg(0); 1362 } 1363 } 1364 return PCI_EAT_NONE; 1365} 1366 1367sub S_isupport { 1368 my ($self, $irc) = splice @_, 0, 2; 1369 my $isupport = ${ $_[0] }; 1370 $self->{ircd_compat}->chantypes( $isupport->isupport('CHANTYPES') || [ '#', '&' ] ); 1371 $irc->yield(sl_login => 'CAPAB IDENTIFY-MSG') if $isupport->isupport('CAPAB'); 1372 $irc->yield(sl_login => 'PROTOCTL NAMESX') if $isupport->isupport('NAMESX'); 1373 $irc->yield(sl_login => 'PROTOCTL UHNAMES') if $isupport->isupport('UHNAMES'); 1374 return PCI_EAT_NONE; 1375} 1376 1377# accesses the ISupport plugin 1378sub isupport { 1379 my ($self, @args) = @_; 1380 return $self->{isupport}->isupport(@args); 1381} 1382 1383sub isupport_dump_keys { 1384 return $_[0]->{isupport}->isupport_dump_keys(); 1385} 1386 1387sub resolver { 1388 return $_[0]->{resolver}; 1389} 1390 1391sub _ip_get_version { 1392 my ($ip) = @_; 1393 return if !defined $ip; 1394 1395 # If the address does not contain any ':', maybe it's IPv4 1396 return 4 if $ip !~ /:/ && _ip_is_ipv4($ip); 1397 1398 # Is it IPv6 ? 1399 return 6 if _ip_is_ipv6($ip); 1400 1401 return; 1402} 1403 1404sub _ip_is_ipv4 { 1405 my ($ip) = @_; 1406 return if !defined $ip; 1407 1408 # Check for invalid chars 1409 return if $ip !~ /^[\d\.]+$/; 1410 return if $ip =~ /^\./; 1411 return if $ip =~ /\.$/; 1412 1413 # Single Numbers are considered to be IPv4 1414 return 1 if $ip =~ /^(\d+)$/ && $1 < 256; 1415 1416 # Count quads 1417 my $n = ($ip =~ tr/\./\./); 1418 1419 # IPv4 must have from 1 to 4 quads 1420 return if $n <= 0 || $n > 4; 1421 1422 # Check for empty quads 1423 return if $ip =~ /\.\./; 1424 1425 for my $quad (split /\./, $ip) { 1426 # Check for invalid quads 1427 return if $quad < 0 || $quad >= 256; 1428 } 1429 return 1; 1430} 1431 1432sub _ip_is_ipv6 { 1433 my ($ip) = @_; 1434 return if !defined $ip; 1435 1436 # Count octets 1437 my $n = ($ip =~ tr/:/:/); 1438 return if ($n <= 0 || $n >= 8); 1439 1440 # $k is a counter 1441 my $k; 1442 1443 for my $octet (split /:/, $ip) { 1444 $k++; 1445 1446 # Empty octet ? 1447 next if $octet eq ''; 1448 1449 # Normal v6 octet ? 1450 next if $octet =~ /^[a-f\d]{1,4}$/i; 1451 1452 # Last octet - is it IPv4 ? 1453 if ($k == $n + 1) { 1454 next if (ip_is_ipv4($octet)); 1455 } 1456 1457 return; 1458 } 1459 1460 # Does the IP address start with : ? 1461 return if $ip =~ m/^:[^:]/; 1462 1463 # Does the IP address finish with : ? 1464 return if $ip =~ m/[^:]:$/; 1465 1466 # Does the IP address have more than one '::' pattern ? 1467 return if $ip =~ s/:(?=:)//g > 1; 1468 1469 return 1; 1470} 1471 14721; 1473 1474=encoding utf8 1475 1476=head1 NAME 1477 1478POE::Component::IRC - A fully event-driven IRC client module 1479 1480=head1 SYNOPSIS 1481 1482 # A simple Rot13 'encryption' bot 1483 1484 use strict; 1485 use warnings; 1486 use POE qw(Component::IRC); 1487 1488 my $nickname = 'Flibble' . $$; 1489 my $ircname = 'Flibble the Sailor Bot'; 1490 my $server = 'irc.perl.org'; 1491 1492 my @channels = ('#Blah', '#Foo', '#Bar'); 1493 1494 # We create a new PoCo-IRC object 1495 my $irc = POE::Component::IRC->spawn( 1496 nick => $nickname, 1497 ircname => $ircname, 1498 server => $server, 1499 ) or die "Oh noooo! $!"; 1500 1501 POE::Session->create( 1502 package_states => [ 1503 main => [ qw(_default _start irc_001 irc_public) ], 1504 ], 1505 heap => { irc => $irc }, 1506 ); 1507 1508 $poe_kernel->run(); 1509 1510 sub _start { 1511 my $heap = $_[HEAP]; 1512 1513 # retrieve our component's object from the heap where we stashed it 1514 my $irc = $heap->{irc}; 1515 1516 $irc->yield( register => 'all' ); 1517 $irc->yield( connect => { } ); 1518 return; 1519 } 1520 1521 sub irc_001 { 1522 my $sender = $_[SENDER]; 1523 1524 # Since this is an irc_* event, we can get the component's object by 1525 # accessing the heap of the sender. Then we register and connect to the 1526 # specified server. 1527 my $irc = $sender->get_heap(); 1528 1529 print "Connected to ", $irc->server_name(), "\n"; 1530 1531 # we join our channels 1532 $irc->yield( join => $_ ) for @channels; 1533 return; 1534 } 1535 1536 sub irc_public { 1537 my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2]; 1538 my $nick = ( split /!/, $who )[0]; 1539 my $channel = $where->[0]; 1540 1541 if ( my ($rot13) = $what =~ /^rot13 (.+)/ ) { 1542 $rot13 =~ tr[a-zA-Z][n-za-mN-ZA-M]; 1543 $irc->yield( privmsg => $channel => "$nick: $rot13" ); 1544 } 1545 return; 1546 } 1547 1548 # We registered for all events, this will produce some debug info. 1549 sub _default { 1550 my ($event, $args) = @_[ARG0 .. $#_]; 1551 my @output = ( "$event: " ); 1552 1553 for my $arg (@$args) { 1554 if ( ref $arg eq 'ARRAY' ) { 1555 push( @output, '[' . join(', ', @$arg ) . ']' ); 1556 } 1557 else { 1558 push ( @output, "'$arg'" ); 1559 } 1560 } 1561 print join ' ', @output, "\n"; 1562 return; 1563 } 1564 1565=head1 DESCRIPTION 1566 1567POE::Component::IRC is a POE component (who'd have guessed?) which 1568acts as an easily controllable IRC client for your other POE 1569components and sessions. You create an IRC component and tell it what 1570events your session cares about and where to connect to, and it sends 1571back interesting IRC events when they happen. You make the client do 1572things by sending it events. That's all there is to it. Cool, no? 1573 1574[Note that using this module requires some familiarity with the 1575details of the IRC protocol. I'd advise you to read up on the gory 1576details of RFC 1459 (L<http://www.faqs.org/rfcs/rfc1459.html>) before you 1577get started. Keep the list of server numeric codes handy while you 1578program. Needless to say, you'll also need a good working knowledge of 1579POE, or this document will be of very little use to you.] 1580 1581The POE::Component::IRC distribution has a F<docs/> folder with a collection of 1582salient documentation including the pertinent RFCs. 1583 1584POE::Component::IRC consists of a POE::Session that manages the IRC connection 1585and dispatches C<irc_> prefixed events to interested sessions and 1586an object that can be used to access additional information using methods. 1587 1588Sessions register their interest in receiving C<irc_> events by sending 1589L<C<register>|/register> to the component. One would usually do this in 1590your C<_start> handler. Your session will continue to receive events until 1591you L<C<unregister>|/unregister>. The component will continue to stay 1592around until you tell it not to with L<C<shutdown>|/shutdown>. 1593 1594The L<SYNOPSIS|/SYNOPSIS> demonstrates a fairly basic bot. 1595 1596See L<POE::Component::IRC::Cookbook|POE::Component::IRC::Cookbook> for more 1597examples. 1598 1599=head2 Useful subclasses 1600 1601Included with POE::Component::IRC are a number of useful subclasses. As they 1602are subclasses they support all the methods, etc. documented here and have 1603additional methods and quirks which are documented separately: 1604 1605=over 4 1606 1607=item * L<POE::Component::IRC::State|POE::Component::IRC::State> 1608 1609POE::Component::IRC::State provides all the functionality of POE::Component::IRC 1610but also tracks IRC state entities such as nicks and channels. 1611 1612=item * L<POE::Component::IRC::Qnet|POE::Component::IRC::Qnet> 1613 1614POE::Component::IRC::Qnet is POE::Component::IRC tweaked for use on Quakenet IRC 1615network. 1616 1617=item * L<POE::Component::IRC::Qnet::State|POE::Component::IRC::Qnet::State> 1618 1619POE::Component::IRC::Qnet::State is a tweaked version of POE::Component::IRC::State 1620for use on the Quakenet IRC network. 1621 1622=back 1623 1624=head2 The Plugin system 1625 1626As of 3.7, PoCo-IRC sports a plugin system. The documentation for it can be 1627read by looking at L<POE::Component::IRC::Plugin|POE::Component::IRC::Plugin>. 1628That is not a subclass, just a placeholder for documentation! 1629 1630A number of useful plugins have made their way into the core distribution: 1631 1632=over 4 1633 1634=item * L<POE::Component::IRC::Plugin::DCC|POE::Component::IRC::Plugin::DCC> 1635 1636Provides DCC support. Loaded by default. 1637 1638=item * L<POE::Component::IRC::Plugin::AutoJoin|POE::Component::IRC::Plugin::AutoJoin> 1639 1640Keeps you on your favorite channels throughout reconnects and even kicks. 1641 1642=item * L<POE::Component::IRC::Plugin::Connector|POE::Component::IRC::Plugin::Connector> 1643 1644Glues an irc bot to an IRC network, i.e. deals with maintaining ircd connections. 1645 1646=item * L<POE::Component::IRC::Plugin::BotTraffic|POE::Component::IRC::Plugin::BotTraffic> 1647 1648Under normal circumstances irc bots do not normal the msgs and public msgs that 1649they generate themselves. This plugin enables you to handle those events. 1650 1651=item * L<POE::Component::IRC::Plugin::BotAddressed|POE::Component::IRC::Plugin::BotAddressed> 1652 1653Generates C<irc_bot_addressed> / C<irc_bot_mentioned> / C<irc_bot_mentioned_action> 1654events whenever your bot's name comes up in channel discussion. 1655 1656=item * L<POE::Component::IRC::Plugin::BotCommand|POE::Component::IRC::Plugin::BotCommand> 1657 1658Provides an easy way to handle commands issued to your bot. 1659 1660=item * L<POE::Component::IRC::Plugin::Console|POE::Component::IRC::Plugin::Console> 1661 1662See inside the component. See what events are being sent. Generate irc commands 1663manually. A TCP based console. 1664 1665=item * L<POE::Component::IRC::Plugin::FollowTail|POE::Component::IRC::Plugin::FollowTail> 1666 1667Follow the tail of an ever-growing file. 1668 1669=item * L<POE::Component::IRC::Plugin::Logger|POE::Component::IRC::Plugin::Logger> 1670 1671Log public and private messages to disk. 1672 1673=item * L<POE::Component::IRC::Plugin::NickServID|POE::Component::IRC::Plugin::NickServID> 1674 1675Identify with NickServ when needed. 1676 1677=item * L<POE::Component::IRC::Plugin::Proxy|POE::Component::IRC::Plugin::Proxy> 1678 1679A lightweight IRC proxy/bouncer. 1680 1681=item * L<POE::Component::IRC::Plugin::CTCP|POE::Component::IRC::Plugin::CTCP> 1682 1683Automagically generates replies to ctcp version, time and userinfo queries. 1684 1685=item * L<POE::Component::IRC::Plugin::PlugMan|POE::Component::IRC::Plugin::PlugMan> 1686 1687An experimental Plugin Manager plugin. 1688 1689=item * L<POE::Component::IRC::Plugin::NickReclaim|POE::Component::IRC::Plugin::NickReclaim> 1690 1691Automagically deals with your nickname being in use and reclaiming it. 1692 1693=item * L<POE::Component::IRC::Plugin::CycleEmpty|POE::Component::IRC::Plugin::CycleEmpty> 1694 1695Cycles (parts and rejoins) channels if they become empty and opless, in order 1696to gain ops. 1697 1698=back 1699 1700=head1 CONSTRUCTORS 1701 1702Both constructors return an object. The object is also available within 'irc_' 1703event handlers by using C<< $_[SENDER]->get_heap() >>. See also 1704L<C<register>|/register> and L<C<irc_registered>|/irc_registered>. 1705 1706=head2 C<spawn> 1707 1708Takes a number of arguments, all of which are optional. All the options 1709below may be supplied to the L<C<connect>|/connect> input event as well, 1710except for B<'alias'>, B<'options'>, B<'NoDNS'>, B<'debug'>, and 1711B<'plugin_debug'>. 1712 1713=over 4 1714 1715=item * B<'alias'>, a name (kernel alias) that this instance will be known 1716by; 1717 1718=item * B<'options'>, a hashref containing L<POE::Session|POE::Session> 1719options; 1720 1721=item * B<'Server'>, the server name; 1722 1723=item * B<'Port'>, the remote port number; 1724 1725=item * B<'Password'>, an optional password for restricted servers; 1726 1727=item * B<'Nick'>, your client's IRC nickname; 1728 1729=item * B<'Username'>, your client's username; 1730 1731=item * B<'Ircname'>, some cute comment or something. 1732 1733=item * B<'Bitmode'>, an integer representing your initial user modes set 1734in the USER command. See RFC 2812. If you do not set this, C<8> (+i) will 1735be used. 1736 1737=item * B<'UseSSL'>, set to some true value if you want to connect using 1738SSL. 1739 1740=item * B<'SSLCert'>, set to a SSL Certificate(PAM encoded) to connect using a client cert 1741 1742=item * B<'SSLKey'>, set to a SSL Key(PAM encoded) to connect using a client cert 1743 1744=item * B<'SSLCtx'>, set to a SSL Context to configure the SSL Connection 1745 1746The B<'SSLCert'> and B<'SSLKey'> both need to be specified. The B<'SSLCtx'> takes precedence specified. 1747 1748=item * B<'Raw'>, set to some true value to enable the component to send 1749L<C<irc_raw>|/irc_raw> and L<C<irc_raw_out>|/irc_raw_out> events. 1750 1751=item * B<'LocalAddr'>, which local IP address on a multihomed box to 1752connect as; 1753 1754=item * B<'LocalPort'>, the local TCP port to open your socket on; 1755 1756=item * B<'NoDNS'>, set this to 1 to disable DNS lookups using 1757PoCo-Client-DNS. (See note below). 1758 1759=item * B<'Flood'>, when true, it disables the component's flood 1760protection algorithms, allowing it to send messages to an IRC server at 1761full speed. Disconnects and k-lines are some common side effects of 1762flooding IRC servers, so care should be used when enabling this option. 1763Default is false. 1764 1765Two new attributes are B<'Proxy'> and B<'ProxyPort'> for sending your 1766=item * B<'Proxy'>, IP address or server name of a proxy server to use. 1767 1768=item * B<'ProxyPort'>, which tcp port on the proxy to connect to. 1769 1770=item * B<'NATAddr'>, what other clients see as your IP address. 1771 1772=item * B<'DCCPorts'>, an arrayref containing tcp ports that can be used 1773for DCC sends. 1774 1775=item * B<'Resolver'>, provide a L<POE::Component::Client::DNS|POE::Component::Client::DNS> object for the component to use. 1776 1777=item * B<'msg_length'>, the maximum length of IRC messages, in bytes. 1778Default is 450. The IRC component shortens all messages longer than this 1779value minus the length of your current nickname. IRC only allows raw 1780protocol lines messages that are 512 bytes or shorter, including the 1781trailing "\r\n". This is most relevant to long PRIVMSGs. The IRC component 1782can't be sure how long your user@host mask will be every time you send a 1783message, considering that most networks mangle the 'user' part and some 1784even replace the whole string (think FreeNode cloaks). If you have an 1785unusually long user@host mask you might want to decrease this value if 1786you're prone to sending long messages. Conversely, if you have an 1787unusually short one, you can increase this value if you want to be able to 1788send as long a message as possible. Be careful though, increase it too 1789much and the IRC server might disconnect you with a "Request too long" 1790message when you try to send a message that's too long. 1791 1792=item * B<'debug'>, if set to a true value causes the IRC component to 1793print every message sent to and from the server, as well as print some 1794warnings when it receives malformed messages. This option will be enabled 1795if the C<POCOIRC_DEBUG> environment variable is set to a true value. 1796 1797=item * B<'plugin_debug'>, set to some true value to print plugin debug 1798info, default 0. Plugins are processed inside an eval. When you enable 1799this option, you will be notified when (and why) a plugin raises an 1800exception. This option will be enabled if the C<POCOIRC_DEBUG> environment 1801variable is set to a true value. 1802 1803=item * B<'socks_proxy'>, specify a SOCKS4/SOCKS4a proxy to use. 1804 1805=item * B<'socks_port'>, the SOCKS port to use, defaults to 1080 if not 1806specified. 1807 1808=item * B<'socks_id'>, specify a SOCKS user_id. Default is none. 1809 1810=item * B<'useipv6'>, enable the use of IPv6 for connections. 1811 1812=item * B<'webirc'>, enable the use of WEBIRC to spoof host/IP. 1813You must have a WEBIRC password set up on the IRC server/network (so will 1814only work for servers which trust you to spoof the IP & host the connection 1815is from) - value should be a hashref containing keys C<pass>, C<user>, 1816C<host> and C<ip>. 1817 1818=back 1819 1820C<spawn> will supply reasonable defaults for any of these attributes 1821which are missing, so don't feel obliged to write them all out. 1822 1823If the component finds that L<POE::Component::Client::DNS|POE::Component::Client::DNS> 1824is installed it will use that to resolve the server name passed. Disable 1825this behaviour if you like, by passing: C<< NoDNS => 1 >>. 1826 1827IRC traffic through a proxy server. B<'Proxy'>'s value should be the IP 1828address or server name of the proxy. B<'ProxyPort'>'s value should be the 1829port on the proxy to connect to. L<C<connect>|/connect> will default 1830to using the I<actual> IRC server's port if you provide a proxy but omit 1831the proxy's port. These are for HTTP Proxies. See B<'socks_proxy'> for 1832SOCKS4 and SOCKS4a support. 1833 1834For those people who run bots behind firewalls and/or Network Address 1835Translation there are two additional attributes for DCC. B<'DCCPorts'>, 1836is an arrayref of ports to use when initiating DCC connections. 1837B<'NATAddr'>, is the NAT'ed IP address that your bot is hidden behind, 1838this is sent whenever you do DCC. 1839 1840SSL support requires L<POE::Component::SSLify|POE::Component::SSLify>, as 1841well as an IRC server that supports SSL connections. If you're missing 1842POE::Component::SSLify, specifying B<'UseSSL'> will do nothing. The 1843default is to not try to use SSL. 1844 1845B<'Resolver'>, requires a L<POE::Component::Client::DNS|POE::Component::Client::DNS> 1846object. Useful when spawning multiple poco-irc sessions, saves the 1847overhead of multiple dns sessions. 1848 1849B<'NoDNS'> has different results depending on whether it is set with 1850L<C<spawn>|/spawn> or L<C<connect>|/connect>. Setting it with 1851C<spawn>, disables the creation of the POE::Component::Client::DNS 1852completely. Setting it with L<C<connect>|/connect> on the other hand 1853allows the PoCo-Client-DNS session to be spawned, but will disable 1854any dns lookups using it. 1855 1856SOCKS4 proxy support is provided by B<'socks_proxy'>, B<'socks_port'> and 1857B<'socks_id'> parameters. If something goes wrong with the SOCKS connection 1858you should get a warning on STDERR. This is fairly experimental currently. 1859 1860IPv6 support is available for connecting to IPv6 enabled ircds (it won't 1861work for DCC though). To enable it, specify B<'useipv6'>. Perl >=5.14 or 1862L<Socket6|Socket6> (for older Perls) is required. If you that and 1863L<POE::Component::Client::DNS|POE::Component::Client::DNS> installed and 1864specify a hostname that resolves to an IPv6 address then IPv6 will be used. 1865If you specify an ipv6 B<'localaddr'> then IPv6 will be used. 1866 1867=head2 C<new> 1868 1869This method is deprecated. See the L<C<spawn>|/spawn> method instead. 1870The first argument should be a name (kernel alias) which this new 1871connection will be known by. Optionally takes more arguments (see 1872L<C<spawn>|/spawn> as name/value pairs. Returns a POE::Component::IRC 1873object. :) 1874 1875B<Note:> Use of this method will generate a warning. There are currently no 1876plans to make it die() >;] 1877 1878=head1 METHODS 1879 1880=head2 Information 1881 1882=head3 C<server> 1883 1884Takes no arguments. Returns the server host we are currently connected to 1885(or trying to connect to). 1886 1887=head3 C<port> 1888 1889Takes no arguments. Returns the server port we are currently connected to 1890(or trying to connect to). 1891 1892=head3 C<server_name> 1893 1894Takes no arguments. Returns the name of the IRC server that the component 1895is currently connected to. 1896 1897=head3 C<server_version> 1898 1899Takes no arguments. Returns the IRC server version. 1900 1901=head3 C<nick_name> 1902 1903Takes no arguments. Returns a scalar containing the current nickname that the 1904bot is using. 1905 1906=head3 C<localaddr> 1907 1908Takes no arguments. Returns the IP address being used. 1909 1910=head3 C<send_queue> 1911 1912The component provides anti-flood throttling. This method takes no arguments 1913and returns a scalar representing the number of messages that are queued up 1914waiting for dispatch to the irc server. 1915 1916=head3 C<logged_in> 1917 1918Takes no arguments. Returns true or false depending on whether the IRC 1919component is logged into an IRC network. 1920 1921=head3 C<connected> 1922 1923Takes no arguments. Returns true or false depending on whether the component's 1924socket is currently connected. 1925 1926=head3 C<disconnect> 1927 1928Takes no arguments. Terminates the socket connection disgracefully >;o] 1929 1930=head3 C<isupport> 1931 1932Takes one argument, a server capability to query. Returns C<undef> on failure 1933or a value representing the applicable capability. A full list of capabilities 1934is available at L<http://www.irc.org/tech_docs/005.html>. 1935 1936=head3 C<isupport_dump_keys> 1937 1938Takes no arguments, returns a list of the available server capabilities keys, 1939which can be used with L<C<isupport>|/isupport>. 1940 1941=head3 C<resolver> 1942 1943Returns a reference to the L<POE::Component::Client::DNS|POE::Component::Client::DNS> 1944object that is internally created by the component. 1945 1946=head2 Events 1947 1948=head3 C<session_id> 1949 1950I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/session_id>> 1951 1952Takes no arguments. Returns the ID of the component's session. Ideal for posting 1953events to the component. 1954 1955 $kernel->post($irc->session_id() => 'mode' => $channel => '+o' => $dude); 1956 1957=head3 C<session_alias> 1958 1959I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/session_alias>> 1960 1961Takes no arguments. Returns the session alias that has been set through 1962L<C<spawn>|/spawn>'s B<'alias'> argument. 1963 1964=head3 C<raw_events> 1965 1966With no arguments, returns true or false depending on whether 1967L<C<irc_raw>|/irc_raw> and L<C<irc_raw_out>|/irc_raw_out> events are being generated 1968or not. Provide a true or false argument to enable or disable this feature 1969accordingly. 1970 1971=head3 C<yield> 1972 1973I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/yield>> 1974 1975This method provides an alternative object based means of posting events to the 1976component. First argument is the event to post, following arguments are sent as 1977arguments to the resultant post. 1978 1979 $irc->yield(mode => $channel => '+o' => $dude); 1980 1981=head3 C<call> 1982 1983I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/call>> 1984 1985This method provides an alternative object based means of calling events to the 1986component. First argument is the event to call, following arguments are sent as 1987arguments to the resultant 1988call. 1989 1990 $irc->call(mode => $channel => '+o' => $dude); 1991 1992=head3 C<delay> 1993 1994I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/delay>> 1995 1996This method provides a way of posting delayed events to the component. The 1997first argument is an arrayref consisting of the delayed command to post and 1998any command arguments. The second argument is the time in seconds that one 1999wishes to delay the command being posted. 2000 2001 my $alarm_id = $irc->delay( [ mode => $channel => '+o' => $dude ], 60 ); 2002 2003Returns an alarm ID that can be used with L<C<delay_remove>|/delay_remove> 2004to cancel the delayed event. This will be undefined if something went wrong. 2005 2006=head3 C<delay_remove> 2007 2008I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/delay_remove>> 2009 2010This method removes a previously scheduled delayed event from the component. 2011Takes one argument, the C<alarm_id> that was returned by a 2012L<C<delay>|/delay> method call. 2013 2014 my $arrayref = $irc->delay_remove( $alarm_id ); 2015 2016Returns an arrayref that was originally requested to be delayed. 2017 2018=head3 C<send_event> 2019 2020I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/send_event>> 2021 2022Sends an event through the component's event handling system. These will get 2023processed by plugins then by registered sessions. First argument is the event 2024name, followed by any parameters for that event. 2025 2026=head3 C<send_event_next> 2027 2028I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/send_event_next>> 2029 2030This sends an event right after the one that's currently being processed. 2031Useful if you want to generate some event which is directly related to 2032another event so you want them to appear together. This method can only be 2033called when POE::Component::IRC is processing an event, e.g. from one of your 2034event handlers. Takes the same arguments as L<C<send_event>|/send_event>. 2035 2036=head3 C<send_event_now> 2037 2038I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/send_event_now>> 2039 2040This will send an event to be processed immediately. This means that if an 2041event is currently being processed and there are plugins or sessions which 2042will receive it after you do, then an event sent with C<send_event_now> will 2043be received by those plugins/sessions I<before> the current event. Takes the 2044same arguments as L<C<send_event>|/send_event>. 2045 2046=head2 Plugins 2047 2048=head3 C<pipeline> 2049 2050I<Inherited from L<Object::Pluggable|Object::Pluggable/pipeline>> 2051 2052Returns the L<Object::Pluggable::Pipeline|Object::Pluggable::Pipeline> 2053object. 2054 2055=head3 C<plugin_add> 2056 2057I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_add>> 2058 2059Accepts two arguments: 2060 2061 The alias for the plugin 2062 The actual plugin object 2063 Any number of extra arguments 2064 2065The alias is there for the user to refer to it, as it is possible to have 2066multiple plugins of the same kind active in one Object::Pluggable object. 2067 2068This method goes through the pipeline's C<push()> method, which will call 2069C<< $plugin->plugin_register($pluggable, @args) >>. 2070 2071Returns the number of plugins now in the pipeline if plugin was initialized, 2072C<undef>/an empty list if not. 2073 2074=head3 C<plugin_del> 2075 2076I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_del>> 2077 2078Accepts the following arguments: 2079 2080 The alias for the plugin or the plugin object itself 2081 Any number of extra arguments 2082 2083This method goes through the pipeline's C<remove()> method, which will call 2084C<< $plugin->plugin_unregister($pluggable, @args) >>. 2085 2086Returns the plugin object if the plugin was removed, C<undef>/an empty list 2087if not. 2088 2089=head3 C<plugin_get> 2090 2091I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_get>> 2092 2093Accepts the following arguments: 2094 2095 The alias for the plugin 2096 2097This method goes through the pipeline's C<get()> method. 2098 2099Returns the plugin object if it was found, C<undef>/an empty list if not. 2100 2101=head3 C<plugin_list> 2102 2103I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_list>> 2104 2105Takes no arguments. 2106 2107Returns a hashref of plugin objects, keyed on alias, or an empty list if 2108there are no plugins loaded. 2109 2110=head3 C<plugin_order> 2111 2112I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_order>> 2113 2114Takes no arguments. 2115 2116Returns an arrayref of plugin objects, in the order which they are 2117encountered in the pipeline. 2118 2119=head3 C<plugin_register> 2120 2121I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_register>> 2122 2123Accepts the following arguments: 2124 2125 The plugin object 2126 The type of the hook (the hook types are specified with _pluggable_init()'s 'types') 2127 The event name[s] to watch 2128 2129The event names can be as many as possible, or an arrayref. They correspond 2130to the prefixed events and naturally, arbitrary events too. 2131 2132You do not need to supply events with the prefix in front of them, just the 2133names. 2134 2135It is possible to register for all events by specifying 'all' as an event. 2136 2137Returns 1 if everything checked out fine, C<undef>/an empty list if something 2138is seriously wrong. 2139 2140=head3 C<plugin_unregister> 2141 2142I<Inherited from L<Object::Pluggable|Object::Pluggable/plugin_unregister>> 2143 2144Accepts the following arguments: 2145 2146 The plugin object 2147 The type of the hook (the hook types are specified with _pluggable_init()'s 'types') 2148 The event name[s] to unwatch 2149 2150The event names can be as many as possible, or an arrayref. They correspond 2151to the prefixed events and naturally, arbitrary events too. 2152 2153You do not need to supply events with the prefix in front of them, just the 2154names. 2155 2156It is possible to register for all events by specifying 'all' as an event. 2157 2158Returns 1 if all the event name[s] was unregistered, undef if some was not 2159found. 2160 2161=head1 INPUT EVENTS 2162 2163How to talk to your new IRC component... here's the events we'll accept. 2164These are events that are posted to the component, either via 2165C<< $poe_kernel->post() >> or via the object method L<C<yield>|/yield>. 2166 2167So the following would be functionally equivalent: 2168 2169 sub irc_001 { 2170 my ($kernel,$sender) = @_[KERNEL,SENDER]; 2171 my $irc = $sender->get_heap(); # obtain the poco's object 2172 2173 $irc->yield( privmsg => 'foo' => 'Howdy!' ); 2174 $kernel->post( $sender => privmsg => 'foo' => 'Howdy!' ); 2175 $kernel->post( $irc->session_id() => privmsg => 'foo' => 'Howdy!' ); 2176 $kernel->post( $irc->session_alias() => privmsg => 'foo' => 'Howdy!' ); 2177 2178 return; 2179 } 2180 2181=head2 Important Commands 2182 2183=head3 C<register> 2184 2185I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/register>> 2186 2187Takes N arguments: a list of event names that your session wants to 2188listen for, minus the C<irc_> prefix. So, for instance, if you just 2189want a bot that keeps track of which people are on a channel, you'll 2190need to listen for JOINs, PARTs, QUITs, and KICKs to people on the 2191channel you're in. You'd tell POE::Component::IRC that you want those 2192events by saying this: 2193 2194 $kernel->post('my client', 'register', qw(join part quit kick)); 2195 2196Then, whenever people enter or leave a channel your bot is on (forcibly 2197or not), your session will receive events with names like 2198L<C<irc_join>|/irc_join>, L<C<irc_kick>|/irc_kick>, etc., 2199which you can use to update a list of people on the channel. 2200 2201Registering for B<'all'> will cause it to send all IRC-related events to 2202you; this is the easiest way to handle it. See the test script for an 2203example. 2204 2205Registering will generate an L<C<irc_registered>|/irc_registered> 2206event that your session can trap. C<ARG0> is the components object. Useful 2207if you want to bolt PoCo-IRC's new features such as Plugins into a bot 2208coded to the older deprecated API. If you are using the new API, ignore this :) 2209 2210Registering with multiple component sessions can be tricky, especially if 2211one wants to marry up sessions/objects, etc. Check the L<SIGNALS|/SIGNALS> 2212section for an alternative method of registering with multiple poco-ircs. 2213 2214Starting with version 4.96, if you spawn the component from inside another POE 2215session, the component will automatically register that session as wanting 2216B<'all'> irc events. That session will receive an 2217L<C<irc_registered>|/irc_registered> event indicating that the component 2218is up and ready to go. 2219 2220=head3 C<unregister> 2221 2222I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/unregister>> 2223 2224Takes N arguments: a list of event names which you I<don't> want to 2225receive. If you've previously done a L<C<register>|/register> 2226for a particular event which you no longer care about, this event will 2227tell the IRC connection to stop sending them to you. (If you haven't, it just 2228ignores you. No big deal.) 2229 2230If you have registered with 'all', attempting to unregister individual 2231events such as 'mode', etc. will not work. This is a 'feature'. 2232 2233=head3 C<connect> 2234 2235Takes one argument: a hash reference of attributes for the new connection, 2236see L<C<spawn>|/spawn> for details. This event tells the IRC client to 2237connect to a new/different server. If it has a connection already open, it'll 2238close it gracefully before reconnecting. 2239 2240=head3 C<ctcp> and C<ctcpreply> 2241 2242Sends a CTCP query or response to the nick(s) or channel(s) which you 2243specify. Takes 2 arguments: the nick or channel to send a message to 2244(use an array reference here to specify multiple recipients), and the 2245plain text of the message to send (the CTCP quoting will be handled 2246for you). The "/me" command in popular IRC clients is actually a CTCP action. 2247 2248 # Doing a /me 2249 $irc->yield(ctcp => $channel => 'ACTION dances.'); 2250 2251=head3 C<join> 2252 2253Tells your IRC client to join a single channel of your choice. Takes 2254at least one arg: the channel name (required) and the channel key 2255(optional, for password-protected channels). 2256 2257=head3 C<kick> 2258 2259Tell the IRC server to forcibly evict a user from a particular 2260channel. Takes at least 2 arguments: a channel name, the nick of the 2261user to boot, and an optional witty message to show them as they sail 2262out the door. 2263 2264=head3 C<remove> 2265 2266Tell the IRC server to forcibly evict a user from a particular 2267channel. Takes at least 2 arguments: a channel name, the nick of the 2268user to boot, and an optional witty message to show them as they sail 2269out the door. Similar to KICK but does an enforced PART instead. Not 2270supported by all servers. 2271 2272=head3 C<mode> 2273 2274Request a mode change on a particular channel or user. Takes at least 2275one argument: the mode changes to effect, as a single string (e.g. 2276"#mychan +sm-p+o"), and any number of optional operands to the mode changes 2277(nicks, hostmasks, channel keys, whatever.) Or just pass them all as one 2278big string and it'll still work, whatever. I regret that I haven't the 2279patience now to write a detailed explanation, but serious IRC users know 2280the details anyhow. 2281 2282=head3 C<nick> 2283 2284Allows you to change your nickname. Takes exactly one argument: the 2285new username that you'd like to be known as. 2286 2287=head3 C<nickserv> 2288 2289Talks to NickServ, on networks which have it. Takes any number of 2290arguments. 2291 2292=head3 C<notice> 2293 2294Sends a NOTICE message to the nick(s) or channel(s) which you 2295specify. Takes 2 arguments: the nick or channel to send a notice to 2296(use an array reference here to specify multiple recipients), and the 2297text of the notice to send. 2298 2299=head3 C<part> 2300 2301Tell your IRC client to leave the channels which you pass to it. Takes 2302any number of arguments: channel names to depart from. If the last argument 2303doesn't begin with a channel name identifier or contains a space character, 2304it will be treated as a PART message and dealt with accordingly. 2305 2306=head3 C<privmsg> 2307 2308Sends a public or private message to the nick(s) or channel(s) which 2309you specify. Takes 2 arguments: the nick or channel to send a message 2310to (use an array reference here to specify multiple recipients), and 2311the text of the message to send. 2312 2313Have a look at the constants in L<IRC::Utils|IRC::Utils> if you would 2314like to use formatting and color codes in your messages. 2315 2316 $irc->yield('primvsg', '#mychannel', 'Hello there'); 2317 2318 # same, but with a green Hello 2319 use IRC::Utils qw(GREEN NORMAL); 2320 $irc->yield('primvsg', '#mychannel', GREEN.'Hello'.NORMAL.' there'); 2321 2322=head3 C<quit> 2323 2324Tells the IRC server to disconnect you. Takes one optional argument: 2325some clever, witty string that other users in your channels will see 2326as you leave. You can expect to get an 2327L<C<irc_disconnected>|/irc_disconnected> event shortly after sending this. 2328 2329=head3 C<shutdown> 2330 2331By default, POE::Component::IRC sessions never go away. Even after 2332they're disconnected, they're still sitting around in the background, 2333waiting for you to call L<C<connect>|/connect> on them again to 2334reconnect. (Whether this behavior is the Right Thing is doubtful, but I 2335don't want to break backwards compatibility at this point.) You can send 2336the IRC session a C<shutdown> event manually to make it delete itself. 2337 2338If you are logged into an IRC server, C<shutdown> first will send a quit 2339message and wait to be disconnected. It will wait for up to 5 seconds before 2340forcibly disconnecting from the IRC server. If you provide an argument, that 2341will be used as the QUIT message. If you provide two arguments, the second 2342one will be used as the timeout (in seconds). 2343 2344Terminating multiple components can be tricky. Check the L<SIGNALS|/SIGNALS> 2345section for a method of shutting down multiple poco-ircs. 2346 2347=head3 C<topic> 2348 2349Retrieves or sets the topic for particular channel. If called with just 2350the channel name as an argument, it will ask the server to return the 2351current topic. If called with the channel name and a string, it will 2352set the channel topic to that string. Supply an empty string to unset a 2353channel topic. 2354 2355=head3 C<debug> 2356 2357Takes one argument: 0 to turn debugging off or 1 to turn debugging on. 2358This flips the debugging flag in L<POE::Filter::IRCD|POE::Filter::IRCD>, 2359L<POE::Filter::IRC::Compat|POE::Filter::IRC::Compat>, and 2360POE::Component::IRC. This has the same effect as setting Debug in 2361L<C<spawn>|/spawn> or L<C<connect>|/connect>. 2362 2363=head2 Not-So-Important Commands 2364 2365=head3 C<admin> 2366 2367Asks your server who your friendly neighborhood server administrators 2368are. If you prefer, you can pass it a server name to query, instead of 2369asking the server you're currently on. 2370 2371=head3 C<away> 2372 2373When sent with an argument (a message describig where you went), the 2374server will note that you're now away from your machine or otherwise 2375preoccupied, and pass your message along to anyone who tries to 2376communicate with you. When sent without arguments, it tells the server 2377that you're back and paying attention. 2378 2379=head3 C<cap> 2380 2381Used to query/enable/disable IRC protocol capabilities. Takes any number of 2382arguments. 2383 2384=head3 C<dcc*> 2385 2386See the L<DCC plugin|POE::Component::IRC::Plugin/COMMANDS> (loaded by default) 2387documentation for DCC-related commands. 2388 2389=head3 C<info> 2390 2391Basically the same as the L<C<version>|/version> command, except that the 2392server is permitted to return any information about itself that it thinks is 2393relevant. There's some nice, specific standards-writing for ya, eh? 2394 2395=head3 C<invite> 2396 2397Invites another user onto an invite-only channel. Takes 2 arguments: 2398the nick of the user you wish to admit, and the name of the channel to 2399invite them to. 2400 2401=head3 C<ison> 2402 2403Asks the IRC server which users out of a list of nicknames are 2404currently online. Takes any number of arguments: a list of nicknames 2405to query the IRC server about. 2406 2407=head3 C<links> 2408 2409Asks the server for a list of servers connected to the IRC 2410network. Takes two optional arguments, which I'm too lazy to document 2411here, so all you would-be linklooker writers should probably go dig up 2412the RFC. 2413 2414=head3 C<list> 2415 2416Asks the server for a list of visible channels and their topics. Takes 2417any number of optional arguments: names of channels to get topic 2418information for. If called without any channel names, it'll list every 2419visible channel on the IRC network. This is usually a really big list, 2420so don't do this often. 2421 2422=head3 C<motd> 2423 2424Request the server's "Message of the Day", a document which typically 2425contains stuff like the server's acceptable use policy and admin 2426contact email addresses, et cetera. Normally you'll automatically 2427receive this when you log into a server, but if you want it again, 2428here's how to do it. If you'd like to get the MOTD for a server other 2429than the one you're logged into, pass it the server's hostname as an 2430argument; otherwise, no arguments. 2431 2432=head3 C<names> 2433 2434Asks the server for a list of nicknames on particular channels. Takes 2435any number of arguments: names of channels to get lists of users 2436for. If called without any channel names, it'll tell you the nicks of 2437everyone on the IRC network. This is a really big list, so don't do 2438this much. 2439 2440=head3 C<quote> 2441 2442Sends a raw line of text to the server. Takes one argument: a string 2443of a raw IRC command to send to the server. It is more optimal to use 2444the events this module supplies instead of writing raw IRC commands 2445yourself. 2446 2447=head3 C<stats> 2448 2449Returns some information about a server. Kinda complicated and not 2450terribly commonly used, so look it up in the RFC if you're 2451curious. Takes as many arguments as you please. 2452 2453=head3 C<time> 2454 2455Asks the server what time it thinks it is, which it will return in a 2456human-readable form. Takes one optional argument: a server name to 2457query. If not supplied, defaults to current server. 2458 2459=head3 C<trace> 2460 2461If you pass a server name or nick along with this request, it asks the 2462server for the list of servers in between you and the thing you 2463mentioned. If sent with no arguments, it will show you all the servers 2464which are connected to your current server. 2465 2466=head3 C<users> 2467 2468Asks the server how many users are logged into it. Defaults to the 2469server you're currently logged into; however, you can pass a server 2470name as the first argument to query some other machine instead. 2471 2472=head3 C<version> 2473 2474Asks the server about the version of ircd that it's running. Takes one 2475optional argument: a server name to query. If not supplied, defaults 2476to current server. 2477 2478=head3 C<who> 2479 2480Lists the logged-on users matching a particular channel name, hostname, 2481nickname, or what-have-you. Takes one optional argument: a string for 2482it to search for. Wildcards are allowed; in the absence of this 2483argument, it will return everyone who's currently logged in (bad 2484move). Tack an "o" on the end if you want to list only IRCops, as per 2485the RFC. 2486 2487=head3 C<whois> 2488 2489Queries the IRC server for detailed information about a particular 2490user. Takes any number of arguments: nicknames or hostmasks to ask for 2491information about. As of version 3.2, you will receive an 2492L<C<irc_whois>|/irc_whois> event in addition to the usual numeric 2493responses. See below for details. 2494 2495=head3 C<whowas> 2496 2497Asks the server for information about nickname which is no longer 2498connected. Takes at least one argument: a nickname to look up (no 2499wildcards allowed), the optional maximum number of history entries to 2500return, and the optional server hostname to query. As of version 3.2, 2501you will receive an L<C<irc_whowas>|/irc_whowas> event in addition 2502to the usual numeric responses. See below for details. 2503 2504=head3 C<ping> and C<pong> 2505 2506Included for completeness sake. The component will deal with ponging to 2507pings automatically. Don't worry about it. 2508 2509=head2 Purely Esoteric Commands 2510 2511=head3 C<die> 2512 2513Tells the IRC server you're connect to, to terminate. Only useful for 2514IRCops, thank goodness. Takes no arguments. 2515 2516=head3 C<locops> 2517 2518Opers-only command. This one sends a message to all currently 2519logged-on local-opers (+l). This option is specific to EFNet. 2520 2521=head3 C<oper> 2522 2523In the exceedingly unlikely event that you happen to be an IRC 2524operator, you can use this command to authenticate with your IRC 2525server. Takes 2 arguments: your username and your password. 2526 2527=head3 C<operwall> 2528 2529Opers-only command. This one sends a message to all currently 2530logged-on global opers. This option is specific to EFNet. 2531 2532=head3 C<rehash> 2533 2534Tells the IRC server you're connected to, to rehash its configuration 2535files. Only useful for IRCops. Takes no arguments. 2536 2537=head3 C<restart> 2538 2539Tells the IRC server you're connected to, to shut down and restart itself. 2540Only useful for IRCops, thank goodness. Takes no arguments. 2541 2542=head3 C<sconnect> 2543 2544Tells one IRC server (which you have operator status on) to connect to 2545another. This is actually the CONNECT command, but I already had an 2546event called L<C<connect>|/connect>, so too bad. Takes the args 2547you'd expect: a server to connect to, an optional port to connect on, 2548and an optional remote server to connect with, instead of the one you're 2549currently on. 2550 2551=head3 C<squit> 2552 2553Operator-only command used to disconnect server links. Takes two arguments, 2554the server to disconnect and a message explaining your action. 2555 2556=head3 C<summon> 2557 2558Don't even ask. 2559 2560=head3 C<servlist> 2561 2562Lists the currently connected services on the network that are visible to you. 2563Takes two optional arguments, a mask for matching service names against, and 2564a service type. 2565 2566=head3 C<squery> 2567 2568Sends a message to a service. Takes the same arguments as 2569L<C<privmsg>|/privmsg>. 2570 2571=head3 C<userhost> 2572 2573Asks the IRC server for information about particular nicknames. (The 2574RFC doesn't define exactly what this is supposed to return.) Takes any 2575number of arguments: the nicknames to look up. 2576 2577=head3 C<wallops> 2578 2579Another opers-only command. This one sends a message to all currently 2580logged-on opers (and +w users); sort of a mass PA system for the IRC 2581server administrators. Takes one argument: some clever, witty message 2582to send. 2583 2584=head1 OUTPUT EVENTS 2585 2586The events you will receive (or can ask to receive) from your running 2587IRC component. Note that all incoming event names your session will 2588receive are prefixed by C<irc_>, to inhibit event namespace pollution. 2589 2590If you wish, you can ask the client to send you every event it 2591generates. Simply register for the event name "all". This is a lot 2592easier than writing a huge list of things you specifically want to 2593listen for. 2594 2595FIXME: I'd really like to classify these somewhat ("basic", "oper", "ctcp", 2596"dcc", "raw" or some such), and I'd welcome suggestions for ways to make 2597this easier on the user, if you can think of some. 2598 2599In your event handlers, C<$_[SENDER]> is the particular component session that 2600sent you the event. C<< $_[SENDER]->get_heap() >> will retrieve the component's 2601object. Useful if you want on-the-fly access to the object and its methods. 2602 2603=head2 Important Events 2604 2605=head3 C<irc_registered> 2606 2607I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/syndicator_registered>> 2608 2609Sent once to the requesting session on registration (see 2610L<C<register>|/register>). C<ARG0> is a reference tothe component's object. 2611 2612=head3 C<irc_shutdown> 2613 2614I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/syndicator_shutdown>> 2615 2616Sent to all registered sessions when the component has been asked to 2617L<C<shutdown>|/shutdown>. C<ARG0> will be the session ID of the requesting 2618session. 2619 2620=head3 C<irc_connected> 2621 2622The IRC component will send an C<irc_connected> event as soon as it 2623establishes a connection to an IRC server, before attempting to log 2624in. C<ARG0> is the server name. 2625 2626B<NOTE:> When you get an C<irc_connected> event, this doesn't mean you 2627can start sending commands to the server yet. Wait until you receive 2628an L<C<irc_001>|/All numeric events> event (the server welcome message) 2629before actually sending anything back to the server. 2630 2631=head3 C<irc_ctcp> 2632 2633C<irc_ctcp> events are generated upon receipt of CTCP messages, in addition to 2634the C<irc_ctcp_*> events mentioned below. They are identical in every way to 2635these, with one difference: instead of the * being in the method name, it 2636is prepended to the argument list. For example, if someone types C</ctcp 2637Flibble foo bar>, an C<irc_ctcp> event will be sent with B<'foo'> as C<ARG0>, 2638and the rest as given below. 2639 2640It is not recommended that you register for both C<irc_ctcp> and C<irc_ctcp_*> 2641events, since they will both be fired and presumably cause duplication. 2642 2643=head3 C<irc_ctcp_*> 2644 2645C<irc_ctcp_whatever> events are generated upon receipt of CTCP messages. 2646For instance, receiving a CTCP PING request generates an C<irc_ctcp_ping> 2647event, CTCP ACTION (produced by typing "/me" in most IRC clients) 2648generates an C<irc_ctcp_action> event, blah blah, so on and so forth. C<ARG0> 2649is the nick!hostmask of the sender. C<ARG1> is the channel/recipient 2650name(s). C<ARG2> is the text of the CTCP message. On servers supporting the 2651IDENTIFY-MSG feature (e.g. FreeNode), CTCP ACTIONs will have C<ARG3>, which 2652will be C<1> if the sender has identified with NickServ, C<0> otherwise. 2653 2654Note that DCCs are handled separately -- see the 2655L<DCC plugin|POE::Component::IRC::Plugin::DCC>. 2656 2657=head3 C<irc_ctcpreply_*> 2658 2659C<irc_ctcpreply_whatever> messages are just like C<irc_ctcp_whatever> 2660messages, described above, except that they're generated when a response 2661to one of your CTCP queries comes back. They have the same arguments and 2662such as C<irc_ctcp_*> events. 2663 2664=head3 C<irc_disconnected> 2665 2666The counterpart to L<C<irc_connected>|/irc_connected>, sent whenever 2667a socket connection to an IRC server closes down (whether intentionally or 2668unintentionally). C<ARG0> is the server name. 2669 2670=head3 C<irc_error> 2671 2672You get this whenever the server sends you an ERROR message. Expect 2673this to usually be accompanied by the sudden dropping of your 2674connection. C<ARG0> is the server's explanation of the error. 2675 2676=head3 C<irc_join> 2677 2678Sent whenever someone joins a channel that you're on. C<ARG0> is the 2679person's nick!hostmask. C<ARG1> is the channel name. 2680 2681=head3 C<irc_invite> 2682 2683Sent whenever someone offers you an invitation to another channel. C<ARG0> 2684is the person's nick!hostmask. C<ARG1> is the name of the channel they want 2685you to join. 2686 2687=head3 C<irc_kick> 2688 2689Sent whenever someone gets booted off a channel that you're on. C<ARG0> 2690is the kicker's nick!hostmask. C<ARG1> is the channel name. C<ARG2> is the 2691nick of the unfortunate kickee. C<ARG3> is the explanation string for the 2692kick. 2693 2694=head3 C<irc_mode> 2695 2696Sent whenever someone changes a channel mode in your presence, or when 2697you change your own user mode. C<ARG0> is the nick!hostmask of that 2698someone. C<ARG1> is the channel it affects (or your nick, if it's a user 2699mode change). C<ARG2> is the mode string (i.e., "+o-b"). The rest of the 2700args (C<ARG3 .. $#_>) are the operands to the mode string (nicks, 2701hostmasks, channel keys, whatever). 2702 2703=head3 C<irc_msg> 2704 2705Sent whenever you receive a PRIVMSG command that was addressed to you 2706privately. C<ARG0> is the nick!hostmask of the sender. C<ARG1> is an array 2707reference containing the nick(s) of the recipients. C<ARG2> is the text 2708of the message. On servers supporting the IDENTIFY-MSG feature (e.g. 2709FreeNode), there will be an additional argument, C<ARG3>, which will be 2710C<1> if the sender has identified with NickServ, C<0> otherwise. 2711 2712=head3 C<irc_nick> 2713 2714Sent whenever you, or someone around you, changes nicks. C<ARG0> is the 2715nick!hostmask of the changer. C<ARG1> is the new nick that they changed 2716to. 2717 2718=head3 C<irc_notice> 2719 2720Sent whenever you receive a NOTICE command. C<ARG0> is the nick!hostmask 2721of the sender. C<ARG1> is an array reference containing the nick(s) or 2722channel name(s) of the recipients. C<ARG2> is the text of the NOTICE 2723message. 2724 2725=head3 C<irc_part> 2726 2727Sent whenever someone leaves a channel that you're on. C<ARG0> is the 2728person's nick!hostmask. C<ARG1> is the channel name. C<ARG2> is the part 2729message. 2730 2731=head3 C<irc_public> 2732 2733Sent whenever you receive a PRIVMSG command that was sent to a channel. 2734C<ARG0> is the nick!hostmask of the sender. C<ARG1> is an array 2735reference containing the channel name(s) of the recipients. C<ARG2> is the 2736text of the message. On servers supporting the IDENTIFY-MSG feature (e.g. 2737FreeNode), there will be an additional argument, C<ARG3>, which will be 2738C<1> if the sender has identified with NickServ, C<0> otherwise. 2739 2740=head3 C<irc_quit> 2741 2742Sent whenever someone on a channel with you quits IRC (or gets 2743KILLed). C<ARG0> is the nick!hostmask of the person in question. C<ARG1> is 2744the clever, witty message they left behind on the way out. 2745 2746=head3 C<irc_socketerr> 2747 2748Sent when a connection couldn't be established to the IRC server. C<ARG0> 2749is probably some vague and/or misleading reason for what failed. 2750 2751=head3 C<irc_topic> 2752 2753Sent when a channel topic is set or unset. C<ARG0> is the nick!hostmask of the 2754sender. C<ARG1> is the channel affected. C<ARG2> will be either: a string if the 2755topic is being set; or a zero-length string (i.e. '') if the topic is being 2756unset. Note: replies to queries about what a channel topic *is* 2757(i.e. TOPIC #channel), are returned as numerics, not with this event. 2758 2759=head3 C<irc_whois> 2760 2761Sent in response to a WHOIS query. C<ARG0> is a hashref, with the following 2762keys: 2763 2764=over 4 2765 2766=item * B<'nick'>, the users nickname; 2767 2768=item * B<'user'>, the users username; 2769 2770=item * B<'host'>, their hostname; 2771 2772=item * B<'real'>, their real name; 2773 2774=item * B<'idle'>, their idle time in seconds; 2775 2776=item * B<'signon'>, the epoch time they signed on (will be undef if ircd 2777does not support this); 2778 2779=item * B<'channels'>, an arrayref listing visible channels they are on, 2780the channel is prefixed with '@','+','%' depending on whether they have 2781+o +v or +h; 2782 2783=item * B<'server'>, their server (might not be useful on some networks); 2784 2785=item * B<'oper'>, whether they are an IRCop, contains the IRC operator 2786string if they are, undef if they aren't. 2787 2788=item * B<'actually'>, some ircds report the user's actual ip address, 2789that'll be here; 2790 2791=item * B<'identified'>. if the user has identified with NICKSERV 2792(ircu, seven, Plexus) 2793 2794=item * B<'modes'>, a string describing the user's modes (Rizon) 2795 2796=back 2797 2798=head3 C<irc_whowas> 2799 2800Similar to the above, except some keys will be missing. 2801 2802=head3 C<irc_raw> 2803 2804Enabled by passing C<< Raw => 1 >> to L<C<spawn>|/spawn> or 2805L<C<connect>|/connect>, or by calling L<C<raw_events>|/raw_events> with 2806a true argument. C<ARG0> is the raw IRC string received by the component from 2807the IRC server, before it has been mangled by filters and such like. 2808 2809=head3 C<irc_raw_out> 2810 2811Enabled by passing C<< Raw => 1 >> to L<C<spawn>|/spawn> or 2812L<C<connect>|/connect>, or by calling L<C<raw_events>|/raw_events> with 2813a true argument. C<ARG0> is the raw IRC string sent by the component to the 2814the IRC server. 2815 2816=head3 C<irc_isupport> 2817 2818Emitted by the first event after an L<C<irc_005>|/All numeric events>, to 2819indicate that isupport information has been gathered. C<ARG0> is the 2820L<POE::Component::IRC::Plugin::ISupport|POE::Component::IRC::Plugin::ISupport> 2821object. 2822 2823=head3 C<irc_socks_failed> 2824 2825Emitted whenever we fail to connect successfully to a SOCKS server or the 2826SOCKS server is not actually a SOCKS server. C<ARG0> will be some vague reason 2827as to what went wrong. Hopefully. 2828 2829=head3 C<irc_socks_rejected> 2830 2831Emitted whenever a SOCKS connection is rejected by a SOCKS server. C<ARG0> is 2832the SOCKS code, C<ARG1> the SOCKS server address, C<ARG2> the SOCKS port and 2833C<ARG3> the SOCKS user id (if defined). 2834 2835=head3 C<irc_plugin_add> 2836 2837I<Inherited from L<Object::Pluggable|Object::Pluggable/_pluggable_event>> 2838 2839Emitted whenever a new plugin is added to the pipeline. C<ARG0> is the 2840plugin alias. C<ARG1> is the plugin object. 2841 2842=head3 C<irc_plugin_del> 2843 2844I<Inherited from L<Object::Pluggable|Object::Pluggable/_pluggable_event>> 2845 2846Emitted whenever a plugin is removed from the pipeline. C<ARG0> is the 2847plugin alias. C<ARG1> is the plugin object. 2848 2849=head3 C<irc_plugin_error> 2850 2851I<Inherited from L<Object::Pluggable|Object::Pluggable/_pluggable_event>> 2852 2853Emitted when an error occurs while executing a plugin handler. C<ARG0> is 2854the error message. C<ARG1> is the plugin alias. C<ARG2> is the plugin object. 2855 2856=head2 Somewhat Less Important Events 2857 2858=head3 C<irc_cap> 2859 2860A reply from the server regarding protocol capabilities. C<ARG0> is the 2861CAP subcommand (e.g. 'LS'). C<ARG1> is the result of the subcommand, unless 2862this is a multi-part reply, in which case C<ARG1> is '*' and C<ARG2> contains 2863the result. 2864 2865=head3 C<irc_dcc_*> 2866 2867See the L<DCC plugin|POE::Component::IRC::Plugin/OUTPUT> (loaded by default) 2868documentation for DCC-related events. 2869 2870=head3 C<irc_ping> 2871 2872An event sent whenever the server sends a PING query to the 2873client. (Don't confuse this with a CTCP PING, which is another beast 2874entirely. If unclear, read the RFC.) Note that POE::Component::IRC will 2875automatically take care of sending the PONG response back to the 2876server for you, although you can still register to catch the event for 2877informational purposes. 2878 2879=head3 C<irc_snotice> 2880 2881A weird, non-RFC-compliant message from an IRC server. Usually sent during 2882to you during an authentication phase right after you connect, while the 2883server does a hostname lookup or similar tasks. C<ARG0> is the text of the 2884server's message. C<ARG1> is the target, which could be B<'*'> or B<'AUTH'> 2885or whatever. Servers vary as to whether these notices include a server name 2886as the sender, or no sender at all. C<ARG1> is the sender, if any. 2887 2888=head3 C<irc_delay_set> 2889 2890I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/syndicator_delay_set>> 2891 2892Emitted on a successful addition of a delayed event using the 2893L<C<delay>|/delay> method. C<ARG0> will be the alarm_id which can be used 2894later with L<C<delay_remove>|/delay_remove>. Subsequent parameters are 2895the arguments that were passed to L<C<delay>|/delay>. 2896 2897=head3 C<irc_delay_removed> 2898 2899I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/syndicator_delay_removed>> 2900 2901Emitted when a delayed command is successfully removed. C<ARG0> will be the 2902alarm_id that was removed. Subsequent parameters are the arguments that were 2903passed to L<C<delay>|/delay>. 2904 2905=head2 All numeric events 2906 2907Most messages from IRC servers are identified only by three-digit 2908numeric codes with undescriptive constant names like RPL_UMODEIS and 2909ERR_NOTOPLEVEL. (Actually, the list of codes in the RFC is kind of 2910out-of-date... the list in the back of Net::IRC::Event.pm is more 2911complete, and different IRC networks have different and incompatible 2912lists. Ack!) As an example, say you wanted to handle event 376 2913(RPL_ENDOFMOTD, which signals the end of the MOTD message). You'd 2914register for '376', and listen for C<irc_376> events. Simple, no? C<ARG0> 2915is the name of the server which sent the message. C<ARG1> is the text of 2916the message. C<ARG2> is an array reference of the parsed message, so there 2917is no need to parse C<ARG1> yourself. 2918 2919=head1 SIGNALS 2920 2921The component will handle a number of custom signals that you may send using 2922L<POE::Kernel|POE::Kernel>'s C<signal> method. 2923 2924=head2 C<POCOIRC_REGISTER> 2925 2926I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/SYNDICATOR_REGISTER>> 2927 2928Registering with multiple PoCo-IRC components has been a pita. Well, no more, 2929using the power of L<POE::Kernel|POE::Kernel> signals. 2930 2931If the component receives a C<POCOIRC_REGISTER> signal it'll register the 2932requesting session and trigger an L<C<irc_registered>|/irc_registered> 2933event. From that event one can get all the information necessary such as the 2934poco-irc object and the SENDER session to do whatever one needs to build a 2935poco-irc dispatch table. 2936 2937The way the signal handler in PoCo-IRC is written also supports sending the 2938C<POCOIRC_REGISTER> to multiple sessions simultaneously, by sending the signal 2939to the POE Kernel itself. 2940 2941Pass the signal your session, session ID or alias, and the IRC events (as 2942specified to L<C<register>|/register>). 2943 2944To register with multiple PoCo-IRCs one can do the following in your session's 2945_start handler: 2946 2947 sub _start { 2948 my ($kernel, $session) = @_[KERNEL, SESSION]; 2949 2950 # Registering with multiple pocoircs for 'all' IRC events 2951 $kernel->signal($kernel, 'POCOIRC_REGISTER', $session->ID(), 'all'); 2952 2953 return: 2954 } 2955 2956Each poco-irc will send your session an 2957L<C<irc_registered>|/irc_registered> event: 2958 2959 sub irc_registered { 2960 my ($kernel, $sender, $heap, $irc_object) = @_[KERNEL, SENDER, HEAP, ARG0]; 2961 2962 # Get the poco-irc session ID 2963 my $sender_id = $sender->ID(); 2964 2965 # Or it's alias 2966 my $poco_alias = $irc_object->session_alias(); 2967 2968 # Store it in our heap maybe 2969 $heap->{irc_objects}->{ $sender_id } = $irc_object; 2970 2971 # Make the poco connect 2972 $irc_object->yield(connect => { }); 2973 2974 return; 2975 } 2976 2977=head2 C<POCOIRC_SHUTDOWN> 2978 2979I<Inherited from L<POE::Component::Syndicator|POE::Component::Syndicator/SYNDICATOR_SHUTDOWN>> 2980 2981Telling multiple poco-ircs to shutdown was a pita as well. The same principle as 2982with registering applies to shutdown too. 2983 2984Send a C<POCOIRC_SHUTDOWN> to the POE Kernel to terminate all the active 2985poco-ircs simultaneously. 2986 2987 $poe_kernel->signal($poe_kernel, 'POCOIRC_SHUTDOWN'); 2988 2989Any additional parameters passed to the signal will become your quit messages 2990on each IRC network. 2991 2992=head1 ENCODING 2993 2994This can be an issue. Take a look at L<IRC::Utils' section|IRC::Utils/ENCODING> 2995on it. 2996 2997=head1 BUGS 2998 2999A few have turned up in the past and they are sure to again. Please use 3000L<http://rt.cpan.org/> to report any. Alternatively, email the current 3001maintainer. 3002 3003=head1 DEVELOPMENT 3004 3005You can find the latest source on github: 3006L<http://github.com/bingos/poe-component-irc> 3007 3008The project's developers usually hang out in the C<#poe> IRC channel on 3009irc.perl.org. Do drop us a line. 3010 3011=head1 MAINTAINERS 3012 3013Chris C<BinGOs> Williams <chris@bingosnet.co.uk> 3014 3015Hinrik E<Ouml>rn SigurE<eth>sson <hinrik.sig@gmail.com> 3016 3017=head1 AUTHOR 3018 3019Dennis Taylor. 3020 3021=head1 LICENCE 3022 3023Copyright (c) Dennis Taylor, Chris Williams and Hinrik E<Ouml>rn SigurE<eth>sson 3024 3025This module may be used, modified, and distributed under the same 3026terms as Perl itself. Please see the license that came with your Perl 3027distribution for details. 3028 3029=head1 MAD PROPS 3030 3031The maddest of mad props go out to Rocco "dngor" Caputo 3032<troc@netrus.net>, for inventing something as mind-bogglingly 3033cool as POE, and to Kevin "oznoid" Lenzo E<lt>lenzo@cs.cmu.eduE<gt>, 3034for being the attentive parent of our precocious little infobot on 3035#perl. 3036 3037Further props to a few of the studly bughunters who made this module not 3038suck: Abys <abys@web1-2-3.com>, Addi <addi@umich.edu>, ResDev 3039<ben@reser.org>, and Roderick <roderick@argon.org>. Woohoo! 3040 3041Kudos to Apocalypse, <apocal@cpan.org>, for the plugin system and to 3042Jeff 'japhy' Pinyan, <japhy@perlmonk.org>, for Pipeline. 3043 3044Thanks to the merry band of POE pixies from #PoE @ irc.perl.org, 3045including ( but not limited to ), ketas, ct, dec, integral, webfox, 3046immute, perigrin, paulv, alias. 3047 3048IP functions are shamelessly 'borrowed' from L<Net::IP|Net::IP> by Manuel 3049Valente 3050 3051Check out the Changes file for further contributors. 3052 3053=head1 SEE ALSO 3054 3055RFC 1459 L<http://www.faqs.org/rfcs/rfc1459.html> 3056 3057L<http://www.irchelp.org/>, 3058 3059L<http://poe.perl.org/>, 3060 3061L<http://www.infobot.org/>, 3062 3063Some good examples reside in the POE cookbook which has a whole section 3064devoted to IRC programming L<http://poe.perl.org/?POE_Cookbook>. 3065 3066The examples/ folder of this distribution. 3067 3068=cut 3069