mirror of
https://github.com/pragma-/pbot.git
synced 2024-11-06 03:59:31 +01:00
331 lines
12 KiB
Perl
331 lines
12 KiB
Perl
# File: Channel.pm
|
|
#
|
|
# Purpose: Handlers for general channel-related IRC events that aren't handled
|
|
# by any specialized Handler modules.
|
|
|
|
# SPDX-FileCopyrightText: 2005-2023 Pragmatic Software <pragma78@gmail.com>
|
|
# SPDX-License-Identifier: MIT
|
|
|
|
package PBot::Core::Handlers::Channel;
|
|
use parent 'PBot::Core::Class';
|
|
|
|
use PBot::Imports;
|
|
use PBot::Core::MessageHistory::Constants ':all';
|
|
|
|
use Data::Dumper;
|
|
use Encode;
|
|
use MIME::Base64;
|
|
use Time::HiRes qw/time/;
|
|
|
|
sub initialize($self, %conf) {
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.mode', sub { $self->on_mode (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.part', sub { $self->on_departure (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.quit', sub { $self->on_departure (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.kick', sub { $self->on_kick (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.invite', sub { $self->on_invite (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.channelmodeis', sub { $self->on_channelmodeis (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.topic', sub { $self->on_topic (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.topicinfo', sub { $self->on_topicinfo (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.channelcreate', sub { $self->on_channelcreate (@_) });
|
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.modeflag', sub { $self->on_modeflag (@_) });
|
|
}
|
|
|
|
sub on_mode($self, $event_type, $event) {
|
|
my ($nick, $user, $host, $mode_string, $channel) = (
|
|
$event->nick,
|
|
$event->user,
|
|
$event->host,
|
|
$event->{args}[0],
|
|
lc $event->{to}[0],
|
|
);
|
|
|
|
($nick, $user, $host) = $self->{pbot}->{irchandlers}->normalize_hostmask($nick, $user, $host);
|
|
|
|
my $i = 0;
|
|
my ($modifier, $flag, $mode, $target);
|
|
|
|
my $source = "$nick!$user\@$host";
|
|
|
|
# split combined modes
|
|
while ($mode_string =~ m/(.)/g) {
|
|
$flag = $1;
|
|
|
|
if ($flag eq '-' or $flag eq '+') {
|
|
$modifier = $flag;
|
|
next;
|
|
}
|
|
|
|
$mode = $modifier . $flag;
|
|
$target = $event->{args}[++$i];
|
|
|
|
$self->{pbot}->{logger}->log("[MODE] $channel $mode" . (length $target ? " $target" : '') . " by $source\n");
|
|
|
|
# dispatch a single mode flag event
|
|
$self->{pbot}->{event_dispatcher}->dispatch_event(
|
|
'irc.modeflag',
|
|
{
|
|
source => $source,
|
|
channel => $channel,
|
|
mode => $mode,
|
|
target => $target,
|
|
},
|
|
);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub on_modeflag($self, $event_type, $event) {
|
|
my ($source, $channel, $mode, $target) = (
|
|
$event->{source},
|
|
$event->{channel},
|
|
$event->{mode},
|
|
$event->{target},
|
|
);
|
|
|
|
# disregard mode set on user instead of channel
|
|
return if defined $target and length $target;
|
|
|
|
my ($modifier, $flag) = split //, $mode;
|
|
|
|
my $modes = $self->{pbot}->{channels}->get_meta($channel, 'MODE') // '';
|
|
|
|
if ($modifier eq '+') {
|
|
$modes = '+' if not length $modes;
|
|
$modes .= $flag if $modes !~ /\Q$flag/;
|
|
} else {
|
|
$modes =~ s/\Q$flag//g;
|
|
$modes = '' if $modes eq '+';
|
|
}
|
|
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'MODE', $modes, 1);
|
|
return 1;
|
|
}
|
|
|
|
sub on_join($self, $event_type, $event) {
|
|
my ($nick, $user, $host, $channel) = (
|
|
$event->nick,
|
|
$event->user,
|
|
$event->host,
|
|
lc $event->{to}[0],
|
|
);
|
|
|
|
($nick, $user, $host) = $self->{pbot}->{irchandlers}->normalize_hostmask($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", MSG_JOIN);
|
|
|
|
$self->{pbot}->{messagehistory}->{database}->devalidate_channel($message_account, $channel);
|
|
|
|
my $msg = 'JOIN';
|
|
|
|
# IRCv3 extended-join capability provides more details about user
|
|
if (exists $self->{pbot}->{irc_capabilities}->{'extended-join'}) {
|
|
my ($nickserv, $gecos) = (
|
|
$event->{args}[0],
|
|
$event->{args}[1],
|
|
);
|
|
|
|
$msg .= " $nickserv :$gecos";
|
|
|
|
$self->{pbot}->{messagehistory}->{database}->update_gecos($message_account, $gecos, scalar time);
|
|
|
|
if ($nickserv ne '*') {
|
|
$self->{pbot}->{messagehistory}->{database}->link_aliases($message_account, undef, $nickserv);
|
|
$self->{pbot}->{messagehistory}->{database}->update_nickserv_account($message_account, $nickserv, scalar time);
|
|
$self->{pbot}->{messagehistory}->{database}->set_current_nickserv_account($message_account, $nickserv);
|
|
} else {
|
|
$self->{pbot}->{messagehistory}->{database}->set_current_nickserv_account($message_account, '');
|
|
}
|
|
|
|
$self->{pbot}->{antiflood}->check_bans($message_account, $event->from, $channel);
|
|
}
|
|
|
|
$self->{pbot}->{antiflood}->check_flood(
|
|
$channel, $nick, $user, $host, $msg,
|
|
$self->{pbot}->{registry}->get_value('antiflood', 'join_flood_threshold'),
|
|
$self->{pbot}->{registry}->get_value('antiflood', 'join_flood_time_threshold'),
|
|
MSG_JOIN,
|
|
);
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub on_invite($self, $event_type, $event) {
|
|
my ($nick, $user, $host, $target, $channel) = (
|
|
$event->nick,
|
|
$event->user,
|
|
$event->host,
|
|
$event->to,
|
|
lc $event->{args}[0]
|
|
);
|
|
|
|
($nick, $user, $host) = $self->{pbot}->{irchandlers}->normalize_hostmask($nick, $user, $host);
|
|
|
|
$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 ($self->{pbot}->{channels}->is_active($channel)) {
|
|
$self->{pbot}->{interpreter}->add_botcmd_to_command_queue($channel, "join $channel", 0);
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub on_kick($self, $event_type, $event) {
|
|
my ($nick, $user, $host, $target, $channel, $reason) = (
|
|
$event->nick,
|
|
$event->user,
|
|
$event->host,
|
|
$event->to,
|
|
lc $event->{args}[0],
|
|
$event->{args}[1]
|
|
);
|
|
|
|
($nick, $user, $host) = $self->{pbot}->{irchandlers}->normalize_hostmask($nick, $user, $host);
|
|
|
|
$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);
|
|
|
|
if (defined $message_account) {
|
|
# update target hostmask
|
|
$target_hostmask = $self->{pbot}->{messagehistory}->{database}->find_most_recent_hostmask($message_account);
|
|
|
|
# add "KICKED by" to kicked person's message history
|
|
my $text = "KICKED by $nick!$user\@$host ($reason)";
|
|
|
|
$self->{pbot}->{messagehistory}->add_message($message_account, $target_hostmask, $channel, $text, MSG_DEPARTURE);
|
|
|
|
# do stuff that happens in check_flood
|
|
my ($target_nick, $target_user, $target_host) = $target_hostmask =~ m/^([^!]+)!([^@]+)@(.*)/;
|
|
|
|
$self->{pbot}->{antiflood}->check_flood(
|
|
$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_time_threshold'),
|
|
MSG_DEPARTURE,
|
|
);
|
|
}
|
|
|
|
# look up message history account for person doing the kicking
|
|
$message_account = $self->{pbot}->{messagehistory}->{database}->get_message_account_id("$nick!$user\@$host");
|
|
|
|
if (defined $message_account) {
|
|
# 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, MSG_CHAT);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub on_departure($self, $event_type, $event) {
|
|
my ($nick, $user, $host, $channel, $args) = (
|
|
$event->nick,
|
|
$event->user,
|
|
$event->host,
|
|
lc $event->{to}[0],
|
|
$event->args
|
|
);
|
|
|
|
($nick, $user, $host) = $self->{pbot}->{irchandlers}->normalize_hostmask($nick, $user, $host);
|
|
|
|
my $text = uc ($event->type) . ' ' . $args;
|
|
|
|
my $message_account = $self->{pbot}->{messagehistory}->get_message_account($nick, $user, $host);
|
|
|
|
if ($text =~ m/^QUIT/) {
|
|
# QUIT messages must be added to the mesasge history of each channel the user is on
|
|
my $channels = $self->{pbot}->{nicklist}->get_channels($nick);
|
|
foreach my $chan (@$channels) {
|
|
next if $chan !~ m/^#/;
|
|
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $chan, $text, MSG_DEPARTURE);
|
|
}
|
|
} else {
|
|
$self->{pbot}->{messagehistory}->add_message($message_account, "$nick!$user\@$host", $channel, $text, MSG_DEPARTURE);
|
|
}
|
|
|
|
$self->{pbot}->{antiflood}->check_flood(
|
|
$channel, $nick, $user, $host, $text,
|
|
$self->{pbot}->{registry}->get_value('antiflood', 'join_flood_threshold'),
|
|
$self->{pbot}->{registry}->get_value('antiflood', 'join_flood_time_threshold'),
|
|
MSG_DEPARTURE,
|
|
);
|
|
|
|
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}) {
|
|
$self->{pbot}->{logger}->log("Logged out $nick.\n");
|
|
delete $u->{loggedin};
|
|
$self->{pbot}->{users}->save;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub on_channelmodeis($self, $event_type, $event) {
|
|
my (undef, $channel, $modes) = $event->args;
|
|
|
|
$self->{pbot}->{logger}->log("Channel $channel modes: $modes\n");
|
|
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'MODE', $modes, 1);
|
|
return 1;
|
|
}
|
|
|
|
sub on_channelcreate($self, $event_type, $event) {
|
|
my ($owner, $channel, $timestamp) = $event->args;
|
|
|
|
$self->{pbot}->{logger}->log("Channel $channel created by $owner on " . localtime($timestamp) . "\n");
|
|
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'CREATED_BY', $owner, 1);
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'CREATED_ON', $timestamp, 1);
|
|
return 1;
|
|
}
|
|
|
|
sub on_topic($self, $event_type, $event) {
|
|
if (not length $event->{to}[0]) {
|
|
# on join
|
|
my (undef, $channel, $topic) = $event->args;
|
|
$self->{pbot}->{logger}->log("Topic for $channel: $topic\n");
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'TOPIC', $topic, 1);
|
|
} else {
|
|
# user changing topic
|
|
my ($nick, $user, $host) = ($event->nick, $event->user, $event->host);
|
|
my $channel = $event->{to}[0];
|
|
my $topic = $event->{args}[0];
|
|
|
|
$self->{pbot}->{logger}->log("$nick!$user\@$host changed topic for $channel to: $topic\n");
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'TOPIC', $topic, 1);
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'TOPIC_SET_BY', "$nick!$user\@$host", 1);
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'TOPIC_SET_ON', time);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
sub on_topicinfo($self, $event_type, $event) {
|
|
my (undef, $channel, $by, $timestamp) = $event->args;
|
|
$self->{pbot}->{logger}->log("Topic for $channel set by $by on " . localtime($timestamp) . "\n");
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'TOPIC_SET_BY', $by, 1);
|
|
$self->{pbot}->{channels}->{storage}->set($channel, 'TOPIC_SET_ON', $timestamp, 1);
|
|
return 1;
|
|
}
|
|
|
|
1;
|