3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-12-02 00:49:26 +01:00

Progress on refactoring and polishing everything

This commit is contained in:
Pragmatic Software 2021-06-24 18:28:49 -07:00
parent 8ea3fab219
commit f1c5b8c706
8 changed files with 335 additions and 145 deletions

View File

@ -624,14 +624,14 @@ sub flush_ban_queue {
my $target = pop @{$self->{ban_queue}->{$channel}->{$mode}}; my $target = pop @{$self->{ban_queue}->{$channel}->{$mode}};
$list .= " $target"; $list .= " $target";
$modes .= $mode; $modes .= $mode;
last if ++$count >= $self->{pbot}->{ircd}->{MODES}; last if ++$count >= $self->{pbot}->{isupport}->{MODES} // 1;
} }
if (not @{$self->{ban_queue}->{$channel}->{$mode}}) { if (not @{$self->{ban_queue}->{$channel}->{$mode}}) {
delete $self->{ban_queue}->{$channel}->{$mode}; delete $self->{ban_queue}->{$channel}->{$mode};
} }
last if $count >= $self->{pbot}->{ircd}->{MODES}; last if $count >= $self->{pbot}->{isupport}->{MODES} // 1;
} }
if (not keys %{$self->{ban_queue}->{$channel}}) { if (not keys %{$self->{ban_queue}->{$channel}}) {
@ -675,14 +675,14 @@ sub flush_unban_queue {
my $target = pop @{$self->{unban_queue}->{$channel}->{$mode}}; my $target = pop @{$self->{unban_queue}->{$channel}->{$mode}};
$list .= " $target"; $list .= " $target";
$modes .= $mode; $modes .= $mode;
last if ++$count >= $self->{pbot}->{ircd}->{MODES}; last if ++$count >= $self->{pbot}->{isupport}->{MODES} // 1;
} }
if (not @{$self->{unban_queue}->{$channel}->{$mode}}) { if (not @{$self->{unban_queue}->{$channel}->{$mode}}) {
delete $self->{unban_queue}->{$channel}->{$mode}; delete $self->{unban_queue}->{$channel}->{$mode};
} }
last if $count >= $self->{pbot}->{ircd}->{MODES}; last if $count >= $self->{pbot}->{isupport}->{MODES} // 1;
} }
if (not keys %{$self->{unban_queue}->{$channel}}) { if (not keys %{$self->{unban_queue}->{$channel}}) {

View File

@ -174,7 +174,7 @@ sub generic_mode {
# add $nick to $args if no argument # add $nick to $args if no argument
if (not $self->{pbot}->{interpreter}->arglist_size($context->{arglist})) { $self->{pbot}->{interpreter}->unshift_arg($context->{arglist}, $context->{nick}); } if (not $self->{pbot}->{interpreter}->arglist_size($context->{arglist})) { $self->{pbot}->{interpreter}->unshift_arg($context->{arglist}, $context->{nick}); }
my $max_modes = $self->{pbot}->{ircd}->{MODES} // 1; my $max_modes = $self->{pbot}->{isupport}->{MODES} // 1;
my $mode = $flag; my $mode = $flag;
my $list = ''; my $list = '';
my $i = 0; my $i = 0;
@ -247,7 +247,7 @@ sub cmd_mode {
my $arg = 0; my $arg = 0;
my ($new_modes, $new_targets) = ("", ""); my ($new_modes, $new_targets) = ("", "");
my $max_modes = $self->{pbot}->{ircd}->{MODES} // 1; my $max_modes = $self->{pbot}->{isupport}->{MODES} // 1;
my $u = $self->{pbot}->{users}->loggedin($channel, $context->{hostmask}); my $u = $self->{pbot}->{users}->loggedin($channel, $context->{hostmask});

View File

@ -205,7 +205,7 @@ sub trans {
'002' => "yourhost", '002' => "yourhost",
'003' => "created", '003' => "created",
'004' => "myinfo", '004' => "myinfo",
'005' => "map", # Undernet Extension, Kajetan@Hinner.com, 17/11/98 '005' => "isupport", # Undernet Extension, Kajetan@Hinner.com, 17/11/98 # renamed to `isupport` - pragma June 24, 2021
'006' => "mapmore", # Undernet Extension, Kajetan@Hinner.com, 17/11/98 '006' => "mapmore", # Undernet Extension, Kajetan@Hinner.com, 17/11/98
'007' => "mapend", # Undernet Extension, Kajetan@Hinner.com, 17/11/98 '007' => "mapend", # Undernet Extension, Kajetan@Hinner.com, 17/11/98
'008' => "snomask", # Undernet Extension, Kajetan@Hinner.com, 17/11/98 '008' => "snomask", # Undernet Extension, Kajetan@Hinner.com, 17/11/98

View File

@ -1,6 +1,9 @@
# File: IRCHandlers.pm # File: IRCHandlers.pm
# #
# Purpose: Subroutines to handle IRC events # Purpose: Subroutines to handle IRC events. Note that various PBot packages
# can in turn register their own IRC event handlers as well. There can be
# multiple handlers for PRIVMSG spread throughout the bot and its plugins,
# for example.
# This Source Code Form is subject to the terms of the Mozilla Public # This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this # License, v. 2.0. If a copy of the MPL was not distributed with this
@ -11,85 +14,107 @@ use parent 'PBot::Class';
use PBot::Imports; use PBot::Imports;
use Time::HiRes qw(gettimeofday); use Time::HiRes qw/time/;
use Data::Dumper; use Data::Dumper;
use MIME::Base64; use MIME::Base64;
use Encode; use Encode;
$Data::Dumper::Sortkeys = 1;
sub initialize { sub initialize {
my ($self, %conf) = @_; my ($self, %conf) = @_;
$self->{pbot}->{event_dispatcher}->register_handler('irc.welcome', sub { $self->on_connect(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.disconnect', sub { $self->on_disconnect(@_) }); # convenient alias so the following lings aren't so long
$self->{pbot}->{event_dispatcher}->register_handler('irc.motd', sub { $self->on_motd(@_) }); my $ed = $self->{pbot}->{event_dispatcher};
$self->{pbot}->{event_dispatcher}->register_handler('irc.notice', sub { $self->on_notice(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.public', sub { $self->on_public(@_) }); # various IRC events (note that other PBot packages and plugins can
$self->{pbot}->{event_dispatcher}->register_handler('irc.caction', sub { $self->on_action(@_) }); # also register additional IRC event handlers, including handlers for
$self->{pbot}->{event_dispatcher}->register_handler('irc.msg', sub { $self->on_msg(@_) }); # the events listed here. any duplicate events will be chained.)
$self->{pbot}->{event_dispatcher}->register_handler('irc.mode', sub { $self->on_mode(@_) }); $ed->register_handler('irc.welcome', sub { $self->on_connect (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.part', sub { $self->on_departure(@_) }); $ed->register_handler('irc.disconnect', sub { $self->on_disconnect (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join(@_) }); $ed->register_handler('irc.motd', sub { $self->on_motd (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.kick', sub { $self->on_kick(@_) }); $ed->register_handler('irc.notice', sub { $self->on_notice (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.quit', sub { $self->on_departure(@_) }); $ed->register_handler('irc.public', sub { $self->on_public (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.nick', sub { $self->on_nickchange(@_) }); $ed->register_handler('irc.caction', sub { $self->on_action (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.nicknameinuse', sub { $self->on_nicknameinuse(@_) }); $ed->register_handler('irc.msg', sub { $self->on_msg (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.invite', sub { $self->on_invite(@_) }); $ed->register_handler('irc.mode', sub { $self->on_mode (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.map', sub { $self->on_map(@_) }); $ed->register_handler('irc.part', sub { $self->on_departure (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.whoreply', sub { $self->on_whoreply(@_) }); $ed->register_handler('irc.join', sub { $self->on_join (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.whospcrpl', sub { $self->on_whospcrpl(@_) }); $ed->register_handler('irc.kick', sub { $self->on_kick (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.endofwho', sub { $self->on_endofwho(@_) }); $ed->register_handler('irc.quit', sub { $self->on_departure (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.channelmodeis', sub { $self->on_channelmodeis(@_) }); $ed->register_handler('irc.nick', sub { $self->on_nickchange (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.topic', sub { $self->on_topic(@_) }); $ed->register_handler('irc.nicknameinuse', sub { $self->on_nicknameinuse (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.topicinfo', sub { $self->on_topicinfo(@_) }); $ed->register_handler('irc.invite', sub { $self->on_invite (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.channelcreate', sub { $self->on_channelcreate(@_) }); $ed->register_handler('irc.isupport', sub { $self->on_isupport (@_) });
$ed->register_handler('irc.whoreply', sub { $self->on_whoreply (@_) });
$ed->register_handler('irc.whospcrpl', sub { $self->on_whospcrpl (@_) });
$ed->register_handler('irc.endofwho', sub { $self->on_endofwho (@_) });
$ed->register_handler('irc.channelmodeis', sub { $self->on_channelmodeis (@_) });
$ed->register_handler('irc.topic', sub { $self->on_topic (@_) });
$ed->register_handler('irc.topicinfo', sub { $self->on_topicinfo (@_) });
$ed->register_handler('irc.channelcreate', sub { $self->on_channelcreate (@_) });
$ed->register_handler('irc.yourhost', sub { $self->log_first_arg (@_) });
$ed->register_handler('irc.created', sub { $self->log_first_arg (@_) });
$ed->register_handler('irc.luserconns', sub { $self->log_first_arg (@_) });
$ed->register_handler('irc.notregistered', sub { $self->log_first_arg (@_) });
$ed->register_handler('irc.n_local', sub { $self->log_third_arg (@_) });
$ed->register_handler('irc.n_global', sub { $self->log_third_arg (@_) });
# IRCv3 client capabilities # IRCv3 client capabilities
$self->{pbot}->{event_dispatcher}->register_handler('irc.cap', sub { $self->on_cap(@_) }); $ed->register_handler('irc.cap', sub { $self->on_cap(@_) });
# IRCv3 SASL # IRCv3 SASL
$self->{pbot}->{event_dispatcher}->register_handler('irc.authenticate', sub { $self->on_sasl_authenticate(@_) }); $ed->register_handler('irc.authenticate', sub { $self->on_sasl_authenticate (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.rpl_loggedin', sub { $self->on_rpl_loggedin(@_) }); $ed->register_handler('irc.rpl_loggedin', sub { $self->on_rpl_loggedin (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.rpl_loggedout', sub { $self->on_rpl_loggedout(@_) }); $ed->register_handler('irc.rpl_loggedout', sub { $self->on_rpl_loggedout (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.err_nicklocked', sub { $self->on_err_nicklocked(@_) }); $ed->register_handler('irc.err_nicklocked', sub { $self->on_err_nicklocked (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.rpl_saslsuccess', sub { $self->on_rpl_saslsuccess(@_) }); $ed->register_handler('irc.rpl_saslsuccess', sub { $self->on_rpl_saslsuccess (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.err_saslfail', sub { $self->on_err_saslfail(@_) }); $ed->register_handler('irc.err_saslfail', sub { $self->on_err_saslfail (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.err_sasltoolong', sub { $self->on_err_sasltoolong(@_) }); $ed->register_handler('irc.err_sasltoolong', sub { $self->on_err_sasltoolong (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.err_saslaborted', sub { $self->on_err_saslaborted(@_) }); $ed->register_handler('irc.err_saslaborted', sub { $self->on_err_saslaborted (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.err_saslalready', sub { $self->on_err_saslalready(@_) }); $ed->register_handler('irc.err_saslalready', sub { $self->on_err_saslalready (@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.rpl_saslmechs', sub { $self->on_rpl_saslmechs(@_) }); $ed->register_handler('irc.rpl_saslmechs', sub { $self->on_rpl_saslmechs (@_) });
# bot itself joining and parting channels # bot itself joining and parting channels
$self->{pbot}->{event_dispatcher}->register_handler('pbot.join', sub { $self->on_self_join(@_) }); $ed->register_handler('pbot.join', sub { $self->on_self_join(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('pbot.part', sub { $self->on_self_part(@_) }); $ed->register_handler('pbot.part', sub { $self->on_self_part(@_) });
# TODO: enqueue these events as needed instead of naively checking every 10 seconds # TODO: enqueue these events as needed instead of naively checking every 10 seconds
$self->{pbot}->{event_queue}->enqueue(sub { $self->check_pending_whos }, 10, 'Check pending WHOs'); $self->{pbot}->{event_queue}->enqueue(sub { $self->check_pending_whos }, 10, 'Check pending WHOs');
} }
# default PBot::IRC handler. this handler prepends 'irc.' to the event-type
# and then dispatches the event through PBot::EventDispatcher
sub default_handler { sub default_handler {
my ($self, $conn, $event) = @_; my ($self, $conn, $event) = @_;
if (not defined $self->{pbot}->{event_dispatcher}->dispatch_event("irc.$event->{type}", {conn => $conn, event => $event})) { my $result = $self->{pbot}->{event_dispatcher}->dispatch_event(
if ($self->{pbot}->{registry}->get_value('irc', 'log_default_handler')) { $self->{pbot}->{logger}->log(Dumper $event); } "irc.$event->{type}",
{
conn => $conn,
event => $event
}
);
if (not defined $result and $self->{pbot}->{registry}->get_value('irc', 'log_default_handler')) {
$Data::Dumper::Sortkeys = 1;
$self->{pbot}->{logger}->log(Dumper $event);
} }
} }
sub on_init { sub on_init {
my ($self, $conn, $event) = @_; my ($self, $conn, $event) = @_;
my (@args) = ($event->args); my (@args) = ($event->args);
shift(@args); shift @args;
$self->{pbot}->{logger}->log("*** @args\n"); $self->{pbot}->{logger}->log("*** @args\n");
} }
sub on_connect { sub on_connect {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
$self->{pbot}->{logger}->log("Connected!\n"); $self->{pbot}->{logger}->log("Connected!\n");
$event->{conn}->{connected} = 1;
if (not $self->{pbot}->{irc_capabilities}->{sasl}) { if (not $self->{pbot}->{irc_capabilities}->{sasl}) {
# not using SASL, so identify the old way by /msg NickServ or some bot # not using SASL, so identify the old way by /msging NickServ or some such services bot
if (length $self->{pbot}->{registry}->get_value('irc', 'identify_password')) { if (length $self->{pbot}->{registry}->get_value('irc', 'identify_password')) {
$self->{pbot}->{logger}->log("Identifying with NickServ . . .\n"); $self->{pbot}->{logger}->log("Identifying with NickServ . . .\n");
@ -104,9 +129,11 @@ sub on_connect {
$event->{conn}->privmsg($nickserv, $command); $event->{conn}->privmsg($nickserv, $command);
} else { } else {
# using SASL, we're already identified at this point
$self->{pbot}->{logger}->log("No identify password; skipping identification to services.\n"); $self->{pbot}->{logger}->log("No identify password; skipping identification to services.\n");
} }
# auto-join channels unless general.autojoin_wait_for_nickserv is true
if (not $self->{pbot}->{registry}->get_value('general', 'autojoin_wait_for_nickserv')) { if (not $self->{pbot}->{registry}->get_value('general', 'autojoin_wait_for_nickserv')) {
$self->{pbot}->{logger}->log("Autojoining channels immediately; to wait for services set general.autojoin_wait_for_nickserv to 1.\n"); $self->{pbot}->{logger}->log("Autojoining channels immediately; to wait for services set general.autojoin_wait_for_nickserv to 1.\n");
$self->{pbot}->{channels}->autojoin; $self->{pbot}->{channels}->autojoin;
@ -114,7 +141,7 @@ sub on_connect {
$self->{pbot}->{logger}->log("Waiting for services identify response before autojoining channels.\n"); $self->{pbot}->{logger}->log("Waiting for services identify response before autojoining channels.\n");
} }
} else { } else {
# using SASL; go ahead and auto-join channels # using SASL; go ahead and auto-join channels now
$self->{pbot}->{logger}->log("Autojoining channels.\n"); $self->{pbot}->{logger}->log("Autojoining channels.\n");
$self->{pbot}->{channels}->autojoin; $self->{pbot}->{channels}->autojoin;
} }
@ -124,9 +151,14 @@ sub on_connect {
sub on_disconnect { sub on_disconnect {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
$self->{pbot}->{logger}->log("Disconnected...\n"); $self->{pbot}->{logger}->log("Disconnected...\n");
$self->{pbot}->{connected} = 0; $self->{pbot}->{connected} = 0;
# attempt to reconnect to server
# TODO: maybe add a registry entry to control whether the bot auto-reconnects
$self->{pbot}->connect; $self->{pbot}->connect;
return 0; return 0;
} }
@ -134,117 +166,168 @@ sub on_motd {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
if ($self->{pbot}->{registry}->get_value('irc', 'show_motd')) { if ($self->{pbot}->{registry}->get_value('irc', 'show_motd')) {
my $server = $event->{event}->{from}; my $from = $event->{event}->{from};
my $msg = $event->{event}->{args}[1]; my $msg = $event->{event}->{args}->[1];
$self->{pbot}->{logger}->log("MOTD from $server :: $msg\n"); $self->{pbot}->{logger}->log("MOTD from $from :: $msg\n");
} }
return 0; return 0;
} }
# the bot itself joining a channel
sub on_self_join { sub on_self_join {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
# early-return if we don't send WHO on join
# (we send WHO to see who is in the channel, for ban-evasion enforcement and such)
return 0 if not $self->{pbot}->{registry}->get_value('general', 'send_who_on_join') // 1; return 0 if not $self->{pbot}->{registry}->get_value('general', 'send_who_on_join') // 1;
# we turn on send_who if the following conditions are met
my $send_who = 0; my $send_who = 0;
if ($self->{pbot}->{registry}->get_value('general', 'send_who_chanop_only') // 1) { if ($self->{pbot}->{registry}->get_value('general', 'send_who_chanop_only') // 1) {
# check if we only send WHO to where we can gain ops
if ($self->{pbot}->{channels}->get_meta($event->{channel}, 'chanop')) { if ($self->{pbot}->{channels}->get_meta($event->{channel}, 'chanop')) {
# yup, we can +o in this channel, turn on send_who
$send_who = 1; $send_who = 1;
} }
} else { } else {
# otherwise just go ahead turn on send_who
$send_who = 1; $send_who = 1;
} }
# schedule the WHO to be sent to this channel
$self->send_who($event->{channel}) if $send_who; $self->send_who($event->{channel}) if $send_who;
return 0; return 0;
} }
# the bot itself leaving a channel
sub on_self_part { sub on_self_part {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
# nothing to do here yet
return 0; return 0;
} }
sub on_public { sub on_public {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my $from = $event->{event}->{to}[0]; my ($from, $nick, $user, $host, $text) = (
my $nick = $event->{event}->nick; $event->{event}->{to}->[0],
my $user = $event->{event}->user; $event->{event}->nick,
my $host = $event->{event}->host; $event->{event}->user,
my $text = $event->{event}->{args}[0]; $event->{event}->host,
$event->{event}->{args}->[0],
);
($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host); ($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host);
# send text to be processed for bot commands, anti-flood enforcement, etc
$event->{interpreted} = $self->{pbot}->{interpreter}->process_line($from, $nick, $user, $host, $text); $event->{interpreted} = $self->{pbot}->{interpreter}->process_line($from, $nick, $user, $host, $text);
return 0; return 0;
} }
sub on_msg { sub on_msg {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my ($nick, $host) = ($event->{event}->nick, $event->{event}->host);
my $text = $event->{event}->{args}[0];
my $bot_trigger = $self->{pbot}->{registry}->get_value('general', 'trigger'); my ($nick, $user, $host, $text) = (
my $bot_nick = $self->{pbot}->{registry}->get_value('irc', 'botnick'); $event->{event}->nick,
$event->{event}->user,
$event->{event}->host,
$event->{event}->{args}->[0],
);
($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host);
# send text to be processed as a bot command, coming from $nick
$event->{interpreted} = $self->{pbot}->{interpreter}->process_line($nick, $nick, $user, $host, $text, 1);
$text =~ s/^$bot_trigger?\s*(.*)/$bot_nick $1/;
$event->{event}->{to}[0] = $nick;
$event->{event}->{args}[0] = $text;
$self->on_public($event_type, $event);
return 0; return 0;
} }
sub on_notice { sub on_notice {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my ($nick, $user, $host) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host);
my $text = $event->{event}->{args}[0];
$self->{pbot}->{logger}->log("Received NOTICE from $nick!$user\@$host to $event->{event}->{to}[0] '$text'\n"); my ($nick, $user, $host, $to, $text) = (
$event->{event}->nick,
$event->{event}->user,
$event->{event}->host,
$event->{event}->to,
$event->{event}->{args}->[0],
);
return 0 if not length $host; # log notice
$self->{pbot}->{logger}->log("NOTICE from $nick!$user\@$host to $to: $text\n");
# notice from NickServ
if ($nick eq 'NickServ') { if ($nick eq 'NickServ') {
# if we have enabled NickServ GUARD protection and we're not identified yet,
# NickServ will warn us to identify -- this is our cue to identify.
if ($text =~ m/This nickname is registered/) { if ($text =~ m/This nickname is registered/) {
if (length $self->{pbot}->{registry}->get_value('irc', 'identify_password')) { if (length $self->{pbot}->{registry}->get_value('irc', 'identify_password')) {
$self->{pbot}->{logger}->log("Identifying with NickServ . . .\n"); $self->{pbot}->{logger}->log("Identifying with NickServ . . .\n");
$event->{conn}->privmsg("nickserv", "identify " . $self->{pbot}->{registry}->get_value('irc', 'identify_password')); $event->{conn}->privmsg("nickserv", "identify " . $self->{pbot}->{registry}->get_value('irc', 'identify_password'));
} }
} elsif ($text =~ m/You are now identified/) { }
if ($self->{pbot}->{registry}->get_value('irc', 'randomize_nick')) { $event->{conn}->nick($self->{pbot}->{registry}->get_value('irc', 'botnick')); } elsif ($text =~ m/You are now identified/) {
else { $self->{pbot}->{channels}->autojoin; } # we have identified with NickServ
} elsif ($text =~ m/has been ghosted/) { if ($self->{pbot}->{registry}->get_value('irc', 'randomize_nick')) {
# if irc.randomize_nicks was enabled, we go ahead and attempt to
# change to our real botnick. we don't auto-join channels just yet in case
# the nick change fails.
$event->{conn}->nick($self->{pbot}->{registry}->get_value('irc', 'botnick'));
} else {
# otherwise go ahead and autojoin channels now
$self->{pbot}->{channels}->autojoin;
}
}
elsif ($text =~ m/has been ghosted/) {
# we have ghosted someone using our botnick, let's attempt to regain it now
$event->{conn}->nick($self->{pbot}->{registry}->get_value('irc', 'botnick')); $event->{conn}->nick($self->{pbot}->{registry}->get_value('irc', 'botnick'));
} }
} else { } else {
if ($event->{event}->{to}[0] eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { $event->{event}->{to}[0] = $nick; } # if NOTICE is sent to the bot then replace the `to` field with the
$self->on_public($event_type, $event); # sender's nick instead so when we pass it on to on_public ...
if ($to eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) {
$event->{event}->{to}->[0] = $nick;
}
# handle this NOTICE as a public message
# (check for bot commands, anti-flooding, etc)
$self->on_public($event_type, $event) unless $to eq '*';
} }
return 0; return 0;
} }
sub on_action { sub on_action {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
$event->{event}->{args}[0] = "/me " . $event->{event}->{args}[0]; # prepend "/me " to the message text
$event->{event}->{args}->[0] = "/me " . $event->{event}->{args}->[0];
# pass this along to on_public
$self->on_public($event_type, $event); $self->on_public($event_type, $event);
return 0; return 0;
} }
# FIXME: on_mode doesn't handle chanmodes that have parameters, e.g. +l # FIXME: on_mode doesn't handle chanmodes that have parameters, e.g. +l
sub on_mode { sub on_mode {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my ($nick, $user, $host) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host);
my $mode_string = $event->{event}->{args}[0]; my ($nick, $user, $host, $mode_string, $channel) = (
my $channel = $event->{event}->{to}[0]; $event->{event}->nick,
$channel = lc $channel; $event->{event}->user,
$event->{event}->host,
$event->{event}->{args}->[0],
lc $event->{event}->{to}->[0],
);
($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host); ($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host);
my ($mode, $mode_char, $modifier);
my $i = 0; my $i = 0;
my $target; my ($mode, $mode_char, $modifier, $target);
while ($mode_string =~ m/(.)/g) { while ($mode_string =~ m/(.)/g) {
my $char = $1; my $char = $1;
@ -254,32 +337,41 @@ sub on_mode {
next; next;
} }
$mode = $modifier . $char; $mode = $modifier . $char;
$mode_char = $char; $target = $event->{event}->{args}->[++$i];
$target = $event->{event}->{args}[++$i];
$self->{pbot}->{logger}->log("Mode $channel [$mode" . (length $target ? " $target" : '') . "] by $nick!$user\@$host\n"); $self->{pbot}->{logger}->log("Mode $channel [$mode" . (length $target ? " $target" : '') . "] by $nick!$user\@$host\n");
# TODO: figure out a good way to allow other packages to receive "track_mode" events
# i.e., perhaps by emitting a modechange event or some such and registering handlers
$self->{pbot}->{banlist}->track_mode("$nick!$user\@$host", $channel, $mode, $target); $self->{pbot}->{banlist}->track_mode("$nick!$user\@$host", $channel, $mode, $target);
$self->{pbot}->{chanops}->track_mode("$nick!$user\@$host", $channel, $mode, $target); $self->{pbot}->{chanops}->track_mode("$nick!$user\@$host", $channel, $mode, $target);
if (defined $target and length $target) { if (defined $target and length $target) {
# mode set on user # mode set on user
my $message_account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host); my $message_account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host);
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "MODE $mode $target", $self->{pbot}->{messagehistory}->{MSG_CHAT}); $self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "MODE $mode $target", $self->{pbot}->{messagehistory}->{MSG_CHAT});
if ($modifier eq '-') { $self->{pbot}->{nicklist}->delete_meta($channel, $target, "+$mode_char"); } # TODO: here as well
else { $self->{pbot}->{nicklist}->set_meta($channel, $target, $mode, 1); } if ($modifier eq '-') {
$self->{pbot}->{nicklist}->delete_meta($channel, $target, "+$mode_char");
} else {
$self->{pbot}->{nicklist}->set_meta($channel, $target, $mode, 1);
}
} else { } else {
# mode set on channel # mode set on channel
my $modes = $self->{pbot}->{channels}->get_meta($channel, 'MODE'); my $modes = $self->{pbot}->{channels}->get_meta($channel, 'MODE');
if (defined $modes) { if (defined $modes) {
if ($modifier eq '+') { if ($modifier eq '+') {
$modes = '+' if not length $modes; $modes = '+' if not length $modes;
$modes .= $mode_char; $modes .= $mode_char;
} else { } else {
$modes =~ s/\Q$mode_char\E//g; $modes =~ s/\Q$mode_char//g;
} }
# TODO: here as well
$self->{pbot}->{channels}->{channels}->set($channel, 'MODE', $modes, 1); $self->{pbot}->{channels}->{channels}->set($channel, 'MODE', $modes, 1);
} }
} }
@ -290,12 +382,16 @@ sub on_mode {
sub on_join { sub on_join {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my ($nick, $user, $host, $channel) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->to);
my ($nick, $user, $host, $channel) = (
$event->{event}->nick,
$event->{event}->user,
$event->{event}->host,
lc $event->{event}->{to}->[0],
);
($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host); ($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host);
$channel = lc $channel;
my $message_account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host); 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}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "JOIN", $self->{pbot}->{messagehistory}->{MSG_JOIN});
@ -303,14 +399,20 @@ sub on_join {
my $msg = 'JOIN'; my $msg = 'JOIN';
# IRCv3 extended-join capability provides more details about user
if (exists $self->{pbot}->{irc_capabilities}->{'extended-join'}) { if (exists $self->{pbot}->{irc_capabilities}->{'extended-join'}) {
$msg .= " $event->{event}->{args}[0] :$event->{event}->{args}[1]"; my ($nickserv, $gecos) = (
$event->{event}->{args}->[0],
$event->{event}->{args}->[1],
);
$self->{pbot}->{messagehistory}->{database}->update_gecos($message_account, $event->{event}->{args}[1], scalar gettimeofday); $msg .= " $nickserv :$gecos";
if ($event->{event}->{args}[0] ne '*') { $self->{pbot}->{messagehistory}->{database}->update_gecos($message_account, $gecos, scalar time);
$self->{pbot}->{messagehistory}->{database}->link_aliases($message_account, undef, $event->{event}->{args}[0]);
$self->{pbot}->{antiflood}->check_nickserv_accounts($nick, $event->{event}->{args}[0]); if ($nickserv ne '*') {
$self->{pbot}->{messagehistory}->{database}->link_aliases($message_account, undef, $nickserv);
$self->{pbot}->{antiflood}->check_nickserv_accounts($nick, $nickserv);
} else { } else {
$self->{pbot}->{messagehistory}->{database}->set_current_nickserv_account($message_account, ''); $self->{pbot}->{messagehistory}->{database}->set_current_nickserv_account($message_account, '');
} }
@ -324,21 +426,30 @@ sub on_join {
$self->{pbot}->{registry}->get_value('antiflood', 'join_flood_time_threshold'), $self->{pbot}->{registry}->get_value('antiflood', 'join_flood_time_threshold'),
$self->{pbot}->{messagehistory}->{MSG_JOIN} $self->{pbot}->{messagehistory}->{MSG_JOIN}
); );
return 0; return 0;
} }
sub on_invite { sub on_invite {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my ($nick, $user, $host, $target, $channel) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->to, $event->{event}->{args}[0]);
my ($nick, $user, $host, $target, $channel) = (
$event->{event}->nick,
$event->{event}->user,
$event->{event}->host,
$event->{event}->to,
lc $event->{event}->{args}->[0]
);
($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host); ($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host);
$channel = lc $channel;
$self->{pbot}->{logger}->log("$nick!$user\@$host invited $target to $channel!\n"); $self->{pbot}->{logger}->log("$nick!$user\@$host invited $target to $channel!\n");
# if invited to a channel on our channel list, go ahead and join it
if ($target eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { if ($target eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) {
if ($self->{pbot}->{channels}->is_active($channel)) { $self->{pbot}->{interpreter}->add_botcmd_to_command_queue($channel, "join $channel", 0); } if ($self->{pbot}->{channels}->is_active($channel)) {
$self->{pbot}->{interpreter}->add_botcmd_to_command_queue($channel, "join $channel", 0);
}
} }
return 0; return 0;
@ -346,24 +457,38 @@ sub on_invite {
sub on_kick { sub on_kick {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my ($nick, $user, $host, $target, $channel, $reason) =
($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->to, $event->{event}->{args}[0], $event->{event}->{args}[1]); my ($nick, $user, $host, $target, $channel, $reason) = (
$channel = lc $channel; $event->{event}->nick,
$event->{event}->user,
$event->{event}->host,
$event->{event}->to,
lc $event->{event}->{args}->[0],
$event->{event}->{args}->[1]
);
($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host); ($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host);
$self->{pbot}->{logger}->log("$nick!$user\@$host kicked $target from $channel ($reason)\n"); $self->{pbot}->{logger}->log("$nick!$user\@$host kicked $target from $channel ($reason)\n");
# hostmask of the person being kicked
my $target_hostmask;
# look up message history account for person being kicked
my ($message_account) = $self->{pbot}->{messagehistory}->{database}->find_message_account_by_nick($target); my ($message_account) = $self->{pbot}->{messagehistory}->{database}->find_message_account_by_nick($target);
my $hostmask;
if (defined $message_account) { if (defined $message_account) {
$hostmask = $self->{pbot}->{messagehistory}->{database}->find_most_recent_hostmask($message_account); # update target hostmask
$target_hostmask = $self->{pbot}->{messagehistory}->{database}->find_most_recent_hostmask($message_account);
my ($target_nick, $target_user, $target_host) = $hostmask =~ m/^([^!]+)!([^@]+)@(.*)/; # add "KICKED by" to kicked person's message history
my $text = "KICKED by $nick!$user\@$host ($reason)"; my $text = "KICKED by $nick!$user\@$host ($reason)";
$self->{pbot}->{messagehistory}->add_message($message_account, $hostmask, $channel, $text, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE}); $self->{pbot}->{messagehistory}->add_message($message_account, $target_hostmask, $channel, $text, $self->{pbot}->{messagehistory}->{MSG_DEPARTURE});
# do stuff that happens in check_flood
my ($target_nick, $target_user, $target_host) = $target_hostmask =~ m/^([^!]+)!([^@]+)@(.*)/;
$self->{pbot}->{antiflood}->check_flood( $self->{pbot}->{antiflood}->check_flood(
$channel, $target_nick, $target_user, $target_host, $text, $channel, $target_nick, $target_user, $target_host, $text,
$self->{pbot}->{registry}->get_value('antiflood', 'join_flood_threshold'), $self->{pbot}->{registry}->get_value('antiflood', 'join_flood_threshold'),
@ -372,29 +497,42 @@ sub on_kick {
); );
} }
# look up message history account for person doing the kicking
$message_account = $self->{pbot}->{messagehistory}->{database}->get_message_account_id("$nick!$user\@$host"); $message_account = $self->{pbot}->{messagehistory}->{database}->get_message_account_id("$nick!$user\@$host");
if (defined $message_account) { if (defined $message_account) {
my $text = "KICKED " . (defined $hostmask ? $hostmask : $target) . " from $channel ($reason)"; # replace target nick with target hostmask if available
if (defined $target_hostmask) {
$target = $target_hostmask;
}
# add "KICKED $target" to kicker's message history
my $text = "KICKED $target from $channel ($reason)";
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, $text, $self->{pbot}->{messagehistory}->{MSG_CHAT}); $self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, $text, $self->{pbot}->{messagehistory}->{MSG_CHAT});
} }
return 0; return 0;
} }
sub on_departure { sub on_departure {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my ($nick, $user, $host, $channel, $args) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->to, $event->{event}->args);
$channel = lc $channel; my ($nick, $user, $host, $channel, $args) = (
$event->{event}->nick,
$event->{event}->user,
$event->{event}->host,
lc $event->{event}->{to}->[0],
$event->{event}->args
);
($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host); ($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host);
my $text = uc $event->{event}->type; my $text = uc ($event->{event}->type) . $args;
$text .= " $args";
my $message_account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host); my $message_account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host);
if ($text =~ m/^QUIT/) { if ($text =~ m/^QUIT/) {
# QUIT messages must be dispatched to each channel the user is on # QUIT messages must be added to the mesasge history of each channel the user is on
my $channels = $self->{pbot}->{nicklist}->get_channels($nick); my $channels = $self->{pbot}->{nicklist}->get_channels($nick);
foreach my $chan (@$channels) { foreach my $chan (@$channels) {
next if $chan !~ m/^#/; next if $chan !~ m/^#/;
@ -412,32 +550,44 @@ sub on_departure {
); );
my $u = $self->{pbot}->{users}->find_user($channel, "$nick!$user\@$host"); my $u = $self->{pbot}->{users}->find_user($channel, "$nick!$user\@$host");
# log user out if logged in and not stayloggedin
# TODO: this should probably be in Users.pm with its own part/quit/kick handler
if (defined $u and $u->{loggedin} and not $u->{stayloggedin}) { if (defined $u and $u->{loggedin} and not $u->{stayloggedin}) {
$self->{pbot}->{logger}->log("Logged out $nick.\n"); $self->{pbot}->{logger}->log("Logged out $nick.\n");
delete $u->{loggedin}; delete $u->{loggedin};
$self->{pbot}->{users}->save; $self->{pbot}->{users}->save;
} }
return 0; return 0;
} }
sub on_map { sub on_isupport {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
# remove and discard first and last elements # remove and discard first and last arguments
# (first arg is botnick, last arg is "are supported by this server")
shift @{$event->{event}->{args}}; shift @{$event->{event}->{args}};
pop @{$event->{event}->{args}}; pop @{$event->{event}->{args}};
my $logmsg = "$event->{event}->{from} supports:";
foreach my $arg (@{$event->{event}->{args}}) { foreach my $arg (@{$event->{event}->{args}}) {
my ($key, $value) = split /=/, $arg; my ($key, $value) = split /=/, $arg;
$self->{pbot}->{ircd}->{$key} = $value; if ($key =~ /^-/) {
# server removed suppport for this key
if (not defined $value) { delete $self->{pbot}->{isupport}->{$key};
$self->{pbot}->{logger}->log(" $key\n");
} else { } else {
$self->{pbot}->{logger}->log(" $key=$value\n"); $self->{pbot}->{isupport}->{$key} = $value // 1;
} }
$logmsg .= defined $value ? " $key=$value" : " $key";
} }
$self->{pbot}->{logger}->log("$logmsg\n");
return 0;
} }
# IRCv3 client capability negotiation # IRCv3 client capability negotiation
@ -522,7 +672,7 @@ sub on_cap {
if ($cap eq 'sasl') { if ($cap eq 'sasl') {
# begin SASL authentication # begin SASL authentication
# TODO: for now we support only PLAIN # TODO: for now we support only PLAIN
$self->{pbot}->{logger}->log("Performing SASL authentication [PLAIN]\n"); $self->{pbot}->{logger}->log("Performing SASL authentication PLAIN\n");
$event->{conn}->sl("AUTHENTICATE PLAIN"); $event->{conn}->sl("AUTHENTICATE PLAIN");
} }
} }
@ -532,6 +682,7 @@ sub on_cap {
} }
else { else {
$self->{pbot}->{logger}->log("Unknown CAP event:\n"); $self->{pbot}->{logger}->log("Unknown CAP event:\n");
$Data::Dumper::Sortkeys = 1;
$self->{pbot}->{logger}->log(Dumper $event->{event}); $self->{pbot}->{logger}->log(Dumper $event->{event});
} }
@ -648,11 +799,11 @@ sub on_nickchange {
next if $channel !~ m/^#/; next if $channel !~ m/^#/;
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "NICKCHANGE $newnick", $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE}); $self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, "NICKCHANGE $newnick", $self->{pbot}->{messagehistory}->{MSG_NICKCHANGE});
} }
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data("$nick!$user\@$host", {last_seen => scalar gettimeofday}); $self->{pbot}->{messagehistory}->{database}->update_hostmask_data("$nick!$user\@$host", {last_seen => scalar time});
my $newnick_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($newnick, $user, $host, $nick); my $newnick_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($newnick, $user, $host, $nick);
$self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($newnick_account, $self->{pbot}->{antiflood}->{NEEDS_CHECKBAN}); $self->{pbot}->{messagehistory}->{database}->devalidate_all_channels($newnick_account, $self->{pbot}->{antiflood}->{NEEDS_CHECKBAN});
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data("$newnick!$user\@$host", {last_seen => scalar gettimeofday}); $self->{pbot}->{messagehistory}->{database}->update_hostmask_data("$newnick!$user\@$host", {last_seen => scalar time});
$self->{pbot}->{antiflood}->check_flood( $self->{pbot}->{antiflood}->check_flood(
"$nick!$user\@$host", $nick, $user, $host, "NICKCHANGE $newnick", "$nick!$user\@$host", $nick, $user, $host, "NICKCHANGE $newnick",
@ -666,25 +817,35 @@ sub on_nickchange {
sub on_nicknameinuse { sub on_nicknameinuse {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my (undef, $nick, $msg) = $event->{event}->args;
my (undef, $nick, $msg) = $event->{event}->args;
my $from = $event->{event}->from; my $from = $event->{event}->from;
$self->{pbot}->{logger}->log("Received nicknameinuse for nick $nick from $from: $msg\n"); $self->{pbot}->{logger}->log("Received nicknameinuse for nick $nick from $from: $msg\n");
# attempt to use NickServ GHOST command to kick nick off
$event->{conn}->privmsg("nickserv", "ghost $nick " . $self->{pbot}->{registry}->get_value('irc', 'identify_password')); $event->{conn}->privmsg("nickserv", "ghost $nick " . $self->{pbot}->{registry}->get_value('irc', 'identify_password'));
return 0; return 0;
} }
sub on_channelmodeis { sub on_channelmodeis {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my (undef, $channel, $modes) = $event->{event}->args;
my (undef, $channel, $modes) = $event->{event}->args;
$self->{pbot}->{logger}->log("Channel $channel modes: $modes\n"); $self->{pbot}->{logger}->log("Channel $channel modes: $modes\n");
$self->{pbot}->{channels}->{channels}->set($channel, 'MODE', $modes, 1); $self->{pbot}->{channels}->{channels}->set($channel, 'MODE', $modes, 1);
} }
sub on_channelcreate { sub on_channelcreate {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my ($owner, $channel, $timestamp) = $event->{event}->args;
my ($owner, $channel, $timestamp) = $event->{event}->args;
$self->{pbot}->{logger}->log("Channel $channel created by $owner on " . localtime($timestamp) . "\n"); $self->{pbot}->{logger}->log("Channel $channel created by $owner on " . localtime($timestamp) . "\n");
$self->{pbot}->{channels}->{channels}->set($channel, 'CREATED_BY', $owner, 1); $self->{pbot}->{channels}->{channels}->set($channel, 'CREATED_BY', $owner, 1);
$self->{pbot}->{channels}->{channels}->set($channel, 'CREATED_ON', $timestamp, 1); $self->{pbot}->{channels}->{channels}->set($channel, 'CREATED_ON', $timestamp, 1);
} }
@ -706,8 +867,10 @@ sub on_topic {
$self->{pbot}->{logger}->log("$nick!$user\@$host changed topic for $channel to: $topic\n"); $self->{pbot}->{logger}->log("$nick!$user\@$host changed topic for $channel to: $topic\n");
$self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC', $topic, 1); $self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC', $topic, 1);
$self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC_SET_BY', "$nick!$user\@$host", 1); $self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC_SET_BY', "$nick!$user\@$host", 1);
$self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC_SET_ON', gettimeofday); $self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC_SET_ON', time);
} }
return 0;
} }
sub on_topicinfo { sub on_topicinfo {
@ -716,6 +879,19 @@ sub on_topicinfo {
$self->{pbot}->{logger}->log("Topic for $channel set by $by on " . localtime($timestamp) . "\n"); $self->{pbot}->{logger}->log("Topic for $channel set by $by on " . localtime($timestamp) . "\n");
$self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC_SET_BY', $by, 1); $self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC_SET_BY', $by, 1);
$self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC_SET_ON', $timestamp, 1); $self->{pbot}->{channels}->{channels}->set($channel, 'TOPIC_SET_ON', $timestamp, 1);
return 0;
}
sub log_first_arg {
my ($self, $event_type, $event) = @_;
$self->{pbot}->{logger}->log("$event->{event}->{args}->[1]\n");
return 0;
}
sub log_third_arg {
my ($self, $event_type, $event) = @_;
$self->{pbot}->{logger}->log("$event->{event}->{args}->[3]\n");
return 0;
} }
sub normalize_hostmask { sub normalize_hostmask {
@ -736,8 +912,10 @@ my $who_pending = 0;
sub on_whoreply { sub on_whoreply {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my (undef, $id, $user, $host, $server, $nick, $usermodes, $gecos) = @{$event->{event}->{args}}; my (undef, $id, $user, $host, $server, $nick, $usermodes, $gecos) = $event->{event}->args;
($nick, $user, $host) = $self->{pbot}->{irchandlers}->normalize_hostmask($nick, $user, $host);
($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host);
my $hostmask = "$nick!$user\@$host"; my $hostmask = "$nick!$user\@$host";
my $channel; my $channel;
@ -767,7 +945,7 @@ sub on_whoreply {
$self->{pbot}->{nicklist}->set_meta($channel, $nick, 'gecos', $gecos); $self->{pbot}->{nicklist}->set_meta($channel, $nick, 'gecos', $gecos);
my $account_id = $self->{pbot}->{messagehistory}->{database}->get_message_account($nick, $user, $host); my $account_id = $self->{pbot}->{messagehistory}->{database}->get_message_account($nick, $user, $host);
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data($hostmask, {last_seen => scalar gettimeofday}); $self->{pbot}->{messagehistory}->{database}->update_hostmask_data($hostmask, {last_seen => scalar time});
$self->{pbot}->{messagehistory}->{database}->link_aliases($account_id, $hostmask, undef); $self->{pbot}->{messagehistory}->{database}->link_aliases($account_id, $hostmask, undef);
@ -780,8 +958,10 @@ sub on_whoreply {
sub on_whospcrpl { sub on_whospcrpl {
my ($self, $event_type, $event) = @_; my ($self, $event_type, $event) = @_;
my (undef, $id, $user, $host, $nick, $nickserv, $gecos) = @{$event->{event}->{args}}; my (undef, $id, $user, $host, $nick, $nickserv, $gecos) = $event->{event}->args;
($nick, $user, $host) = $self->{pbot}->{irchandlers}->normalize_hostmask($nick, $user, $host);
($nick, $user, $host) = $self->normalize_hostmask($nick, $user, $host);
$last_who_id = $id; $last_who_id = $id;
my $hostmask = "$nick!$user\@$host"; my $hostmask = "$nick!$user\@$host";
my $channel = $who_cache{$id}; my $channel = $who_cache{$id};
@ -799,7 +979,7 @@ sub on_whospcrpl {
$self->{pbot}->{nicklist}->set_meta($channel, $nick, 'gecos', $gecos); $self->{pbot}->{nicklist}->set_meta($channel, $nick, 'gecos', $gecos);
my $account_id = $self->{pbot}->{messagehistory}->{database}->get_message_account($nick, $user, $host); my $account_id = $self->{pbot}->{messagehistory}->{database}->get_message_account($nick, $user, $host);
$self->{pbot}->{messagehistory}->{database}->update_hostmask_data($hostmask, {last_seen => scalar gettimeofday}); $self->{pbot}->{messagehistory}->{database}->update_hostmask_data($hostmask, {last_seen => scalar time});
if ($nickserv ne '0') { if ($nickserv ne '0') {
$self->{pbot}->{messagehistory}->{database}->link_aliases($account_id, undef, $nickserv); $self->{pbot}->{messagehistory}->{database}->link_aliases($account_id, undef, $nickserv);

View File

@ -37,7 +37,7 @@ sub initialize {
# this is the main entry point for a message to be parsed into commands # this is the main entry point for a message to be parsed into commands
# and to execute those commands and process their output # and to execute those commands and process their output
sub process_line { sub process_line {
my ($self, $from, $nick, $user, $host, $text) = @_; my ($self, $from, $nick, $user, $host, $text, $is_command) = @_;
# lowercase `from` field for case-insensitivity # lowercase `from` field for case-insensitivity
$from = lc $from; $from = lc $from;
@ -104,9 +104,17 @@ sub process_line {
my $command; # current command being parsed my $command; # current command being parsed
my $embedded = 0; # was command embedded within a message, e.g.: "see the !{help xyz} about that" my $embedded = 0; # was command embedded within a message, e.g.: "see the !{help xyz} about that"
my $nick_prefix = undef; # addressed nickname for prefixing output my $nick_prefix = undef; # addressed nickname for prefixing output
my $processed = 0; # counts how many commands were successfully processed my $processed = 0; # counts how many commands were successfully processed
# check if we should treat this entire text as a command
# (i.e., it came from /msg or was otherwise flagged as a command)
if ($is_command) {
$command = $cmd_text;
goto CHECK_EMBEDDED_CMD;
}
# otherwise try to parse any potential commands
if ($cmd_text =~ m/^\s*($nick_regex)[,:]?\s+$bot_trigger\{\s*(.+?)\s*\}\s*$/) { if ($cmd_text =~ m/^\s*($nick_regex)[,:]?\s+$bot_trigger\{\s*(.+?)\s*\}\s*$/) {
# "somenick: !{command}" # "somenick: !{command}"
goto CHECK_EMBEDDED_CMD; goto CHECK_EMBEDDED_CMD;

View File

@ -36,6 +36,8 @@ sub initialize {
# handlers for various IRC events # handlers for various IRC events
# TODO: track mode changes to update user flags # TODO: track mode changes to update user flags
# Update: turns out that IRCHandler's on_mode() is doing this already -- we need to make that
# emit a mode-change event or some such and register a handler for it here.
$self->{pbot}->{event_dispatcher}->register_handler('irc.namreply', sub { $self->on_namreply(@_) }); $self->{pbot}->{event_dispatcher}->register_handler('irc.namreply', sub { $self->on_namreply(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join(@_) }); $self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.part', sub { $self->on_part(@_) }); $self->{pbot}->{event_dispatcher}->register_handler('irc.part', sub { $self->on_part(@_) });

View File

@ -274,6 +274,7 @@ sub connect {
# ignore these events # ignore these events
$self->{conn}->add_handler( $self->{conn}->add_handler(
[ [
'myinfo',
'whoisserver', 'whoisserver',
'whoiscountry', 'whoiscountry',
'whoischannels', 'whoischannels',

View File

@ -185,8 +185,7 @@ sub cmd_remindme {
# add to the reminder text anything left in the arguments # add to the reminder text anything left in the arguments
if (@opt_args) { if (@opt_args) {
$text .= ' ' if length $text; $text .= " @opt_args";
$text .= join ' ', @opt_args;
} }
return "Please use -t to specify a time for this reminder." if not $alarm; return "Please use -t to specify a time for this reminder." if not $alarm;