pbot/PBot/NickList.pm

556 lines
18 KiB
Perl
Raw Normal View History

# File: NickList.pm
#
# Purpose: Maintains lists of nicks currently present in channels.
2019-06-26 18:34:19 +02:00
# Used to retrieve list of channels a nick is present in or to
# determine if a nick is present in a channel.
2021-07-11 00:00:22 +02:00
# SPDX-FileCopyrightText: 2021 Pragmatic Software <pragma78@gmail.com>
# SPDX-License-Identifier: MIT
License project under MPL2 This patch adds the file LICENSE which is the verbatim copy of the Mozilla Public License Version 2.0 as retreived from https://www.mozilla.org/media/MPL/2.0/index.815ca599c9df.txt on 2017-03-05. This patch also places license headers for the MPL2 type A variant of the license header in the following files: PBot/AntiFlood.pm PBot/BanTracker.pm PBot/BlackList.pm PBot/BotAdminCommands.pm PBot/BotAdmins.pm PBot/ChanOpCommands.pm PBot/ChanOps.pm PBot/Channels.pm PBot/Commands.pm PBot/DualIndexHashObject.pm PBot/EventDispatcher.pm PBot/FactoidCommands.pm PBot/FactoidModuleLauncher.pm PBot/Factoids.pm PBot/HashObject.pm PBot/IRCHandlers.pm PBot/IgnoreList.pm PBot/IgnoreListCommands.pm PBot/Interpreter.pm PBot/LagChecker.pm PBot/Logger.pm PBot/MessageHistory.pm PBot/MessageHistory_SQLite.pm PBot/NickList.pm PBot/PBot.pm PBot/Plugins.pm PBot/Plugins/AntiAway.pm PBot/Plugins/AntiKickAutoRejoin.pm PBot/Plugins/AntiRepeat.pm PBot/Plugins/AntiTwitter.pm PBot/Plugins/AutoRejoin.pm PBot/Plugins/Counter.pm PBot/Plugins/Quotegrabs.pm PBot/Plugins/Quotegrabs/Quotegrabs_Hashtable.pm PBot/Plugins/Quotegrabs/Quotegrabs_SQLite.pm PBot/Plugins/UrlTitles.pm PBot/Plugins/_Example.pm PBot/Refresher.pm PBot/Registerable.pm PBot/Registry.pm PBot/RegistryCommands.pm PBot/SQLiteLogger.pm PBot/SQLiteLoggerLayer.pm PBot/SelectHandler.pm PBot/StdinReader.pm PBot/Timer.pm PBot/Utils/ParseDate.pm PBot/VERSION.pm build/update-version.pl modules/acronym.pl modules/ago.pl modules/c11std.pl modules/c2english.pl modules/c2english/CGrammar.pm modules/c2english/c2eng.pl modules/c99std.pl modules/cdecl.pl modules/cfaq.pl modules/cjeopardy/IRCColors.pm modules/cjeopardy/QStatskeeper.pm modules/cjeopardy/Scorekeeper.pm modules/cjeopardy/cjeopardy.pl modules/cjeopardy/cjeopardy_answer.pl modules/cjeopardy/cjeopardy_filter.pl modules/cjeopardy/cjeopardy_hint.pl modules/cjeopardy/cjeopardy_qstats.pl modules/cjeopardy/cjeopardy_scores.pl modules/cjeopardy/cjeopardy_show.pl modules/codepad.pl modules/compiler_block.pl modules/compiler_client.pl modules/compiler_vm/Diff.pm modules/compiler_vm/cc modules/compiler_vm/compiler_client.pl modules/compiler_vm/compiler_server.pl modules/compiler_vm/compiler_server_vbox_win32.pl modules/compiler_vm/compiler_server_watchdog.pl modules/compiler_vm/compiler_vm_client.pl modules/compiler_vm/compiler_vm_server.pl modules/compiler_vm/compiler_watchdog.pl modules/compiler_vm/languages/_c_base.pm modules/compiler_vm/languages/_default.pm modules/compiler_vm/languages/bash.pm modules/compiler_vm/languages/bc.pm modules/compiler_vm/languages/bf.pm modules/compiler_vm/languages/c11.pm modules/compiler_vm/languages/c89.pm modules/compiler_vm/languages/c99.pm modules/compiler_vm/languages/clang.pm modules/compiler_vm/languages/clang11.pm modules/compiler_vm/languages/clang89.pm modules/compiler_vm/languages/clang99.pm modules/compiler_vm/languages/clangpp.pm modules/compiler_vm/languages/clisp.pm modules/compiler_vm/languages/cpp.pm modules/compiler_vm/languages/freebasic.pm modules/compiler_vm/languages/go.pm modules/compiler_vm/languages/haskell.pm modules/compiler_vm/languages/java.pm modules/compiler_vm/languages/javascript.pm modules/compiler_vm/languages/ksh.pm modules/compiler_vm/languages/lua.pm modules/compiler_vm/languages/perl.pm modules/compiler_vm/languages/python.pm modules/compiler_vm/languages/python3.pm modules/compiler_vm/languages/qbasic.pm modules/compiler_vm/languages/scheme.pm modules/compiler_vm/languages/server/_c_base.pm modules/compiler_vm/languages/server/_default.pm modules/compiler_vm/languages/server/c11.pm modules/compiler_vm/languages/server/c89.pm modules/compiler_vm/languages/server/c99.pm modules/compiler_vm/languages/server/clang.pm modules/compiler_vm/languages/server/clang11.pm modules/compiler_vm/languages/server/clang89.pm modules/compiler_vm/languages/server/clang99.pm modules/compiler_vm/languages/server/cpp.pm modules/compiler_vm/languages/server/freebasic.pm modules/compiler_vm/languages/server/haskell.pm modules/compiler_vm/languages/server/java.pm modules/compiler_vm/languages/server/qbasic.pm modules/compiler_vm/languages/server/tendra.pm modules/compiler_vm/languages/sh.pm modules/compiler_vm/languages/tendra.pm modules/compliment modules/cstd.pl modules/define.pl modules/dice_roll.pl modules/excuse.sh modules/expand_macros.pl modules/fnord.pl modules/funnyish_quote.pl modules/g.pl modules/gdefine.pl modules/gen_cfacts.pl modules/gencstd.pl modules/get_title.pl modules/getcfact.pl modules/google.pl modules/gspy.pl modules/gtop10.pl modules/gtop15.pl modules/headlines.pl modules/horoscope modules/horrorscope modules/ideone.pl modules/insult.pl modules/love_quote.pl modules/man.pl modules/map.pl modules/math.pl modules/prototype.pl modules/qalc.pl modules/random_quote.pl modules/seen.pl modules/urban modules/weather.pl modules/wikipedia.pl pbot.pl pbot.sh It is highly recommended that this list of files is reviewed to ensure that all files are the copyright of the sole maintainer of the repository. If any files with license headers contain the intellectual property of anyone else, it is recommended that a request is made to revise this patch or that the explicit permission of the co-author is gained to allow for the license of the work to be changed. I (Tomasz Kramkowski), the contributor, take no responsibility for any legal action taken against the maintainer of this repository for incorrectly claiming copyright to any work not owned by the maintainer of this repository.
2017-03-05 22:33:31 +01:00
package PBot::NickList;
use parent 'PBot::Class';
2021-06-19 06:23:34 +02:00
use PBot::Imports;
2019-07-11 03:40:53 +02:00
use Text::Levenshtein qw/fastdistance/;
use Data::Dumper;
2020-02-15 23:38:32 +01:00
2018-02-28 20:13:56 +01:00
$Data::Dumper::Sortkeys = 1;
use Time::HiRes qw/gettimeofday/;
use Time::Duration qw/concise ago/;
use Getopt::Long qw/GetOptionsFromArray/;
sub initialize {
2020-02-15 23:38:32 +01:00
my ($self, %conf) = @_;
# nicklist hash
2020-02-15 23:38:32 +01:00
$self->{nicklist} = {};
# nicklist debug registry entry
2020-02-15 23:38:32 +01:00
$self->{pbot}->{registry}->add_default('text', 'nicklist', 'debug', '0');
# nicklist bot command
$self->{pbot}->{commands}->register(sub { $self->cmd_nicklist(@_) }, "nicklist", 1);
2020-02-15 23:38:32 +01:00
# handlers for various IRC events
# 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.
2020-02-15 23:38:32 +01:00
$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.part', sub { $self->on_part(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.quit', sub { $self->on_quit(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.kick', sub { $self->on_kick(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.nick', sub { $self->on_nickchange(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.public', sub { $self->on_activity(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('irc.caction', sub { $self->on_activity(@_) });
# handlers for the bot itself joining/leaving channels
$self->{pbot}->{event_dispatcher}->register_handler('pbot.join', sub { $self->on_self_join(@_) });
$self->{pbot}->{event_dispatcher}->register_handler('pbot.part', sub { $self->on_self_part(@_) });
}
sub cmd_nicklist {
my ($self, $context) = @_;
my $usage = "Usage: nicklist (<channel [nick]> | <nick>) [-sort <by>] [-hostmask] [-join]; -hostmask shows hostmasks instead of nicks; -join includes join time";
my $getopt_error;
local $SIG{__WARN__} = sub {
$getopt_error = shift;
chomp $getopt_error;
};
Getopt::Long::Configure("bundling_override");
my $sort_method = 'nick';
my $full_hostmask = 0;
my $include_join = 0;
my @args = $self->{pbot}->{interpreter}->split_line($context->{arguments}, strip_quotes => 1);
GetOptionsFromArray(
\@args,
'sort|s=s' => \$sort_method,
'hostmask|hm' => \$full_hostmask,
'join|j' => \$include_join,
);
return "$getopt_error; $usage" if defined $getopt_error;
return "Too many arguments -- $usage" if @args > 2;
return $usage if @args == 0 or not length $args[0];
my %sort = (
'spoken' => sub {
if ($_[1] eq '+') {
return $_[0]->{$b}->{timestamp} <=> $_[0]->{$a}->{timestamp};
} else {
return $_[0]->{$a}->{timestamp} <=> $_[0]->{$b}->{timestamp};
}
},
'join' => sub {
if ($_[1] eq '+') {
return $_[0]->{$b}->{join} <=> $_[0]->{$a}->{join};
} else {
return $_[0]->{$a}->{join} <=> $_[0]->{$b}->{join};
}
},
'host' => sub {
if ($_[1] eq '+') {
return lc $_[0]->{$a}->{host} cmp lc $_[0]->{$b}->{host};
} else {
return lc $_[0]->{$b}->{host} cmp lc $_[0]->{$a}->{host};
}
},
'nick' => sub {
if ($_[1] eq '+') {
return lc $_[0]->{$a}->{nick} cmp lc $_[0]->{$b}->{nick};
} else {
return lc $_[0]->{$b}->{nick} cmp lc $_[0]->{$a}->{nick};
}
},
);
my $sort_direction = '+';
if ($sort_method =~ s/^(\+|\-)//) {
$sort_direction = $1;
}
if (not exists $sort{$sort_method}) {
return "Invalid sort method '$sort_method'; valid methods are: " . join(', ', sort keys %sort) . "; prefix with - to invert sort direction.";
}
# insert from channel as first argument if first argument is not a channel
if ($args[0] !~ /^#/) {
unshift @args, $context->{from};
}
# ensure channel has a nicklist
if (not exists $self->{nicklist}->{lc $args[0]}) {
return "No nicklist for channel $args[0].";
}
my $result;
2020-02-15 23:38:32 +01:00
if (@args == 1) {
# nicklist for a specific channel
my $count = keys %{$self->{nicklist}->{lc $args[0]}};
$result = "$count nick" . ($count == 1 ? '' : 's') . " in $args[0]:\n";
foreach my $entry (sort { $sort{$sort_method}->($self->{nicklist}->{lc $args[0]}, $sort_direction) } keys %{$self->{nicklist}->{lc $args[0]}}) {
if ($full_hostmask) {
$result .= " $self->{nicklist}->{lc $args[0]}->{$entry}->{hostmask}";
} else {
$result .= " $self->{nicklist}->{lc $args[0]}->{$entry}->{nick}";
}
my $sep = ': ';
if ($self->{nicklist}->{lc $args[0]}->{$entry}->{timestamp} > 0) {
my $duration = concise ago (gettimeofday - $self->{nicklist}->{lc $args[0]}->{$entry}->{timestamp});
$result .= "${sep}last spoken $duration";
$sep = ', ';
}
if ($include_join and $self->{nicklist}->{lc $args[0]}->{$entry}->{join} > 0) {
my $duration = concise ago (gettimeofday - $self->{nicklist}->{lc $args[0]}->{$entry}->{join});
$result .= "${sep}joined $duration";
$sep = ', ';
}
foreach my $key (sort keys %{$self->{nicklist}->{lc $args[0]}->{$entry}}) {
next if grep { $key eq $_ } qw/nick user host join timestamp hostmask/;
if ($self->{nicklist}->{lc $args[0]}->{$entry}->{$key} == 1) {
$result .= "$sep$key";
} else {
$result .= "$sep$key => $self->{nicklist}->{lc $args[0]}->{$entry}->{$key}";
}
$sep = ', ';
}
$result .= "\n";
}
2020-02-15 23:38:32 +01:00
} else {
# nicklist for a specific user
if (not exists $self->{nicklist}->{lc $args[0]}->{lc $args[1]}) {
return "No such nick $args[1] in channel $args[0].";
}
$result = "Nicklist information for $self->{nicklist}->{lc $args[0]}->{lc $args[1]}->{hostmask} in $args[0]: ";
my $sep = '';
if ($self->{nicklist}->{lc $args[0]}->{lc $args[1]}->{timestamp} > 0) {
my $duration = concise ago (gettimeofday - $self->{nicklist}->{lc $args[0]}->{lc $args[1]}->{timestamp});
$result .= "last spoken $duration";
$sep = ', ';
}
if ($self->{nicklist}->{lc $args[0]}->{lc $args[1]}->{join} > 0) {
my $duration = concise ago (gettimeofday - $self->{nicklist}->{lc $args[0]}->{lc $args[1]}->{join});
$result .= "${sep}joined $duration";
$sep = ', ';
}
foreach my $key (sort keys %{$self->{nicklist}->{lc $args[0]}->{lc $args[1]}}) {
next if grep { $key eq $_ } qw/nick user host join timestamp hostmask/;
$result .= "$sep$key => $self->{nicklist}->{lc $args[0]}->{lc $args[1]}->{$key}";
$sep = ', ';
}
$result .= 'no details' if $sep eq '';
}
return $result;
}
sub update_timestamp {
2020-02-15 23:38:32 +01:00
my ($self, $channel, $nick) = @_;
2020-02-15 23:38:32 +01:00
my $orig_nick = $nick;
2020-02-15 23:38:32 +01:00
$channel = lc $channel;
$nick = lc $nick;
if (exists $self->{nicklist}->{$channel} and exists $self->{nicklist}->{$channel}->{$nick}) { $self->{nicklist}->{$channel}->{$nick}->{timestamp} = gettimeofday; }
else {
$self->{pbot}->{logger}->log("Adding nick '$orig_nick' to channel '$channel'\n") if $self->{pbot}->{registry}->get_value('nicklist', 'debug');
$self->{nicklist}->{$channel}->{$nick} = {nick => $orig_nick, timestamp => scalar gettimeofday};
}
}
sub remove_channel {
2020-02-15 23:38:32 +01:00
my ($self, $channel) = @_;
delete $self->{nicklist}->{lc $channel};
}
sub add_nick {
2020-02-15 23:38:32 +01:00
my ($self, $channel, $nick) = @_;
2020-02-15 23:38:32 +01:00
if (not exists $self->{nicklist}->{lc $channel}->{lc $nick}) {
$self->{pbot}->{logger}->log("Adding nick '$nick' to channel '$channel'\n") if $self->{pbot}->{registry}->get_value('nicklist', 'debug');
$self->{nicklist}->{lc $channel}->{lc $nick} = {nick => $nick, timestamp => 0};
}
}
sub remove_nick {
2020-02-15 23:38:32 +01:00
my ($self, $channel, $nick) = @_;
$self->{pbot}->{logger}->log("Removing nick '$nick' from channel '$channel'\n") if $self->{pbot}->{registry}->get_value('nicklist', 'debug');
delete $self->{nicklist}->{lc $channel}->{lc $nick};
}
sub get_channels {
2020-02-15 23:38:32 +01:00
my ($self, $nick) = @_;
2020-02-15 23:38:32 +01:00
$nick = lc $nick;
my @channels;
2020-02-15 23:38:32 +01:00
foreach my $channel (keys %{$self->{nicklist}}) {
if (exists $self->{nicklist}->{$channel}->{$nick}) {
push @channels, $channel;
}
}
2019-06-26 18:34:19 +02:00
2020-02-15 23:38:32 +01:00
return \@channels;
}
2020-02-06 19:47:19 +01:00
sub get_nicks {
2020-02-15 23:38:32 +01:00
my ($self, $channel) = @_;
2020-02-15 23:38:32 +01:00
$channel = lc $channel;
2020-02-15 23:38:32 +01:00
my @nicks;
2020-02-15 23:38:32 +01:00
return @nicks if not exists $self->{nicklist}->{$channel};
foreach my $nick (keys %{$self->{nicklist}->{$channel}}) {
push @nicks, $self->{nicklist}->{$channel}->{$nick}->{nick};
}
2020-02-15 23:38:32 +01:00
return @nicks;
2020-02-06 19:47:19 +01:00
}
sub set_meta {
2020-02-15 23:38:32 +01:00
my ($self, $channel, $nick, $key, $value) = @_;
$channel = lc $channel;
$nick = lc $nick;
if (not exists $self->{nicklist}->{$channel} or not exists $self->{nicklist}->{$channel}->{$nick}) {
if (exists $self->{nicklist}->{$channel} and $nick =~ m/[*?]/) {
my $regex = quotemeta $nick;
2020-02-15 23:38:32 +01:00
$regex =~ s/\\\*/.*?/g;
$regex =~ s/\\\?/./g;
my $found = 0;
2020-02-15 23:38:32 +01:00
foreach my $n (keys %{$self->{nicklist}->{$channel}}) {
if (exists $self->{nicklist}->{$channel}->{$n}->{hostmask} and $self->{nicklist}->{$channel}->{$n}->{hostmask} =~ m/$regex/i) {
$self->{nicklist}->{$channel}->{$n}->{$key} = $value;
$found++;
}
}
2020-02-15 23:38:32 +01:00
return $found;
} else {
$self->{pbot}->{logger}->log("Nicklist: Attempt to set invalid meta ($key => $value) for $nick in $channel.\n");
return 0;
}
}
2020-02-15 23:38:32 +01:00
$self->{nicklist}->{$channel}->{$nick}->{$key} = $value;
return 1;
}
sub delete_meta {
2020-02-15 23:38:32 +01:00
my ($self, $channel, $nick, $key) = @_;
2020-02-15 23:38:32 +01:00
$channel = lc $channel;
$nick = lc $nick;
2020-02-15 23:38:32 +01:00
if (not exists $self->{nicklist}->{$channel} or not exists $self->{nicklist}->{$channel}->{$nick} or not exists $self->{nicklist}->{$channel}->{$nick}->{$key}) {
return undef;
}
2020-02-15 23:38:32 +01:00
return delete $self->{nicklist}->{$channel}->{$nick}->{$key};
}
sub get_meta {
2020-02-15 23:38:32 +01:00
my ($self, $channel, $nick, $key) = @_;
2020-02-15 23:38:32 +01:00
$channel = lc $channel;
$nick = lc $nick;
2020-02-15 23:38:32 +01:00
if (not exists $self->{nicklist}->{$channel} or not exists $self->{nicklist}->{$channel}->{$nick} or not exists $self->{nicklist}->{$channel}->{$nick}->{$key}) {
return undef;
}
2020-02-15 23:38:32 +01:00
return $self->{nicklist}->{$channel}->{$nick}->{$key};
}
sub is_present_any_channel {
2020-02-15 23:38:32 +01:00
my ($self, $nick) = @_;
2020-02-15 23:38:32 +01:00
$nick = lc $nick;
2020-02-15 23:38:32 +01:00
foreach my $channel (keys %{$self->{nicklist}}) {
if (exists $self->{nicklist}->{$channel}->{$nick}) {
return $self->{nicklist}->{$channel}->{$nick}->{nick};
}
}
2020-02-15 23:38:32 +01:00
return 0;
}
sub is_present {
2020-02-15 23:38:32 +01:00
my ($self, $channel, $nick) = @_;
2020-02-15 23:38:32 +01:00
$channel = lc $channel;
$nick = lc $nick;
if (exists $self->{nicklist}->{$channel} and exists $self->{nicklist}->{$channel}->{$nick}) {
return $self->{nicklist}->{$channel}->{$nick}->{nick};
} else {
return 0;
}
}
sub is_present_similar {
my ($self, $channel, $nick, $similarity) = @_;
2020-02-15 23:38:32 +01:00
$channel = lc $channel;
$nick = lc $nick;
return 0 if not exists $self->{nicklist}->{$channel};
2020-02-15 23:38:32 +01:00
return $self->{nicklist}->{$channel}->{$nick}->{nick} if $self->is_present($channel, $nick);
if ($nick =~ m/(?:^\$|\s)/) {
# not nick-like
# TODO: why do we have this check? added log message to find out when/if it happens
$self->{pbot}->{logger}->log("NickList::is_present_similiar [$channel] [$nick] is not nick-like?\n");
return 0;
}
my $percentage;
if (defined $similarity) {
$percentage = $similarity;
} else {
$percentage = $self->{pbot}->{registry}->get_value('interpreter', 'nick_similarity') // 0.20;
}
2020-02-15 23:38:32 +01:00
my $now = gettimeofday;
foreach my $person (sort { $self->{nicklist}->{$channel}->{$b}->{timestamp} <=> $self->{nicklist}->{$channel}->{$a}->{timestamp} } keys %{$self->{nicklist}->{$channel}}) {
if ($now - $self->{nicklist}->{$channel}->{$person}->{timestamp} > 3600) {
# if it has been 1 hour since this person has last spoken, the similar nick
# is probably not intended for them.
return 0;
}
2020-02-15 23:38:32 +01:00
my $distance = fastdistance($nick, $person);
my $length = length $nick > length $person ? length $nick : length $person;
if ($length != 0 && $distance / $length <= $percentage) {
return $self->{nicklist}->{$channel}->{$person}->{nick};
}
}
2020-02-15 23:38:32 +01:00
return 0;
}
sub random_nick {
2020-02-15 23:38:32 +01:00
my ($self, $channel) = @_;
2020-02-15 23:38:32 +01:00
$channel = lc $channel;
2020-02-15 23:38:32 +01:00
if (exists $self->{nicklist}->{$channel}) {
my $now = gettimeofday;
# build list of nicks that have spoken within the last 2 hours
2020-02-15 23:38:32 +01:00
my @nicks = grep { $now - $self->{nicklist}->{$channel}->{$_}->{timestamp} < 3600 * 2 } keys %{$self->{nicklist}->{$channel}};
2019-06-26 18:34:19 +02:00
# pick a random nick from tha list
2020-02-15 23:38:32 +01:00
my $nick = $nicks[rand @nicks];
# return its canonical name
2020-02-15 23:38:32 +01:00
return $self->{nicklist}->{$channel}->{$nick}->{nick};
} else {
return undef;
}
}
sub on_namreply {
2020-02-15 23:38:32 +01:00
my ($self, $event_type, $event) = @_;
my ($channel, $nicks) = ($event->{event}->{args}[2], $event->{event}->{args}[3]);
2020-02-15 23:38:32 +01:00
foreach my $nick (split ' ', $nicks) {
my $stripped_nick = $nick;
2020-02-15 23:38:32 +01:00
$stripped_nick =~ s/^[@+%]//g; # remove OP/Voice/etc indicator from nick
2020-02-15 23:38:32 +01:00
$self->add_nick($channel, $stripped_nick);
2020-02-15 23:38:32 +01:00
my ($account_id, $hostmask) = $self->{pbot}->{messagehistory}->{database}->find_message_account_by_nick($stripped_nick);
2020-02-15 23:38:32 +01:00
if (defined $hostmask) {
my ($user, $host) = $hostmask =~ m/[^!]+!([^@]+)@(.*)/;
$self->set_meta($channel, $stripped_nick, 'hostmask', $hostmask);
$self->set_meta($channel, $stripped_nick, 'user', $user);
$self->set_meta($channel, $stripped_nick, 'host', $host);
}
2020-02-15 23:38:32 +01:00
if ($nick =~ m/\@/) { $self->set_meta($channel, $stripped_nick, '+o', 1); }
2020-02-15 23:38:32 +01:00
if ($nick =~ m/\+/) { $self->set_meta($channel, $stripped_nick, '+v', 1); }
2020-02-15 23:38:32 +01:00
if ($nick =~ m/\%/) { $self->set_meta($channel, $stripped_nick, '+h', 1); }
}
2020-02-15 23:38:32 +01:00
return 0;
}
sub on_activity {
2020-02-15 23:38:32 +01:00
my ($self, $event_type, $event) = @_;
2020-02-15 23:38:32 +01:00
my ($nick, $user, $host, $channel) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->{to}[0]);
2020-02-15 23:38:32 +01:00
$self->update_timestamp($channel, $nick);
2020-02-15 23:38:32 +01:00
return 0;
}
sub on_join {
2020-02-15 23:38:32 +01:00
my ($self, $event_type, $event) = @_;
2020-02-15 23:38:32 +01:00
my ($nick, $user, $host, $channel) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->to);
2020-02-15 23:38:32 +01:00
$self->add_nick($channel, $nick);
2020-02-15 23:38:32 +01:00
$self->set_meta($channel, $nick, 'hostmask', "$nick!$user\@$host");
$self->set_meta($channel, $nick, 'user', $user);
$self->set_meta($channel, $nick, 'host', $host);
$self->set_meta($channel, $nick, 'join', gettimeofday);
2020-02-15 23:38:32 +01:00
return 0;
}
sub on_part {
2020-02-15 23:38:32 +01:00
my ($self, $event_type, $event) = @_;
2020-02-15 23:38:32 +01:00
my ($nick, $user, $host, $channel) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->to);
2020-02-15 23:38:32 +01:00
$self->remove_nick($channel, $nick);
2020-02-15 23:38:32 +01:00
return 0;
}
sub on_quit {
2020-02-15 23:38:32 +01:00
my ($self, $event_type, $event) = @_;
my ($nick, $user, $host) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host);
2020-02-15 23:38:32 +01:00
foreach my $channel (keys %{$self->{nicklist}}) {
if ($self->is_present($channel, $nick)) {
$self->remove_nick($channel, $nick);
}
}
2020-02-15 23:38:32 +01:00
return 0;
}
sub on_kick {
2020-02-15 23:38:32 +01:00
my ($self, $event_type, $event) = @_;
2020-02-15 23:38:32 +01:00
my ($nick, $channel) = ($event->{event}->to, $event->{event}->{args}[0]);
2020-02-15 23:38:32 +01:00
$self->remove_nick($channel, $nick);
2020-02-15 23:38:32 +01:00
return 0;
}
sub on_nickchange {
2020-02-15 23:38:32 +01:00
my ($self, $event_type, $event) = @_;
my ($nick, $user, $host, $newnick) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->args);
foreach my $channel (keys %{$self->{nicklist}}) {
if ($self->is_present($channel, $nick)) {
my $meta = delete $self->{nicklist}->{$channel}->{lc $nick};
$meta->{nick} = $newnick;
$meta->{timestamp} = gettimeofday;
2020-02-15 23:38:32 +01:00
$self->{nicklist}->{$channel}->{lc $newnick} = $meta;
}
}
2020-02-15 23:38:32 +01:00
return 0;
}
sub on_self_join {
2020-02-15 23:38:32 +01:00
my ($self, $event_type, $event) = @_;
2020-02-15 23:38:32 +01:00
$self->remove_channel($event->{channel}); # clear nicklist to remove any stale nicks before repopulating with namreplies
2020-02-15 23:38:32 +01:00
return 0;
}
sub on_self_part {
2020-02-15 23:38:32 +01:00
my ($self, $event_type, $event) = @_;
2020-02-15 23:38:32 +01:00
$self->remove_channel($event->{channel});
2020-02-15 23:38:32 +01:00
return 0;
}
1;