From 9336428ea22bc5d9eb103613f351fe3ccbacf367 Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Sat, 29 Jan 2011 01:21:17 +0000 Subject: [PATCH] factoids: improved handling of factoids belonging to other channels; significantly --- PBot/DualIndexHashObject.pm | 68 ++++++++++++++------------------- PBot/Factoids.pm | 75 ++++++++++++++++++++++++++----------- PBot/VERSION.pm | 2 +- 3 files changed, 83 insertions(+), 62 deletions(-) diff --git a/PBot/DualIndexHashObject.pm b/PBot/DualIndexHashObject.pm index 97e97510..75a92bab 100644 --- a/PBot/DualIndexHashObject.pm +++ b/PBot/DualIndexHashObject.pm @@ -168,57 +168,37 @@ sub save { } sub find_index { - my ($self, $primary_index_key, $secondary_index_key) = @_; - my $result; + my $self = shift; + my ($primary_index_key, $secondary_index_key) = map {lc} @_; - if(not $secondary_index_key) { - $result = eval { - foreach my $index (keys %{ $self->hash }) { - if($primary_index_key =~ m/^\Q$index\E$/i) { - return $index; - } - } + return undef if not defined $primary_index_key; - return undef; - }; + return $primary_index_key if not $secondary_index_key; - if($@) { - Carp::carp ("find_index: bad regex: $@\n"); - return undef; - } - } else { - $result = eval { - foreach my $index (keys %{ $self->hash->{$primary_index_key} }) { - if($secondary_index_key =~ m/^\Q$index\E$/i) { - return $index; - } - } + return undef if not exists $self->hash->{$primary_index_key}; - return undef; - }; - - if($@) { - Carp::carp ("find_index: bad regex: $@\n"); - return undef; - } + foreach my $index (keys %{ $self->hash->{$primary_index_key} }) { + return $index if $secondary_index_key eq lc $index; } - return $result; + return undef; } sub levenshtein_matches { - my ($self, $primary_index_key, $secondary_index_key) = @_; + my ($self, $primary_index_key, $secondary_index_key, $distance) = @_; my $comma = ''; my $result = ""; + $distance = 0.60 if not defined $distance; + $primary_index_key = '.*' if not defined $primary_index_key; if(not $secondary_index_key) { foreach my $index (sort keys %{ $self->hash }) { - my $distance = fastdistance($primary_index_key, $index); + my $distance_result = fastdistance($primary_index_key, $index); my $length = (length($primary_index_key) > length($index)) ? length $primary_index_key : length $index; - if($distance / $length < 0.50) { + if($distance_result / $length < $distance) { $result .= $comma . $index; $comma = ", "; } @@ -230,13 +210,23 @@ sub levenshtein_matches { return 'none'; } - foreach my $index (sort keys %{ $self->hash->{$primary} }) { - my $distance = fastdistance($secondary_index_key, $index); - my $length = (length($secondary_index_key) > length($index)) ? length $secondary_index_key : length $index; + my $last_header = ""; + my $header = ""; - if($distance / $length < 0.60) { - $result .= $comma . $index; - $comma = ", "; + foreach my $index1 (sort keys %{ $self->hash }) { + $header = "[$index1] "; + $header = "[global channel] " if $header eq "[.*] "; + + foreach my $index2 (sort keys %{ $self->hash->{$index1} }) { + my $distance_result = fastdistance($secondary_index_key, $index2); + my $length = (length($secondary_index_key) > length($index2)) ? length $secondary_index_key : length $index2; + + if($distance_result / $length < $distance) { + $header = "" if $last_header eq $header; + $last_header = $header; + $result .= $comma . $header . $index2; + $comma = ", "; + } } } } diff --git a/PBot/Factoids.pm b/PBot/Factoids.pm index a9688edc..b6eb4119 100644 --- a/PBot/Factoids.pm +++ b/PBot/Factoids.pm @@ -37,10 +37,7 @@ sub initialize { my $export_path = delete $conf{export_path}; my $export_site = delete $conf{export_site}; - my $pbot = delete $conf{pbot}; - if(not defined $pbot) { - Carp::croak("Missing pbot reference to Factoids"); - } + my $pbot = delete $conf{pbot} || Carp::croak("Missing pbot reference to Factoids"); $self->{factoids} = PBot::DualIndexHashObject->new(name => 'Factoids', filename => $filename); $self->{export_path} = $export_path; @@ -199,23 +196,54 @@ sub find_factoid { sub interpreter { my $self = shift; - my ($from, $nick, $user, $host, $count, $keyword, $arguments, $tonick) = @_; + my ($from, $nick, $user, $host, $count, $keyword, $arguments, $tonick, $ref_from) = @_; my ($result, $channel); my $pbot = $self->{pbot}; - $from = lc $from; - return undef if not length $keyword; + $from = lc $from; + $ref_from = "" if not defined $ref_from; + my $original_keyword = $keyword; ($channel, $keyword) = $self->find_factoid($from, $keyword, $arguments); if(not defined $keyword) { - my $matches = $self->factoids->levenshtein_matches('.*', lc $original_keyword); + # first check all channels for this keyword + my $chans = ""; + my $comma = ""; + my $found = 0; + my ($fwd_chan, $fwd_trig); - return undef if $matches eq 'none'; + foreach my $chan (keys %{ $self->factoids->hash }) { + foreach my $trig (keys %{ $self->factoids->hash->{$chan} }) { + if(lc $trig eq lc $original_keyword) { + $chans .= $comma . $chan; + $comma = ", "; + $found++; + $fwd_chan = $chan; + $fwd_trig = $trig; + last; + } + } + } - return "No such factoid '$original_keyword'; did you mean $matches?"; + if($found > 1) { + return $ref_from . "Ambiguous keyword '$original_keyword' exists in multiple locations (choose one): $chans"; + } + elsif($found == 1) { + $pbot->logger->log("Found '$original_keyword' as '$fwd_trig' in [$fwd_chan]\n"); + + return $ref_from . $pbot->factoids->interpreter($fwd_chan, $nick, $user, $host, $count, $fwd_trig, $arguments, undef, "[$fwd_chan] "); + } else { + # if keyword hasn't been found, display similiar matches for all channels + my $matches = $self->factoids->levenshtein_matches('.*', lc $original_keyword); + + # don't say anything if nothing similiar was found + return undef if $matches eq 'none'; + + return $ref_from . "No such factoid '$original_keyword'; did you mean $matches?"; + } } my $type = $self->factoids->hash->{$channel}->{$keyword}->{type}; @@ -235,7 +263,7 @@ sub interpreter { $self->factoids->hash->{$channel}->{$keyword}->{ref_user} = $nick; $self->factoids->hash->{$channel}->{$keyword}->{last_referenced_on} = gettimeofday; - return $pbot->interpreter->interpret($from, $nick, $user, $host, $count, $command); + return $ref_from . $pbot->interpreter->interpret($from, $nick, $user, $host, $count, $command, $ref_from); } my $last_ref_in = 0; @@ -248,13 +276,13 @@ sub interpreter { } if(($last_ref_in == 1) and (gettimeofday - $self->factoids->hash->{$channel}->{$keyword}->{last_referenced_on} < $self->factoids->hash->{$channel}->{$keyword}->{rate_limit})) { - return "/msg $nick '$keyword' is rate-limited; try again in " . ($self->factoids->hash->{$channel}->{$keyword}->{rate_limit} - int(gettimeofday - $self->factoids->hash->{$channel}->{$keyword}->{last_referenced_on})) . " seconds."; + return "/msg $nick $ref_from'$keyword' is rate-limited; try again in " . ($self->factoids->hash->{$channel}->{$keyword}->{rate_limit} - int(gettimeofday - $self->factoids->hash->{$channel}->{$keyword}->{last_referenced_on})) . " seconds."; } } if($self->factoids->hash->{$channel}->{$keyword}->{enabled} == 0) { $self->{pbot}->logger->log("$keyword disabled.\n"); - return "/msg $nick $keyword is currently disabled."; + return "/msg $nick $ref_from$keyword is currently disabled."; } elsif($self->factoids->hash->{$channel}->{$keyword}->{type} eq 'module') { $self->{pbot}->logger->log("Found module\n"); @@ -264,7 +292,7 @@ sub interpreter { $self->factoids->hash->{$channel}->{$keyword}->{last_referenced_on} = gettimeofday; $self->factoids->hash->{$channel}->{$keyword}->{last_referenced_in} = $from || "stdin"; - return $self->{factoidmodulelauncher}->execute_module($from, $tonick, $nick, $user, $host, $keyword, $arguments); + return $ref_from . $self->{factoidmodulelauncher}->execute_module($from, $tonick, $nick, $user, $host, $keyword, $arguments); } elsif($self->factoids->hash->{$channel}->{$keyword}->{type} eq 'text') { $self->{pbot}->logger->log("Found factoid\n"); @@ -353,9 +381,9 @@ sub interpreter { if($result =~ s/^\/say\s+//i || $result =~ /^\/me\s+/i || $result =~ /^\/msg\s+/i) { - return $result; + return $ref_from . $result; } else { - return "$keyword is $result"; + return $ref_from . "$keyword is $result"; } } elsif($self->factoids->hash->{$channel}->{$keyword}->{type} eq 'regex') { $result = eval { @@ -382,21 +410,24 @@ sub interpreter { $cmd = $self->factoids->hash->{$channel}->{$keyword}->{action}; } - $result = $pbot->interpreter->interpret($from, $nick, $user, $host, $count, $cmd); - return $result; + $result = $pbot->interpreter->interpret($from, $nick, $user, $host, $count, $cmd, $ref_from); + return $ref_from . $result; }; if($@) { $self->{pbot}->logger->log("Regex fail: $@\n"); - return "/msg $nick Fail."; + return "/msg $nick $ref_from" . "Fail."; } - return $result; + return $ref_from . $result; } else { $self->{pbot}->logger->log("($from): $nick!$user\@$host): Unknown command type for '$keyword'\n"); - return "/me blinks."; + return "/me blinks." . " $ref_from"; } - return "/me wrinkles her nose."; + + # should never be reached; if it has, something has gone horribly wrong. + # (advanced notification of corruption or a waste of space?) + return "/me wrinkles her nose." . " $ref_from"; } sub export_path { diff --git a/PBot/VERSION.pm b/PBot/VERSION.pm index 9ad42451..85787fbb 100644 --- a/PBot/VERSION.pm +++ b/PBot/VERSION.pm @@ -13,7 +13,7 @@ use warnings; # These are set automatically by the build/commit script use constant { BUILD_NAME => "PBot", - BUILD_REVISION => 275, + BUILD_REVISION => 276, BUILD_DATE => "2011-01-28", };