mirror of
https://github.com/pragma-/pbot.git
synced 2024-11-26 22:09:26 +01:00
Add nick similarity completion
Previously, the bot wouldn't address people by nick if the provided nick argument doesn't exist in the channel. Now, the bot's nick list is searched for a nick with a certain similarity percentage in the order of most recently spoken nicks first. This allows the bot to address nicks when somebody may have forgotten to add a trailing underscore/punctuation or may have typoed the nick. The similarity percentage can be set via the interpreter->nick_similiarty registry key. A value of 0 should disable the behavior.
This commit is contained in:
parent
6369a8df99
commit
f34854fcec
@ -615,7 +615,7 @@ sub interpreter {
|
|||||||
$arguments = "";
|
$arguments = "";
|
||||||
} else {
|
} else {
|
||||||
if ($self->{factoids}->hash->{$channel}->{$keyword}->{type} eq 'text') {
|
if ($self->{factoids}->hash->{$channel}->{$keyword}->{type} eq 'text') {
|
||||||
my $target = $self->{pbot}->{nicklist}->is_present($from, $arguments);
|
my $target = $self->{pbot}->{nicklist}->is_present_similar($from, $arguments);
|
||||||
if ($target and $action !~ /\$nick/) {
|
if ($target and $action !~ /\$nick/) {
|
||||||
if ($action !~ m/^(\/[^ ]+) /) {
|
if ($action !~ m/^(\/[^ ]+) /) {
|
||||||
$action =~ s/^/\/say $target: $keyword is / unless defined $tonick;
|
$action =~ s/^/\/say $target: $keyword is / unless defined $tonick;
|
||||||
|
@ -99,7 +99,7 @@ sub process_line {
|
|||||||
if ($cmd_text =~ s/\B$bot_trigger`([^`]+)// || $cmd_text =~ s/\B$bot_trigger\{([^}]+)//) {
|
if ($cmd_text =~ s/\B$bot_trigger`([^`]+)// || $cmd_text =~ s/\B$bot_trigger\{([^}]+)//) {
|
||||||
my $cmd = $1;
|
my $cmd = $1;
|
||||||
my ($nick) = $cmd_text =~ m/^([^ ,:;]+)/;
|
my ($nick) = $cmd_text =~ m/^([^ ,:;]+)/;
|
||||||
$nick = $self->{pbot}->{nicklist}->is_present($from, $nick);
|
$nick = $self->{pbot}->{nicklist}->is_present_similar($from, $nick);
|
||||||
if ($nick) {
|
if ($nick) {
|
||||||
$command = "tell $nick about $cmd";
|
$command = "tell $nick about $cmd";
|
||||||
} else {
|
} else {
|
||||||
@ -114,6 +114,8 @@ sub process_line {
|
|||||||
$nick_override = $1;
|
$nick_override = $1;
|
||||||
$has_code = $2 if length $2 and $nick_override !~ /^(?:enum|struct|union)$/;
|
$has_code = $2 if length $2 and $nick_override !~ /^(?:enum|struct|union)$/;
|
||||||
$preserve_whitespace = 1;
|
$preserve_whitespace = 1;
|
||||||
|
my $similar = $self->{pbot}->{nicklist}->is_present_similar($from, $nick_override);
|
||||||
|
$nick_override = $similar if $similar;
|
||||||
$processed += 100;
|
$processed += 100;
|
||||||
} elsif($cmd_text =~ s/^$bot_trigger(.*)$//) {
|
} elsif($cmd_text =~ s/^$bot_trigger(.*)$//) {
|
||||||
$command = $1;
|
$command = $1;
|
||||||
@ -172,8 +174,12 @@ sub interpret {
|
|||||||
if($command =~ /^tell\s+(.{1,20})\s+about\s+(.*?)\s+(.*)$/i)
|
if($command =~ /^tell\s+(.{1,20})\s+about\s+(.*?)\s+(.*)$/i)
|
||||||
{
|
{
|
||||||
($keyword, $arguments, $tonick) = ($2, $3, $1);
|
($keyword, $arguments, $tonick) = ($2, $3, $1);
|
||||||
|
my $similar = $self->{pbot}->{nicklist}->is_present_similar($from, $tonick);
|
||||||
|
$tonick = $similar if $similar;
|
||||||
} elsif($command =~ /^tell\s+(.{1,20})\s+about\s+(.*)$/i) {
|
} elsif($command =~ /^tell\s+(.{1,20})\s+about\s+(.*)$/i) {
|
||||||
($keyword, $tonick) = ($2, $1);
|
($keyword, $tonick) = ($2, $1);
|
||||||
|
my $similar = $self->{pbot}->{nicklist}->is_present_similar($from, $tonick);
|
||||||
|
$tonick = $similar if $similar;
|
||||||
} elsif($command =~ /^(.*?)\s+(.*)$/) {
|
} elsif($command =~ /^(.*?)\s+(.*)$/) {
|
||||||
($keyword, $arguments) = ($1, $2);
|
($keyword, $arguments) = ($1, $2);
|
||||||
} else {
|
} else {
|
||||||
|
@ -10,8 +10,10 @@ package PBot::NickList;
|
|||||||
use warnings;
|
use warnings;
|
||||||
use strict;
|
use strict;
|
||||||
|
|
||||||
|
use Text::Levenshtein qw/fastdistance/;
|
||||||
use Data::Dumper;
|
use Data::Dumper;
|
||||||
use Carp ();
|
use Carp ();
|
||||||
|
use Time::HiRes qw/gettimeofday/;
|
||||||
|
|
||||||
sub new {
|
sub new {
|
||||||
Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference") if ref $_[1] eq 'HASH';
|
Carp::croak("Options to " . __FILE__ . " should be key/value pairs, not hash reference") if ref $_[1] eq 'HASH';
|
||||||
@ -37,6 +39,8 @@ sub initialize {
|
|||||||
$self->{pbot}->{event_dispatcher}->register_handler('irc.quit', sub { $self->on_quit(@_) });
|
$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.kick', sub { $self->on_kick(@_) });
|
||||||
$self->{pbot}->{event_dispatcher}->register_handler('irc.nick', sub { $self->on_nickchange(@_) });
|
$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(@_) });
|
||||||
$self->{pbot}->{event_dispatcher}->register_handler('irc.whospcrpl', sub { $self->on_whospcrpl(@_) });
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.whospcrpl', sub { $self->on_whospcrpl(@_) });
|
||||||
$self->{pbot}->{event_dispatcher}->register_handler('irc.endofwho', sub { $self->on_endofwho(@_) });
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.endofwho', sub { $self->on_endofwho(@_) });
|
||||||
|
|
||||||
@ -53,6 +57,16 @@ sub dumpnicks {
|
|||||||
return $nicklist;
|
return $nicklist;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub update_timestamp {
|
||||||
|
my ($self, $channel, $nick) = @_;
|
||||||
|
$channel = lc $channel;
|
||||||
|
$nick = lc $nick;
|
||||||
|
|
||||||
|
if (exists $self->{nicklist}->{$channel} and exists $self->{nicklist}->{$channel}->{$nick}) {
|
||||||
|
$self->{nicklist}->{$channel}->{$nick}->{timestamp} = gettimeofday;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub remove_channel {
|
sub remove_channel {
|
||||||
my ($self, $channel) = @_;
|
my ($self, $channel) = @_;
|
||||||
delete $self->{nicklist}->{lc $channel};
|
delete $self->{nicklist}->{lc $channel};
|
||||||
@ -61,7 +75,7 @@ sub remove_channel {
|
|||||||
sub add_nick {
|
sub add_nick {
|
||||||
my ($self, $channel, $nick) = @_;
|
my ($self, $channel, $nick) = @_;
|
||||||
$self->{pbot}->{logger}->log("Adding nick '$nick' to channel '$channel'\n") if $self->{pbot}->{registry}->get_value('nicklist', 'debug');
|
$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 };
|
$self->{nicklist}->{lc $channel}->{lc $nick} = { nick => $nick, timestamp => 0 };
|
||||||
}
|
}
|
||||||
|
|
||||||
sub remove_nick {
|
sub remove_nick {
|
||||||
@ -98,6 +112,35 @@ sub is_present {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub is_present_similar {
|
||||||
|
my ($self, $channel, $nick) = @_;
|
||||||
|
|
||||||
|
$channel = lc $channel;
|
||||||
|
$nick = lc $nick;
|
||||||
|
|
||||||
|
return 0 if not exists $self->{nicklist}->{$channel};
|
||||||
|
return $nick if $self->is_present($channel, $nick);
|
||||||
|
|
||||||
|
my $percentage = $self->{pbot}->{registry}->get_value('interpreter', 'nick_similarity');
|
||||||
|
$percentage = 0.20 if not defined $percentage;
|
||||||
|
|
||||||
|
foreach my $person (sort { $self->{nicklist}->{$channel}->{$b}->{timestamp} <=> $self->{nicklist}->{$channel}->{$a}->{timestamp} } keys $self->{nicklist}->{$channel}) {
|
||||||
|
my $distance = fastdistance($nick, $person);
|
||||||
|
my $length = length $nick > length $person ? length $nick : length $person;
|
||||||
|
|
||||||
|
=cut
|
||||||
|
my $p = $length != 0 ? $distance / $length : 0;
|
||||||
|
$self->{pbot}->{logger}->log("[$percentage] $nick <-> $person: $p %\n");
|
||||||
|
=cut
|
||||||
|
|
||||||
|
if ($length != 0 && $distance / $length <= $percentage) {
|
||||||
|
return $self->{nicklist}->{$channel}->{$person}->{nick};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
sub random_nick {
|
sub random_nick {
|
||||||
my ($self, $channel) = @_;
|
my ($self, $channel) = @_;
|
||||||
|
|
||||||
@ -124,6 +167,12 @@ sub on_namreply {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub on_activity {
|
||||||
|
my ($self, $event_type, $event) = @_;
|
||||||
|
my ($nick, $user, $host, $channel) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->{to}[0]);
|
||||||
|
$self->update_timestamp($channel, $nick);
|
||||||
|
}
|
||||||
|
|
||||||
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, $event->{event}->to);
|
||||||
|
Loading…
Reference in New Issue
Block a user