3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-11-19 10:29:30 +01:00
pbot/PBot/IRCHandlers.pm
Pragmatic Software d955bfa06c Add centralized configuration registry module
Allows changing of bot configuration values without needing to restart
bot instance or needing to edit pbot.pl script.

Registry will initially be populated with default values from pbot.pl,
but if a registry file exists then the registry values will take
precedence over the pbot.pl values. For instance, if you regset the
bot trigger to '%' then the trigger will be '%' even if pbot.pl has '!'
or something else explicitly set.

Some registry items can have trigger hooks associated with them.  For
instance, the irc->botnick registry entry has a change_botnick_trigger
associated with it which changes the IRC nick on the server when a new
value is set via regset/regadd.

Tons of other fixes and improvements throughout.
2014-05-17 20:08:19 +00:00

260 lines
9.3 KiB
Perl

# File: IRCHandlers.pm
# Author: pragma_
#
# Purpose: Subroutines to handle IRC events
package PBot::IRCHandlers;
use warnings;
use strict;
use Carp();
use Time::HiRes qw(gettimeofday);
sub new {
if(ref($_[1]) eq 'HASH') {
Carp::croak("Options to IRCHandlers should be key/value pairs, not hash reference");
}
my ($class, %conf) = @_;
my $self = bless {}, $class;
$self->initialize(%conf);
return $self;
}
sub initialize {
my ($self, %conf) = @_;
my $pbot = delete $conf{pbot};
Carp::croak("Missing pbot parameter to IRCHandlers") if not defined $pbot;
$self->{pbot} = $pbot;
}
# IRC related subroutines
#################################################
sub on_connect {
my ($self, $conn) = @_;
$self->{pbot}->logger->log("Connected!\n");
$conn->{connected} = 1;
}
sub on_disconnect {
my ($self, $conn, $event) = @_;
$self->{pbot}->logger->log("Disconnected, attempting to reconnect...\n");
$conn->connect();
if(not $conn->connected) {
sleep(5);
$self->on_disconnect($self, $conn, $event);
}
}
sub on_init {
my ($self, $conn, $event) = @_;
my (@args) = ($event->args);
shift (@args);
$self->{pbot}->logger->log("*** @args\n");
}
sub on_public {
my ($self, $conn, $event) = @_;
my $from = $event->{to}[0];
my $nick = $event->nick;
my $user = $event->user;
my $host = $event->host;
my $text = $event->{args}[0];
$self->{pbot}->interpreter->process_line($from, $nick, $user, $host, $text);
}
sub on_msg {
my ($self, $conn, $event) = @_;
my ($nick, $host) = ($event->nick, $event->host);
my $text = $event->{args}[0];
my $bot_trigger = $self->{pbot}->{registry}->get_value('general', 'trigger');
$text =~ s/^\Q$bot_trigger\E?(.*)/$bot_trigger$1/;
$event->{to}[0] = $nick;
$event->{args}[0] = $text;
$self->on_public($conn, $event);
}
sub on_notice {
my ($self, $conn, $event) = @_;
my ($nick, $host) = ($event->nick, $event->host);
my $text = $event->{args}[0];
$self->{pbot}->logger->log("Received NOTICE from $nick $host '$text'\n");
if($nick eq "NickServ" && $text =~ m/This nickname is registered/) {
$self->{pbot}->logger->log("Identifying with NickServ . . .\n");
$conn->privmsg("nickserv", "identify " . $self->{pbot}->{registry}->get_value('irc', 'identify_password'));
}
if($nick eq "NickServ" && $text =~ m/You are now identified/) {
foreach my $chan (keys %{ $self->{pbot}->channels->channels->hash }) {
if($self->{pbot}->channels->channels->hash->{$chan}{enabled}) {
$self->{pbot}->logger->log("Joining channel: $chan\n");
$conn->join($chan);
}
}
$self->{pbot}->{joined_channels} = 1;
}
}
sub on_action {
my ($self, $conn, $event) = @_;
$event->{args}[0] = "/me " . $event->{args}[0];
$self->on_public($conn, $event);
}
sub on_mode {
my ($self, $conn, $event) = @_;
my ($nick, $user, $host) = ($event->nick, $event->user, $event->host);
my $mode_string = $event->{args}[0];
my $channel = $event->{to}[0];
$channel = lc $channel;
my ($mode, $modifier);
my $i = 0;
my $target;
while($mode_string =~ m/(.)/g) {
my $char = $1;
if($char eq '-' or $char eq '+') {
$modifier = $char;
next;
}
$mode = $modifier . $char;
$target = $event->{args}[++$i];
$self->{pbot}->logger->log("Got mode: source: $nick!$user\@$host, mode: $mode, target: " . (defined $target ? $target : "(undef)") . ", channel: $channel\n");
if($mode eq "-b" or $mode eq "+b" or $mode eq "-q" or $mode eq "+q") {
$self->{pbot}->bantracker->track_mode("$nick!$user\@$host", $mode, $target, $channel);
}
if(defined $target && $target eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { # bot targeted
if($mode eq "+o") {
$self->{pbot}->logger->log("$nick opped me in $channel\n");
$self->{pbot}->chanops->{is_opped}->{$channel}{timeout} = gettimeofday + 300; # 5 minutes
$self->{pbot}->chanops->perform_op_commands($channel);
}
elsif($mode eq "-o") {
$self->{pbot}->logger->log("$nick removed my ops in $channel\n");
delete $self->{pbot}->chanops->{is_opped}->{$channel};
}
elsif($mode eq "+b") {
$self->{pbot}->logger->log("Got banned in $channel, attempting unban.");
$conn->privmsg("chanserv", "unban $channel");
}
}
else { # bot not targeted
if($mode eq "+b") {
if($nick eq "ChanServ") {
$self->{pbot}->chanops->{unban_timeout}->hash->{$channel}->{$target}{timeout} = gettimeofday + 3600 * 2; # 2 hours
$self->{pbot}->chanops->{unban_timeout}->save;
}
}
elsif($mode eq "+e" && $channel eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) {
foreach my $chan (keys %{ $self->{pbot}->channels->channels->hash }) {
if($self->channels->channels->hash->{$chan}{enabled}) {
$self->{pbot}->logger->log("Joining channel: $chan\n");
$self->{pbot}->conn->join($chan);
}
}
$self->{pbot}->{joined_channels} = 1;
}
}
}
}
sub on_join {
my ($self, $conn, $event) = @_;
my ($nick, $user, $host, $channel) = ($event->nick, $event->user, $event->host, $event->to);
my $message_account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host);
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "JOIN", $self->{pbot}->{messagehistory}->{MSG_JOIN});
$self->{pbot}->antiflood->check_flood($channel, $nick, $user, $host, "JOIN", $self->{pbot}->{registry}->get_value('antiflood', 'max_join_flood'), 60 * 30, $self->{pbot}->{messagehistory}->{MSG_JOIN});
}
sub on_kick {
my ($self, $conn, $event) = @_;
my ($nick, $user, $host, $target, $channel, $reason) = ($event->nick, $event->user, $event->host, $event->to, $event->{args}[0], $event->{args}[1]);
$self->{pbot}->logger->log("$nick!$user\@$host kicked $target from $channel ($reason)\n");
my ($message_account) = $self->{pbot}->{messagehistory}->{database}->find_message_account_by_nick($target);
if(defined $message_account) {
my $hostmask = $self->{pbot}->{messagehistory}->{database}->find_most_recent_hostmask($message_account);
my ($target_nick, $target_user, $target_host) = $hostmask =~ m/^([^!]+)!([^@]+)@(.*)/;
my $text = "KICKED by $nick!$user\@$host ($reason)";
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, $text, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE});
$self->{pbot}->antiflood->check_flood($channel, $target_nick, $target_user, $target_host, $text, $self->{pbot}->{registry}->get_value('antiflood', 'max_join_flood'), 60 * 30, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE});
}
}
sub on_departure {
my ($self, $conn, $event) = @_;
my ($nick, $user, $host, $channel, $args) = ($event->nick, $event->user, $event->host, $event->to, $event->args);
my $text = uc $event->type;
$text .= " $args";
my $message_account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host);
if($text =~ m/^QUIT/) {
# QUIT messages must be dispatched to each channel the user is on
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($message_account);
foreach my $chan (@channels) {
next if $chan !~ m/^#/;
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $chan, $text, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE});
}
} else {
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, $text, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE});
}
$self->{pbot}->antiflood->check_flood($channel, $nick, $user, $host, $text, $self->{pbot}->{registry}->get_value('antiflood', 'max_join_flood'), 60 * 30, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE});
my $admin = $self->{pbot}->admins->find_admin($channel, "$nick!$user\@$host");
if(defined $admin and $admin->{loggedin}) {
$self->{pbot}->logger->log("Whoops, $nick left while still logged in.\n");
$self->{pbot}->logger->log("Logged out $nick.\n");
delete $admin->{loggedin};
}
}
sub on_nickchange {
my ($self, $conn, $event) = @_;
my ($nick, $user, $host, $newnick) = ($event->nick, $event->user, $event->host, $event->args);
$self->{pbot}->logger->log("$nick!$user\@$host changed nick to $newnick\n");
my $message_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($nick, $user, $host);
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($message_account);
my @channels = $self->{pbot}->{messagehistory}->{database}->get_channels($message_account);
foreach my $channel (@channels) {
next if $channel !~ m/^#/;
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "NICKCHANGE $newnick", $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE});
}
my $newnick_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($newnick, $user, $host);
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($newnick_account);
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data($newnick_account, { last_seen => scalar gettimeofday });
$self->{pbot}->antiflood->check_flood("$nick!$user\@$host", $nick, $user, $host, "NICKCHANGE $newnick", $self->{pbot}->{registry}->get_value('antiflood', 'max_nick_flood'), 60 * 30, $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE});
}
1;