pbot/lib/PBot/Core/NickList.pm

253 lines
6.9 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.
2023-02-21 06:31:52 +01:00
# SPDX-FileCopyrightText: 2014-2023 Pragmatic Software <pragma78@gmail.com>
2021-07-11 00:00:22 +02:00
# 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
2021-07-21 07:44:51 +02:00
package PBot::Core::NickList;
use parent 'PBot::Core::Class';
2021-06-19 06:23:34 +02:00
use PBot::Imports;
2019-07-11 03:40:53 +02:00
use Text::Levenshtein::XS qw/distance/;
use Time::HiRes qw/gettimeofday/;
sub initialize {
2020-02-15 23:38:32 +01:00
my ($self, %conf) = @_;
# nicklist hashtable
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');
}
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 (not exists $self->{nicklist}->{$channel} or not exists $self->{nicklist}->{$channel}->{$nick}) {
$self->add_nick($channel, $orig_nick);
2020-02-15 23:38:32 +01:00
}
$self->{nicklist}->{$channel}->{$nick}->{timestamp} = 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}) {
if ($self->{pbot}->{registry}->get_value('nicklist', 'debug')) {
$self->{pbot}->{logger}->log("Adding nick '$nick' to channel '$channel'\n");
}
$self->{nicklist}->{lc $channel}->{lc $nick} = { nick => $nick, timestamp => 0, join => 0 };
2020-02-15 23:38:32 +01:00
}
}
sub remove_nick {
2020-02-15 23:38:32 +01:00
my ($self, $channel, $nick) = @_;
if ($self->{pbot}->{registry}->get_value('nicklist', 'debug')) {
$self->{pbot}->{logger}->log("Removing nick '$nick' from channel '$channel'\n");
}
2020-02-15 23:38:32 +01:00
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;
}
my $distance = distance($nick, $person);
2020-02-15 23:38:32 +01:00
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;
}
}
1;