#!/usr/bin/env perl use strict; use warnings; use POSIX qw(setuid setgid); use POE; use POE::Kernel; use POE::Component::IRC; use POE::Component::IRC::Plugin::NickServID; use config_file; use IRC::Utils qw(strip_color strip_formatting); use Module::Pluggable search_path => "plugin", instantiate => 'configure'; my $config_file = "bot.conf"; my %config = config_file::parse_config($config_file); $| = 1; my $current_nick = $config{nick}; # Hack: coerce into numeric type +$config{url_on}; +$config{url_len}; my @plugin_list = plugins("dummy", \%config); # New PoCo-IRC object my $irc = POE::Component::IRC->spawn( UseSSL => $config{usessl}, nick => $config{nick}, ircname => $config{ircname}, port => $config{port}, server => $config{server}, username => $config{username}, ) or die "Failed to create new PoCo-IRC: $!"; # Plugins $config{password} and $irc->plugin_add( 'NickServID', POE::Component::IRC::Plugin::NickServID->new( Password => $config{password} )); POE::Session->create( package_states => [ main => [ qw( _default _start irc_001 irc_kick irc_ctcp_action irc_public irc_msg irc_invite irc_nick irc_disconnected irc_error irc_socketerr) ], ], heap => { irc => $irc }, ); drop_priv(); $poe_kernel->run(); sub drop_priv { setgid($config{gid}) or die "Failed to setgid: $!\n"; setuid($config{uid}) or die "Failed to setuid: $!\n"; } sub _start { my $heap = $_[HEAP]; my $irc = $heap->{irc}; $irc->yield(register => 'all'); $irc->yield(connect => { }); return; } sub irc_001 { my $sender = $_[SENDER]; my $irc = $sender->get_heap(); print "Connected to server ", $irc->server_name(), "\n"; $irc->yield( join => $_ ) for @{$config{channels}}; return; } sub irc_nick { my ($who, $new_nick) = @_[ARG0 .. ARG1]; my $oldnick = (split /!/, $who)[0]; if ($oldnick eq $current_nick) { $current_nick = $new_nick; } return; } sub irc_kick { my ($kicker, $channel, $kickee, $reason) = @_[ARG0 .. ARG3]; if ($kickee eq $current_nick) { print "I was kicked by $kicker ($reason). Rejoining now.\n"; $irc->yield(join => $channel); } return; } sub irc_ctcp_action { irc_public(@_); } sub irc_public { my ($sender, $who, $where, $what) = @_[SENDER, ARG0 .. ARG2]; my $nick = ( split /!/, $who )[0]; my $channel = $where->[0]; print("[$channel] $who: $what\n"); # reject ignored nicks first return if (grep {$_ eq $nick} @{$config{ignore}}); for my $module (@plugin_list) { my $stripped_what = strip_color(strip_formatting($what)); my $output = $module->message($irc->nick_name, $who, $where, $what, $stripped_what); $irc->yield(privmsg => $where => $output) if $output; } return; } sub irc_msg { my ($who, $to, $what, $ided) = @_[ARG0 .. ARG3]; my $nick = (split /!/, $who)[0]; if ($config{must_id} && $ided != 1) { $irc->yield(privmsg => $nick => "You must identify with services"); return; } if (!grep {$_ eq $who} @{$config{admins}}) { $irc->yield(privmsg => $nick => "I am bot, go away"); return; } # FIXME this needs tidying. Some of this can be factored out, surely. if ($what =~ /^nick\s/) { my ($newnick) = $what =~ /^nick\s+(\S+)$/; if ($newnick) { $irc->yield(nick => $newnick); $irc->yield(privmsg => $nick => "Requested."); } else { $irc->yield(privmsg => $nick => "Syntax: nick "); } } if ($what =~ /^part\s/) { my $message; if ($what =~ /^part(\s+(\S+))+$/m) { $what =~ s/^part\s+//; my ($chan_str, $reason) = split /\s+(?!#)/, $what, 2; my @channels = split /\s+/, $chan_str; $reason = "Commanded by $nick" unless $reason; $irc->yield(part => @channels => $reason); $irc->yield(privmsg => $nick => "Requested."); } else { $irc->yield(privmsg => $nick => "Syntax: part [channel2 ...] [partmsg]"); } } if ($what =~ /^join\s/) { if ($what =~ /^join(\s+(\S+))+$/) { $what =~ s/^join\s+//; my @channels = split /\s+/, $what; $irc->yield(join => $_) for @channels; $irc->yield(privmsg => $nick => "Requested."); } else { $irc->yield(privmsg => $nick => "Syntax: join [channel2 ...]"); } } if ($what =~ /^say\s/) { my ($channel, $message) = $what =~ /^say\s+(\S+)\s(.*)$/; if ($channel and $message) { $irc->yield(privmsg => $channel => $message); $irc->yield(privmsg => $nick => "Requested."); } else { $irc->yield(privmsg => $nick => "Syntax: say "); } } if ($what =~ /^action\s/) { my ($channel, $action) = $what =~ /^action\s+(\S+)\s(.*)$/; if ($channel and $action) { $irc->yield(ctcp => $channel => "ACTION $action"); $irc->yield(privmsg => $nick => "Requested."); } else { $irc->yield(privmsg => $nick => "Syntax: action "); } } if ($what =~ /^kick\s/) { my ($channel, $kickee, undef, $reason) = $what =~ /^kick\s+(\S+)\s(\S+)((?:\s)(.*))?$/; if ($channel and $kickee) { $reason = "Requested by $nick" unless $reason; $irc->yield(kick => $channel => $kickee => $reason); $irc->yield(privmsg => $nick => "Requested."); } else { $irc->yield(privmsg => $nick => "Syntax: kick [reason]"); } } if ($what =~ /^reconnect/) { my ($reason) = $what =~ /^reconnect\s+(.+)$/; $irc->yield(privmsg => $nick => "Doing that now"); if (!$reason) { $reason = $config{quit_msg}; } $irc->yield(quit => $reason); } return; } sub irc_invite { my ($who, $where) = @_[ARG0 .. ARG1]; $irc->yield(join => $where) if (grep {$_ eq $where} @{$config{channels}}); } sub irc_disconnected { %config = config_file::parse_config($config_file); $irc->yield(connect => { }); } sub irc_error { $irc->yield(connect => { }); } sub irc_socketerr { $irc->yield(connect => { }); } sub _default { my ($event, $args) = @_[ARG0 .. $#_]; my @output = ( "$event: " ); for my $arg (@$args) { if ( ref $arg eq 'ARRAY' ) { push( @output, '[' . join(', ', @$arg ) . ']' ); } else { push ( @output, "'$arg'" ); } } print join ' ', @output, "\n"; return; }