From 02552081d5cd4ff834b1ab4bf699387cb7e689a9 Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Tue, 14 Jan 2020 18:10:53 -0800 Subject: [PATCH] Fix all potential case-sensitivity issues Misc clean-ups and refactors --- PBot/AntiFlood.pm | 88 +++---- PBot/AntiSpam.pm | 63 +++-- PBot/BanTracker.pm | 14 +- PBot/BotAdminCommands.pm | 33 ++- PBot/BotAdmins.pm | 61 ++--- PBot/ChanOpCommands.pm | 40 +-- PBot/ChanOps.pm | 73 +++--- PBot/Channels.pm | 81 +++--- PBot/DualIndexHashObject.pm | 287 +++++++++++---------- PBot/FactoidCommands.pm | 452 +++++++++++++++++++--------------- PBot/FactoidModuleLauncher.pm | 8 +- PBot/Factoids.pm | 254 ++++++++++--------- PBot/HashObject.pm | 163 +++++------- PBot/IRCHandlers.pm | 8 +- PBot/Interpreter.pm | 4 +- PBot/NickList.pm | 2 +- PBot/Refresher.pm | 4 +- PBot/Registry.pm | 74 +++--- PBot/RegistryCommands.pm | 24 +- PBot/Utils/SafeFilename.pm | 2 +- Plugins/AntiAway.pm | 2 +- Plugins/AutoRejoin.pm | 4 +- Plugins/Spinach.pm | 29 +-- 23 files changed, 898 insertions(+), 872 deletions(-) diff --git a/PBot/AntiFlood.pm b/PBot/AntiFlood.pm index 4f975958..cd3f4460 100644 --- a/PBot/AntiFlood.pm +++ b/PBot/AntiFlood.pm @@ -99,19 +99,20 @@ sub whitelisted { given ($mode) { when ('ban') { - return 1 if exists $self->{whitelist}->hash->{$channel} - and exists $self->{whitelist}->hash->{$channel}->{$hostmask} - and $self->{whitelist}->hash->{$channel}->{$hostmask}->{ban}; + return 1 if exists $self->{whitelist}->{hash}->{$channel} + and exists $self->{whitelist}->{hash}->{$channel}->{$hostmask} + and $self->{whitelist}->{hash}->{$channel}->{$hostmask}->{ban}; return 0; } default { my $ret = eval { - foreach my $chan (keys %{ $self->{whitelist}->hash }) { - next unless $channel =~ m/^$chan$/i; - foreach my $mask (keys %{ $self->{whitelist}->hash->{$chan} }) { - next if $self->{whitelist}->hash->{$chan}->{$mask}->{ban}; - return 1 if $hostmask =~ m/^$mask$/i and $self->{whitelist}->hash->{$chan}->{$mask}->{$mode}; + foreach my $chan (keys %{ $self->{whitelist}->{hash} }) { + next unless $channel eq $chan; + foreach my $mask (keys %{ $self->{whitelist}->{hash}->{$chan} }) { + next if $mask eq '_name'; + next if $self->{whitelist}->{hash}->{$chan}->{$mask}->{ban}; + return 1 if $hostmask eq $mask and $self->{whitelist}->{hash}->{$chan}->{$mask}->{$mode}; } } return 0; @@ -141,13 +142,14 @@ sub whitelist { when ($_ eq "list" or $_ eq "show") { my $text = "Whitelist:\n"; my $entries = 0; - foreach my $channel (keys %{ $self->{whitelist}->hash }) { - $text .= " $channel:\n"; - foreach my $mask (keys %{ $self->{whitelist}->hash->{$channel} }) { + foreach my $channel (keys %{ $self->{whitelist}->{hash} }) { + $text .= " $self->{whitelist}->{hash}->{$channel}->{_name}:\n"; + foreach my $mask (keys %{ $self->{whitelist}->{hash}->{$channel} }) { + next if $mask eq '_name'; my $mode = ''; - $mode .= 'u' if $self->{whitelist}->hash->{$channel}->{$mask}->{user}; - $mode .= 'b' if $self->{whitelist}->hash->{$channel}->{$mask}->{ban}; - $mode .= 'a' if $self->{whitelist}->hash->{$channel}->{$mask}->{antiflood}; + $mode .= 'u' if $self->{whitelist}->{hash}->{$channel}->{$mask}->{user}; + $mode .= 'b' if $self->{whitelist}->{hash}->{$channel}->{$mask}->{ban}; + $mode .= 'a' if $self->{whitelist}->{hash}->{$channel}->{$mask}->{antiflood}; $mode = '?' if not length $mode; $text .= " $mask [$mode],\n"; $entries++; @@ -160,23 +162,23 @@ sub whitelist { my ($channel, $mask, $flag, $value) = $self->{pbot}->{interpreter}->split_args($arglist, 4); return "Usage: whitelist set [flag] [value]" if not defined $channel or not defined $mask; - if (not exists $self->{whitelist}->hash->{$channel}) { + if (not exists $self->{whitelist}->{hash}->{lc $channel}) { return "There is no such channel `$channel` in the whitelist."; } - if (not exists $self->{whitelist}->hash->{$channel}->{$mask}) { + if (not exists $self->{whitelist}->{hash}->{lc $channel}->{lc $mask}) { return "There is no such mask `$mask` for channel `$channel` in the whitelist."; } if (not defined $flag) { my $text = "Flags:\n"; my $comma = ''; - foreach $flag (keys %{ $self->{whitelist}->hash->{$channel}->{$mask} }) { + foreach $flag (keys %{ $self->{whitelist}->{hash}->{lc $channel}->{lc $mask} }) { if ($flag eq 'created_on') { - my $timestamp = strftime "%a %b %e %H:%M:%S %Z %Y", localtime $self->{whitelist}->hash->{$channel}->{$mask}->{$flag}; + my $timestamp = strftime "%a %b %e %H:%M:%S %Z %Y", localtime $self->{whitelist}->{hash}->{lc $channel}->{lc $mask}->{$flag}; $text .= $comma . "created_on: $timestamp"; } else { - $value = $self->{whitelist}->hash->{$channel}->{$mask}->{$flag}; + $value = $self->{whitelist}->{hash}->{lc $channel}->{lc $mask}->{$flag}; $text .= $comma . "$flag: $value"; } $comma = ",\n "; @@ -185,7 +187,7 @@ sub whitelist { } if (not defined $value) { - $value = $self->{whitelist}->hash->{$channel}->{$mask}->{$flag}; + $value = $self->{whitelist}->{hash}->{lc $channel}->{lc $mask}->{$flag}; if (not defined $value) { return "/say $flag is not set."; } else { @@ -193,7 +195,7 @@ sub whitelist { } } - $self->{whitelist}->hash->{$channel}->{$mask}->{$flag} = $value; + $self->{whitelist}->{hash}->{lc $channel}->{lc $mask}->{$flag} = $value; $self->{whitelist}->save; return "Flag set."; } @@ -201,55 +203,45 @@ sub whitelist { my ($channel, $mask, $flag) = $self->{pbot}->{interpreter}->split_args($arglist, 3); return "Usage: whitelist unset " if not defined $channel or not defined $mask or not defined $flag; - if (not exists $self->{whitelist}->hash->{$channel}) { + if (not exists $self->{whitelist}->{hash}->{lc $channel}) { return "There is no such channel `$channel` in the whitelist."; } - if (not exists $self->{whitelist}->hash->{$channel}->{$mask}) { + if (not exists $self->{whitelist}->{hash}->{lc $channel}->{lc $mask}) { return "There is no such mask `$mask` for channel `$channel` in the whitelist."; } - if (not exists $self->{whitelist}->hash->{$channel}->{$mask}->{$flag}) { + if (not exists $self->{whitelist}->{hash}->{lc $channel}->{lc $mask}->{$flag}) { return "There is no such flag `$flag` for mask `$mask` for channel `$channel` in the whitelist."; } - delete $self->{whitelist}->hash->{$channel}->{$mask}->{$flag}; + delete $self->{whitelist}->{hash}->{lc $channel}->{lc $mask}->{$flag}; $self->{whitelist}->save; return "Flag unset."; } when ("add") { my ($channel, $mask, $mode) = $self->{pbot}->{interpreter}->split_args($arglist, 3); return "Usage: whitelist add [mode (user or ban, default: user)]" if not defined $channel or not defined $mask; - $mode = 'user' if not defined $mode; - if ($mode eq 'user') { - $self->{whitelist}->hash->{$channel}->{$mask}->{user} = 1; - } else { - $self->{whitelist}->hash->{$channel}->{$mask}->{ban} = 1; - } - $self->{whitelist}->hash->{$channel}->{$mask}->{owner} = "$nick!$user\@$host"; - $self->{whitelist}->hash->{$channel}->{$mask}->{created_on} = gettimeofday; + my $data = { + owner => "$nick!$user\@$host", + created_on => scalar gettimeofday + }; - $self->{whitelist}->save; + if ($mode eq 'user') { + $data->{user} = 1; + } else { + $data->{ban} = 1; + } + + $self->{whitelist}->add($channel, $mask, $data); return "/say $mask whitelisted in channel $channel"; } when ("remove") { my ($channel, $mask) = $self->{pbot}->{interpreter}->split_args($arglist, 2); return "Usage: whitelist remove " if not defined $channel or not defined $mask; - - if (not defined $self->{whitelist}->hash->{$channel}) { - return "No whitelists for channel $channel"; - } - - if (not defined $self->{whitelist}->hash->{$channel}->{$mask}) { - return "No such whitelist $mask for channel $channel"; - } - - delete $self->{whitelist}->hash->{$channel}->{$mask}; - delete $self->{whitelist}->hash->{$channel} if keys %{ $self->{whitelist}->hash->{$channel} } == 0; - $self->{whitelist}->save; - return "/say $mask whitelist removed from channel $channel"; + return $self->{whitelist}->remove($channel, $mask); } default { return "Unknown command '$command'; commands are: list/show, add, remove"; @@ -667,7 +659,7 @@ sub unbanme { foreach my $channel (@channels) { next if exists $unbanned->{$channel} and exists $unbanned->{$channel}->{$mask}; - next if not $self->{pbot}->{chanops}->{unban_timeout}->find_index($channel . '-floodbans', $mask); + next if not $self->{pbot}->{chanops}->{unban_timeout}->exists($channel . '-floodbans', $mask); my $message_account = $self->{pbot}->{messagehistory}->{database}->get_message_account($anick, $auser, $ahost); my @nickserv_accounts = $self->{pbot}->{messagehistory}->{database}->get_nickserv_accounts($message_account); diff --git a/PBot/AntiSpam.pm b/PBot/AntiSpam.pm index 526da036..3895242e 100644 --- a/PBot/AntiSpam.pm +++ b/PBot/AntiSpam.pm @@ -46,14 +46,16 @@ sub initialize { sub is_spam { my ($self, $namespace, $text, $all_namespaces) = @_; + my $lc_namespace = lc $namespace; return 0 if not $self->{pbot}->{registry}->get_value('antispam', 'enforce'); return 0 if $self->{pbot}->{registry}->get_value($namespace, 'dont_enforce_antispam'); my $ret = eval { - foreach my $space (keys %{ $self->{keywords}->hash }) { - if ($all_namespaces or $namespace =~ m/^$space$/i) { - foreach my $keyword (keys %{ $self->{keywords}->hash->{$space} }) { + foreach my $space (keys %{ $self->{keywords}->{hash} }) { + if ($all_namespaces or $lc_namespace eq $space) { + foreach my $keyword (keys %{ $self->{keywords}->{hash}->{$space} }) { + next if $keyword eq '_name'; return 1 if $text =~ m/$keyword/i; } } @@ -82,10 +84,11 @@ sub antispam_cmd { when ($_ eq "list" or $_ eq "show") { my $text = "Spam keywords:\n"; my $entries = 0; - foreach my $namespace (keys %{ $self->{keywords}->hash }) { - $text .= " $namespace:\n"; - foreach my $keyword (keys %{ $self->{keywords}->hash->{$namespace} }) { - $text .= " $keyword,\n"; + foreach my $namespace (keys %{ $self->{keywords}->{hash} }) { + $text .= " $self->{keywords}->{hash}->{$namespace}->{_name}:\n"; + foreach my $keyword (keys %{ $self->{keywords}->{hash}->{$namespace} }) { + next if $keyword eq '_name'; + $text .= " $self->{keywords}->{hash}->{$namespace}->{$keyword}->{_name},\n"; $entries++; } } @@ -96,23 +99,23 @@ sub antispam_cmd { my ($namespace, $keyword, $flag, $value) = $self->{pbot}->{interpreter}->split_args($arglist, 4); return "Usage: antispam set [flag] [value]" if not defined $namespace or not defined $keyword; - if (not exists $self->{keywords}->hash->{$namespace}) { + if (not exists $self->{keywords}->{hash}->{lc $namespace}) { return "There is no such namespace `$namespace`."; } - if (not exists $self->{keywords}->hash->{$namespace}->{$keyword}) { - return "There is no such regex `$keyword` for namespace `$namespace`."; + if (not exists $self->{keywords}->{hash}->{lc $namespace}->{lc $keyword}) { + return "There is no such regex `$keyword` for namespace `$self->{keywords}->{hash}->{$namespace}->{_name}`."; } if (not defined $flag) { my $text = "Flags:\n"; my $comma = ''; - foreach $flag (keys %{ $self->{keywords}->hash->{$namespace}->{$keyword} }) { + foreach $flag (keys %{ $self->{keywords}->{hash}->{lc $namespace}->{lc $keyword} }) { if ($flag eq 'created_on') { - my $timestamp = strftime "%a %b %e %H:%M:%S %Z %Y", localtime $self->{keywords}->hash->{$namespace}->{$keyword}->{$flag}; + my $timestamp = strftime "%a %b %e %H:%M:%S %Z %Y", localtime $self->{keywords}->{hash}->{lc $namespace}->{lc $keyword}->{$flag}; $text .= $comma . "created_on: $timestamp"; } else { - $value = $self->{keywords}->hash->{$namespace}->{$keyword}->{$flag}; + $value = $self->{keywords}->{hash}->{lc $namespace}->{lc $keyword}->{$flag}; $text .= $comma . "$flag: $value"; } $comma = ",\n "; @@ -121,7 +124,7 @@ sub antispam_cmd { } if (not defined $value) { - $value = $self->{keywords}->hash->{$namespace}->{$keyword}->{$flag}; + $value = $self->{keywords}->{hash}->{lc $namespace}->{lc $keyword}->{$flag}; if (not defined $value) { return "/say $flag is not set."; } else { @@ -129,7 +132,7 @@ sub antispam_cmd { } } - $self->{keywords}->hash->{$namespace}->{$keyword}->{$flag} = $value; + $self->{keywords}->{hash}->{lc $namespace}->{lc $keyword}->{$flag} = $value; $self->{keywords}->save; return "Flag set."; } @@ -137,46 +140,36 @@ sub antispam_cmd { my ($namespace, $keyword, $flag) = $self->{pbot}->{interpreter}->split_args($arglist, 3); return "Usage: antispam unset " if not defined $namespace or not defined $keyword or not defined $flag; - if (not exists $self->{keywords}->hash->{$namespace}) { + if (not exists $self->{keywords}->{hash}->{lc $namespace}) { return "There is no such namespace `$namespace`."; } - if (not exists $self->{keywords}->hash->{$namespace}->{$keyword}) { + if (not exists $self->{keywords}->{hash}->{lc $namespace}->{lc $keyword}) { return "There is no such keyword `$keyword` for namespace `$namespace`."; } - if (not exists $self->{keywords}->hash->{$namespace}->{$keyword}->{$flag}) { + if (not exists $self->{keywords}->{hash}->{lc $namespace}->{lc $keyword}->{$flag}) { return "There is no such flag `$flag` for regex `$keyword` for namespace `$namespace`."; } - delete $self->{keywords}->hash->{$namespace}->{$keyword}->{$flag}; + delete $self->{keywords}->{hash}->{lc $namespace}->{lc $keyword}->{$flag}; $self->{keywords}->save; return "Flag unset."; } when ("add") { my ($namespace, $keyword) = $self->{pbot}->{interpreter}->split_args($arglist, 2); return "Usage: antispam add " if not defined $namespace or not defined $keyword; - $self->{keywords}->hash->{$namespace}->{$keyword}->{owner} = "$nick!$user\@$host"; - $self->{keywords}->hash->{$namespace}->{$keyword}->{created_on} = gettimeofday; - $self->{keywords}->save; + my $data = { + owner => "$nick!$user\@$host", + created_on => scalar gettimeofday + }; + $self->{keywords}->add($namespace, $keyword, $data); return "/say Added `$keyword`."; } when ("remove") { my ($namespace, $keyword) = $self->{pbot}->{interpreter}->split_args($arglist, 2); return "Usage: antispam remove " if not defined $namespace or not defined $keyword; - - if (not defined $self->{keywords}->hash->{$namespace}) { - return "No entries for namespace $namespace"; - } - - if (not defined $self->{keywords}->hash->{$namespace}->{$keyword}) { - return "No such entry for namespace $namespace"; - } - - delete $self->{keywords}->hash->{$namespace}->{$keyword}; - delete $self->{keywords}->hash->{$namespace} if keys %{ $self->{keywords}->hash->{$namespace} } == 0; - $self->{keywords}->save; - return "/say Removed `$keyword`."; + return $self->{keywords}->remove($namespace, $keyword); } default { return "Unknown command '$command'; commands are: list/show, add, remove"; diff --git a/PBot/BanTracker.pm b/PBot/BanTracker.pm index b9989fce..0a692a62 100644 --- a/PBot/BanTracker.pm +++ b/PBot/BanTracker.pm @@ -92,11 +92,11 @@ sub on_banlist_entry { } if ($timeout && $self->{pbot}->{chanops}->can_gain_ops($channel)) { - if (not exists $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$target}) { + if (not exists $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$target}) { $self->{pbot}->{logger}->log("Temp ban for $target in $channel.\n"); - $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$target}{timeout} = gettimeofday + $timeout; - $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$target}{owner} = $source; - $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$target}{reason} = 'Temp ban on *!*@... or *!...@gateway/web'; + $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$target}->{timeout} = gettimeofday + $timeout; + $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$target}->{owner} = $source; + $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$target}->{reason} = 'Temp ban on *!*@... or *!...@gateway/web'; $self->{pbot}->{chanops}->{unban_timeout}->save; } } @@ -228,15 +228,15 @@ sub track_mode { delete $self->{banlist}->{$channel}->{$mode eq "-b" ? "+b" : "+q"}->{$target}; if ($mode eq "-b") { - if ($self->{pbot}->{chanops}->{unban_timeout}->find_index($channel, $target)) { + if ($self->{pbot}->{chanops}->{unban_timeout}->exists($channel, $target)) { $self->{pbot}->{chanops}->{unban_timeout}->remove($channel, $target); - } elsif ($self->{pbot}->{chanops}->{unban_timeout}->find_index($channel, "$target\$##stop_join_flood")) { + } elsif ($self->{pbot}->{chanops}->{unban_timeout}->exists($channel, "$target\$##stop_join_flood")) { # freenode strips channel forwards from unban result if no ban exists with a channel forward $self->{pbot}->{chanops}->{unban_timeout}->remove($channel, "$target\$##stop_join_flood"); } } elsif ($mode eq "-q") { - if ($self->{pbot}->{chanops}->{unmute_timeout}->find_index($channel, $target)) { + if ($self->{pbot}->{chanops}->{unmute_timeout}->exists($channel, $target)) { $self->{pbot}->{chanops}->{unmute_timeout}->remove($channel, $target); } } diff --git a/PBot/BotAdminCommands.pm b/PBot/BotAdminCommands.pm index 099eb454..8b08548b 100644 --- a/PBot/BotAdminCommands.pm +++ b/PBot/BotAdminCommands.pm @@ -158,10 +158,11 @@ sub adminrem { $channel = '.*' if $channel eq 'global'; - if (exists $self->{pbot}->{admins}->{admins}->hash->{$channel}) { - if (not exists $self->{pbot}->{admins}->{admins}->hash->{$channel}->{$hostmask}) { - foreach my $mask (keys %{ $self->{pbot}->{admins}->{admins}->hash->{$channel} }) { - if ($self->{pbot}->{admins}->{admins}->hash->{$channel}->{$mask}->{name} eq $hostmask) { + if (exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}) { + if (not exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$hostmask}) { + foreach my $mask (keys %{ $self->{pbot}->{admins}->{admins}->{hash}->{$channel} }) { + next if $mask eq '_name'; + if ($self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$mask}->{name} eq $hostmask) { $hostmask = $mask; last; } @@ -169,11 +170,7 @@ sub adminrem { } } - if ($self->{pbot}->{admins}->remove_admin($channel, $hostmask)) { - return "Admin removed."; - } else { - return "No such admin found."; - } + return $self->{pbot}->{admins}->remove_admin($channel, $hostmask); } sub adminset { @@ -190,10 +187,11 @@ sub adminset { $channel = '.*' if $channel eq 'global'; - if (exists $self->{pbot}->{admins}->{admins}->hash->{$channel}) { - if (not exists $self->{pbot}->{admins}->{admins}->hash->{$channel}->{$hostmask}) { - foreach my $mask (keys %{ $self->{pbot}->{admins}->{admins}->hash->{$channel} }) { - if ($self->{pbot}->{admins}->{admins}->hash->{$channel}->{$mask}->{name} eq $hostmask) { + if (exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}) { + if (not exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$hostmask}) { + foreach my $mask (keys %{ $self->{pbot}->{admins}->{admins}->{hash}->{$channel} }) { + next if $mask eq '_name'; + if ($self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$mask}->{name} eq $hostmask) { $hostmask = $mask; last; } @@ -239,10 +237,11 @@ sub adminunset { $channel = '.*' if $channel eq 'global'; - if (exists $self->{pbot}->{admins}->{admins}->hash->{$channel}) { - if (not exists $self->{pbot}->{admins}->{admins}->hash->{$channel}->{$hostmask}) { - foreach my $mask (keys %{ $self->{pbot}->{admins}->{admins}->hash->{$channel} }) { - if ($self->{pbot}->{admins}->{admins}->hash->{$channel}->{$mask}->{name} eq $hostmask) { + if (exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}) { + if (not exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$hostmask}) { + foreach my $mask (keys %{ $self->{pbot}->{admins}->{admins}->{hash}->{$channel} }) { + next if $mask eq '_name'; + if ($self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$mask}->{name} eq $hostmask) { $hostmask = $mask; last; } diff --git a/PBot/BotAdmins.pm b/PBot/BotAdmins.pm index cecd0d54..d9431ce3 100644 --- a/PBot/BotAdmins.pm +++ b/PBot/BotAdmins.pm @@ -38,45 +38,29 @@ sub initialize { sub add_admin { my $self = shift; my ($name, $channel, $hostmask, $level, $password, $dont_save) = @_; - $channel = lc $channel; $channel = '.*' if $channel !~ m/^#/; - $hostmask = lc $hostmask; - $self->{admins}->hash->{$channel}->{$hostmask}->{name} = $name; - $self->{admins}->hash->{$channel}->{$hostmask}->{level} = $level; - $self->{admins}->hash->{$channel}->{$hostmask}->{password} = $password; + + my $data = { + name => $name, + level => $level, + password => $password + }; + $self->{pbot}->{logger}->log("Adding new level $level admin: [$name] [$hostmask] for channel [$channel]\n"); - $self->save_admins unless $dont_save; + $self->{admins}->add($channel, $hostmask, $data, $dont_save); } sub remove_admin { my $self = shift; my ($channel, $hostmask) = @_; - - $channel = lc $channel; - $hostmask = lc $hostmask; - $channel = '.*' if $channel !~ m/^#/; - - my $admin = delete $self->{admins}->hash->{$channel}->{$hostmask}; - - if (not keys %{$self->{admins}->hash->{$channel}}) { - delete $self->{admins}->hash->{$channel}; - } - - if (defined $admin) { - $self->{pbot}->{logger}->log("Removed level $admin->{level} admin [$admin->{name}] [$hostmask] from channel [$channel]\n"); - $self->save_admins; - return 1; - } else { - $self->{pbot}->{logger}->log("Attempt to remove non-existent admin [$hostmask] from channel [$channel]\n"); - return 0; - } + return $self->{admins}->remove($channel, $hostmask); } sub load_admins { my $self = shift; my $filename; - if (@_) { $filename = shift; } else { $filename = $self->{admins}->filename; } + if (@_) { $filename = shift; } else { $filename = $self->{admins}->{filename}; } if (not defined $filename) { Carp::carp "No admins path specified -- skipping loading of admins"; @@ -86,18 +70,20 @@ sub load_admins { $self->{admins}->load; my $i = 0; - foreach my $channel (keys %{ $self->{admins}->hash } ) { - foreach my $hostmask (keys %{ $self->{admins}->hash->{$channel} }) { + foreach my $channel (sort keys %{ $self->{admins}->{hash} } ) { + foreach my $hostmask (sort keys %{ $self->{admins}->{hash}->{$channel} }) { + next if $hostmask eq '_name'; $i++; - my $name = $self->{admins}->hash->{$channel}->{$hostmask}->{name}; - my $level = $self->{admins}->hash->{$channel}->{$hostmask}->{level}; - my $password = $self->{admins}->hash->{$channel}->{$hostmask}->{password}; + my $name = $self->{admins}->{hash}->{$channel}->{$hostmask}->{name}; + my $level = $self->{admins}->{hash}->{$channel}->{$hostmask}->{level}; + my $password = $self->{admins}->{hash}->{$channel}->{$hostmask}->{password}; if (not defined $name or not defined $level or not defined $password) { - Carp::croak "Admin #$i of $filename is missing critical data\n"; + Carp::croak "An admin in $filename is missing critical data\n"; } - $self->{pbot}->{logger}->log("Adding new level $level admin: [$name] [$hostmask] for channel [$channel]\n"); + my $chan = $channel eq '.*' ? 'global' : $channel; + $self->{pbot}->{logger}->log("Adding new level $level $chan admin: $name $hostmask\n"); } } @@ -117,18 +103,19 @@ sub find_admin { $hostmask = lc $hostmask; my $result = eval { - foreach my $channel_regex (keys %{ $self->{admins}->hash }) { + foreach my $channel_regex (keys %{ $self->{admins}->{hash} }) { if ($from !~ m/^#/ or $from =~ m/^$channel_regex$/i) { - foreach my $hostmask_regex (keys %{ $self->{admins}->hash->{$channel_regex} }) { + foreach my $hostmask_regex (keys %{ $self->{admins}->{hash}->{$channel_regex} }) { + next if $hostmask_regex eq '_name'; if ($hostmask_regex =~ m/[*?]/) { # contains * or ? so it's converted to a regex my $hostmask_quoted = quotemeta $hostmask_regex; $hostmask_quoted =~ s/\\\*/.*?/g; $hostmask_quoted =~ s/\\\?/./g; - return $self->{admins}->hash->{$channel_regex}->{$hostmask_regex} if $hostmask =~ m/^$hostmask_quoted$/i; + return $self->{admins}->{hash}->{$channel_regex}->{$hostmask_regex} if $hostmask =~ m/^$hostmask_quoted$/i; } else { # direct comparison - return $self->{admins}->hash->{$channel_regex}->{$hostmask_regex} if $hostmask eq lc $hostmask_regex; + return $self->{admins}->{hash}->{$channel_regex}->{$hostmask_regex} if $hostmask eq lc $hostmask_regex; } } } diff --git a/PBot/ChanOpCommands.pm b/PBot/ChanOpCommands.pm index 2f2eebe0..e640d6fb 100644 --- a/PBot/ChanOpCommands.pm +++ b/PBot/ChanOpCommands.pm @@ -278,13 +278,13 @@ sub checkban { $channel = lc $channel; $target = lc $target; - my $mask = $self->{pbot}->{chanops}->nick_to_banmask($target); + my $mask = lc $self->{pbot}->{chanops}->nick_to_banmask($target); - if (exists $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel} - && exists $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$mask}) { - my $timeout = $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$mask}{timeout}; - my $owner = $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$mask}{owner}; - my $reason = $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$mask}{reason}; + if (exists $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel} + && exists $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$mask}) { + my $timeout = $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$mask}->{timeout}; + my $owner = $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$mask}->{owner}; + my $reason = $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$mask}->{reason}; my $duration = concise duration($timeout - gettimeofday); my $result = "$mask banned in $channel "; @@ -316,13 +316,13 @@ sub checkmute { $channel = lc $channel; $target = lc $target; - my $mask = $self->{pbot}->{chanops}->nick_to_banmask($target); + my $mask = lc $self->{pbot}->{chanops}->nick_to_banmask($target); - if (exists $self->{pbot}->{chanops}->{unmute_timeout}->hash->{$channel} - && exists $self->{pbot}->{chanops}->{unmute_timeout}->hash->{$channel}->{$mask}) { - my $timeout = $self->{pbot}->{chanops}->{unmute_timeout}->hash->{$channel}->{$mask}{timeout}; - my $owner = $self->{pbot}->{chanops}->{unmute_timeout}->hash->{$channel}->{$mask}{owner}; - my $reason = $self->{pbot}->{chanops}->{unmute_timeout}->hash->{$channel}->{$mask}{reason}; + if (exists $self->{pbot}->{chanops}->{unmute_timeout}->{hash}->{$channel} + && exists $self->{pbot}->{chanops}->{unmute_timeout}->{hash}->{$channel}->{$mask}) { + my $timeout = $self->{pbot}->{chanops}->{unmute_timeout}->{hash}->{$channel}->{$mask}->{timeout}; + my $owner = $self->{pbot}->{chanops}->{unmute_timeout}->{hash}->{$channel}->{$mask}->{owner}; + my $reason = $self->{pbot}->{chanops}->{unmute_timeout}->{hash}->{$channel}->{$mask}->{reason}; my $duration = concise duration($timeout - gettimeofday); my $result = "$mask muted in $channel "; @@ -386,11 +386,11 @@ sub ban_user { my @targets = split /,/, $target; my $immediately = @targets > 1 ? 0 : 1; foreach my $t (@targets) { - my $mask = $self->{pbot}->{chanops}->nick_to_banmask($t); + my $mask = lc $self->{pbot}->{chanops}->nick_to_banmask($t); - if ($no_length && exists $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel} - && exists $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$mask}) { - my $timeout = $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$mask}{timeout}; + if ($no_length && exists $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel} + && exists $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$mask}) { + my $timeout = $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{$channel}->{$mask}->{timeout}; my $duration = duration($timeout - gettimeofday); $result .= "$sep$mask has $duration remaining on their $channel ban"; @@ -517,11 +517,11 @@ sub mute_user { my @targets = split /,/, $target; my $immediately = @targets > 1 ? 0 : 1; foreach my $t (@targets) { - my $mask = $self->{pbot}->{chanops}->nick_to_banmask($t); + my $mask = lc $self->{pbot}->{chanops}->nick_to_banmask($t); - if ($no_length && exists $self->{pbot}->{chanops}->{unmute_timeout}->hash->{$channel} - && exists $self->{pbot}->{chanops}->{unmute_timeout}->hash->{$channel}->{$mask}) { - my $timeout = $self->{pbot}->{chanops}->{unmute_timeout}->hash->{$channel}->{$mask}{timeout}; + if ($no_length && exists $self->{pbot}->{chanops}->{unmute_timeout}->{hash}->{$channel} + && exists $self->{pbot}->{chanops}->{unmute_timeout}->{hash}->{$channel}->{$mask}) { + my $timeout = $self->{pbot}->{chanops}->{unmute_timeout}->{hash}->{$channel}->{$mask}->{timeout}; my $duration = duration($timeout - gettimeofday); $result .= "$sep$mask has $duration remaining on their $channel mute"; diff --git a/PBot/ChanOps.pm b/PBot/ChanOps.pm index 87ffa31f..6b9cb6d2 100644 --- a/PBot/ChanOps.pm +++ b/PBot/ChanOps.pm @@ -69,9 +69,9 @@ sub initialize { sub can_gain_ops { my ($self, $channel) = @_; $channel = lc $channel; - return exists $self->{pbot}->{channels}->{channels}->hash->{$channel} - && $self->{pbot}->{channels}->{channels}->hash->{$channel}{chanop} - && $self->{pbot}->{channels}->{channels}->hash->{$channel}{enabled}; + return exists $self->{pbot}->{channels}->{channels}->{hash}->{$channel} + && $self->{pbot}->{channels}->{channels}->{hash}->{$channel}->{chanop} + && $self->{pbot}->{channels}->{channels}->{hash}->{$channel}->{enabled}; } sub gain_ops { @@ -240,14 +240,14 @@ sub ban_user_timed { $self->ban_user($mask, $channel, $immediately); if ($length > 0) { - $self->{unban_timeout}->hash->{$channel}->{$mask}{timeout} = gettimeofday + $length; - $self->{unban_timeout}->hash->{$channel}->{$mask}{owner} = $owner if defined $owner; - $self->{unban_timeout}->hash->{$channel}->{$mask}{reason} = $reason if defined $reason; - $self->{unban_timeout}->save; + my $data = { + timeout => gettimeofday + $length + }; + $data->{owner} = $owner if defined $owner; + $data->{reason} = $reason if defined $reason; + $self->{unban_timeout}->add($channel, $mask, $data); } else { - if ($self->{pbot}->{chanops}->{unban_timeout}->find_index($channel, $mask)) { - $self->{pbot}->{chanops}->{unban_timeout}->remove($channel, $mask); - } + $self->{unban_timeout}->remove($channel, $mask); } } @@ -281,14 +281,14 @@ sub mute_user_timed { $self->mute_user($mask, $channel, $immediately); if ($length > 0) { - $self->{unmute_timeout}->hash->{$channel}->{$mask}{timeout} = gettimeofday + $length; - $self->{unmute_timeout}->hash->{$channel}->{$mask}{owner} = $owner if defined $owner; - $self->{unmute_timeout}->hash->{$channel}->{$mask}{reason} = $reason if defined $reason; - $self->{unmute_timeout}->save; + my $data = { + timeout => gettimeofday + $length + }; + $data->{owner} = $owner if defined $owner; + $data->{reason} = $reason if defined $reason; + $self->{unmute_timeout}->add($channel, $mask, $data); } else { - if ($self->{pbot}->{chanops}->{unmute_timeout}->find_index($channel, $mask)) { - $self->{pbot}->{chanops}->{unmute_timeout}->remove($channel, $mask); - } + $self->{unmute_timeout}->remove($channel, $mask); } } @@ -304,9 +304,8 @@ sub join_channel { delete $self->{is_opped}->{$channel}; delete $self->{op_requested}->{$channel}; - if (exists $self->{pbot}->{channels}->{channels}->hash->{$channel} - and exists $self->{pbot}->{channels}->{channels}->hash->{$channel}{permop} - and $self->{pbot}->{channels}->{channels}->hash->{$channel}{permop}) { + if (exists $self->{pbot}->{channels}->{channels}->{hash}->{lc $channel} + and $self->{pbot}->{channels}->{channels}->{hash}->{lc $channel}->{permop}) { $self->gain_ops($channel); } @@ -328,12 +327,12 @@ sub part_channel { sub has_ban_timeout { my ($self, $channel, $mask) = @_; - return exists $self->{unban_timeout}->hash->{lc $channel}->{lc $mask}; + return exists $self->{unban_timeout}->{hash}->{lc $channel}->{lc $mask}; } sub has_mute_timeout { my ($self, $channel, $mask) = @_; - return exists $self->{unmute_timeout}->hash->{lc $channel}->{lc $mask}; + return exists $self->{unmute_timeout}->{hash}->{lc $channel}->{lc $mask}; } sub add_to_ban_queue { @@ -443,10 +442,11 @@ sub check_unban_timeouts { my $now = gettimeofday(); - foreach my $channel (keys %{ $self->{unban_timeout}->hash }) { - foreach my $mask (keys %{ $self->{unban_timeout}->hash->{$channel} }) { - if ($self->{unban_timeout}->hash->{$channel}->{$mask}{timeout} < $now) { - $self->{unban_timeout}->hash->{$channel}->{$mask}{timeout} = $now + 7200; + foreach my $channel (keys %{ $self->{unban_timeout}->{hash} }) { + foreach my $mask (keys %{ $self->{unban_timeout}->{hash}->{$channel} }) { + next if $mask eq '_name'; + if ($self->{unban_timeout}->{hash}->{$channel}->{$mask}->{timeout} < $now) { + $self->{unban_timeout}->{hash}->{$channel}->{$mask}->{timeout} = $now + 7200; $self->unban_user($mask, $channel); } } @@ -460,10 +460,11 @@ sub check_unmute_timeouts { my $now = gettimeofday(); - foreach my $channel (keys %{ $self->{unmute_timeout}->hash }) { - foreach my $mask (keys %{ $self->{unmute_timeout}->hash->{$channel} }) { - if ($self->{unmute_timeout}->hash->{$channel}->{$mask}{timeout} < $now) { - $self->{unmute_timeout}->hash->{$channel}->{$mask}{timeout} = $now + 7200; + foreach my $channel (keys %{ $self->{unmute_timeout}->{hash} }) { + foreach my $mask (keys %{ $self->{unmute_timeout}->{hash}->{$channel} }) { + next if $mask eq '_name'; + if ($self->{unmute_timeout}->{hash}->{$channel}->{$mask}->{timeout} < $now) { + $self->{unmute_timeout}->{hash}->{$channel}->{$mask}->{timeout} = $now + 7200; $self->unmute_user($mask, $channel); } } @@ -476,21 +477,17 @@ sub check_opped_timeouts { foreach my $channel (keys %{ $self->{is_opped} }) { if ($self->{is_opped}->{$channel}{timeout} < $now) { - unless (exists $self->{pbot}->{channels}->{channels}->hash->{$channel} - and exists $self->{pbot}->{channels}->{channels}->hash->{$channel}{permop} - and $self->{pbot}->{channels}->{channels}->hash->{$channel}{permop}) { + unless (exists $self->{pbot}->{channels}->{channels}->{hash}->{lc $channel} + and $self->{pbot}->{channels}->{channels}->{hash}->{lc $channel}->{permop}) { $self->lose_ops($channel); } - } else { - # my $timediff = $self->{is_opped}->{$channel}{timeout} - $now; - # $self->{pbot}->{logger}->log("deop $channel in $timediff seconds\n"); } } foreach my $channel (keys %{ $self->{op_requested} }) { if ($now - $self->{op_requested}->{$channel} > 60 * 5) { - if (exists $self->{pbot}->{channels}->{channels}->hash->{$channel} - and $self->{pbot}->{channels}->{channels}->hash->{$channel}{enabled}) { + if (exists $self->{pbot}->{channels}->{channels}->{hash}->{lc $channel} + and $self->{pbot}->{channels}->{channels}->{hash}->{lc $channel}->{enabled}) { $self->{pbot}->{logger}->log("5 minutes since OP request for $channel and no OP yet; trying again ...\n"); delete $self->{op_requested}->{$channel}; $self->gain_ops($channel); diff --git a/PBot/Channels.pm b/PBot/Channels.pm index 1bf6b13a..d0d53b1b 100644 --- a/PBot/Channels.pm +++ b/PBot/Channels.pm @@ -18,12 +18,8 @@ use Carp (); use PBot::HashObject; sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak ("Options to " . __FILE__ . " should be key/value pairs, not hash reference"); - } - + Carp::croak ("Options to " . __FILE__ . " should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH'; my ($class, %conf) = @_; - my $self = bless {}, $class; $self->initialize(%conf); return $self; @@ -32,37 +28,29 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - $self->{channels} = PBot::HashObject->new(pbot => $self->{pbot}, name => 'Channels', filename => delete $conf{filename}); + $self->{channels} = PBot::HashObject->new(pbot => $self->{pbot}, name => 'Channels', filename => $conf{filename}); $self->load_channels; - $self->{pbot}->{commands}->register(sub { $self->set(@_) }, "chanset", 40); - $self->{pbot}->{commands}->register(sub { $self->unset(@_) }, "chanunset", 40); - $self->{pbot}->{commands}->register(sub { $self->add(@_) }, "chanadd", 40); - $self->{pbot}->{commands}->register(sub { $self->remove(@_) }, "chanrem", 40); - $self->{pbot}->{commands}->register(sub { $self->list(@_) }, "chanlist", 10); + $self->{pbot}->{commands}->register(sub { $self->set(@_) }, "chanset", 40); + $self->{pbot}->{commands}->register(sub { $self->unset(@_) }, "chanunset", 40); + $self->{pbot}->{commands}->register(sub { $self->add(@_) }, "chanadd", 40); + $self->{pbot}->{commands}->register(sub { $self->remove(@_) }, "chanrem", 40); + $self->{pbot}->{commands}->register(sub { $self->list(@_) }, "chanlist", 10); } sub set { my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_; my ($channel, $key, $value) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 3); - - if (not defined $channel) { - return "Usage: chanset [key [value]]"; - } - + return "Usage: chanset [key [value]]" if not defined $channel; return $self->{channels}->set($channel, $key, $value); } sub unset { my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_; my ($channel, $key) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 2); - - if (not defined $channel or not defined $key) { - return "Usage: chanunset "; - } - + return "Usage: chanunset " if not defined $channel or not defined $key; return $self->{channels}->unset($channel, $key); } @@ -73,12 +61,13 @@ sub add { return "Usage: chanadd "; } - my $hash = {}; - $hash->{enabled} = 1; - $hash->{chanop} = 0; - $hash->{permop} = 0; + my $data = { + enabled => 1, + chanop => 0, + permop => 0 + }; - return $self->{channels}->add($arguments, $hash); + return $self->{channels}->add($arguments, $data); } sub remove { @@ -95,11 +84,11 @@ sub list { my ($self, $from, $nick, $user, $host, $arguments) = @_; my $result; - foreach my $index (sort keys %{ $self->{channels}->hash }) { - $result .= "$index: {"; + foreach my $index (sort keys %{ $self->{channels}->{hash} }) { + $result .= "$self->{channels}->{hash}->{$index}->{_name}: {"; my $comma = ' '; - foreach my $key (sort keys %{ ${ $self->{channels}->hash }{$index} }) { - $result .= "$comma$key => ${ $self->{channels}->hash }{$index}{$key}"; + foreach my $key (sort keys %{ $self->{channels}->{hash}->{$index} }) { + $result .= "$comma$key => $self->{channels}->{hash}->{$index}->{$key}"; $comma = ', '; } $result .= " }\n"; @@ -111,9 +100,9 @@ sub autojoin { my ($self) = @_; return if $self->{pbot}->{joined_channels}; my $chans; - foreach my $chan (keys %{ $self->{pbot}->{channels}->{channels}->hash }) { - if ($self->{pbot}->{channels}->{channels}->hash->{$chan}{enabled}) { - $chans .= "$chan,"; + foreach my $chan (keys %{ $self->{channels}->{hash} }) { + if ($self->{channels}->{hash}->{$chan}->{enabled}) { + $chans .= "$self->{channels}->{hash}->{$chan}->{_name},"; } } $self->{pbot}->{logger}->log("Joining channels: $chans\n"); @@ -123,34 +112,30 @@ sub autojoin { sub is_active { my ($self, $channel) = @_; - return exists $self->{channels}->hash->{$channel} && $self->{channels}->hash->{$channel}->{enabled}; + my $lc_channel = lc $channel; + return exists $self->{channels}->{hash}->{$lc_channel} && $self->{channels}->{hash}->{$lc_channel}->{enabled}; } sub is_active_op { my ($self, $channel) = @_; - return $self->is_active($channel) && $self->{channels}->hash->{$channel}->{chanop}; + return $self->is_active($channel) && $self->{channels}->{hash}->{lc $channel}->{chanop}; } sub get_meta { my ($self, $channel, $key) = @_; - my $index = $self->{channels}->find_hash($channel); - return undef if not defined $index; - return $self->{channels}->{hash}->{$index}->{$key}; + my $lc_channel = lc $channel; + return undef if not exists $self->{channels}->{hash}->{$lc_channel}; + return $self->{channels}->{hash}->{$lc_channel}->{$key}; } sub load_channels { - my $self = shift; - $self->{channels}->load(); + my ($self) = @_; + $self->{channels}->load; } sub save_channels { - my $self = shift; - $self->{channels}->save(); -} - -sub channels { - my $self = shift; - return $self->{channels}; + my ($self) = @_; + $self->{channels}->save; } 1; diff --git a/PBot/DualIndexHashObject.pm b/PBot/DualIndexHashObject.pm index e95a0a4b..22fd660b 100644 --- a/PBot/DualIndexHashObject.pm +++ b/PBot/DualIndexHashObject.pm @@ -2,7 +2,10 @@ # Author: pragma_ # # Purpose: Provides a hash-table object with an abstracted API that includes -# setting and deleting values, saving to and loading from files, etc. +# setting and deleting values, saving to and loading from files, etc. This +# extends the HashObject with an additional index key. Provides case-insensitive +# access to both index keys, while preserving original case when displaying the +# keys. # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this @@ -20,12 +23,8 @@ use JSON; use Carp (); sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to DualIndexHashObject should be key/value pairs, not hash reference"); - } - + Carp::croak("Options to DualIndexHashObject should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH'; my ($class, %conf) = @_; - my $self = bless {}, $class; $self->initialize(%conf); return $self; @@ -33,34 +32,16 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{name} = delete $conf{name} // 'Dual Index hash object'; $self->{filename} = delete $conf{filename} // Carp::carp("Missing filename to DualIndexHashObject, will not be able to save to or load from file."); $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{hash} = {}; } - -sub hash_add { - my ($self, $primary_index_key, $secondary_index_key, $hash) = @_; - - if (defined $hash) { - if (exists $self->hash->{$primary_index_key}->{$secondary_index_key}) { - return undef; - } - - foreach my $key (keys %$hash) { - $self->hash->{$primary_index_key}->{$secondary_index_key}->{$key} = $hash->{$key}; - } - return 1; - } - return undef; -} - sub load { my ($self, $filename) = @_; - $filename = $self->filename if not defined $filename; + $filename = $self->{filename} if not defined $filename; if (not defined $filename) { Carp::carp "No $self->{name} filename specified -- skipping loading from file"; @@ -81,13 +62,50 @@ sub load { $self->{hash} = decode_json $contents if length $contents; close FILE; + + # update existing entries to use _name to preserve case + # and lowercase any non-lowercased entries + foreach my $primary_index (keys %{ $self->{hash} }) { + if (not exists $self->{hash}->{$primary_index}->{_name}) { + if (lc $primary_index eq $primary_index) { + $self->{hash}->{$primary_index}->{_name} = $primary_index; + } else { + if (exists $self->{hash}->{lc $primary_index}) { + Carp::croak "Cannot update $self->{name} primary index $primary_index; duplicate object found"; + } + + my $data = delete $self->{hash}->{$primary_index}; + $data->{_name} = $primary_index; + $primary_index = lc $primary_index; + $self->{hash}->{$primary_index} = $data; + } + } + + foreach my $secondary_index (keys %{ $self->{hash}->{$primary_index} }) { + next if $secondary_index eq '_name'; + if (not exists $self->{hash}->{$primary_index}->{$secondary_index}->{_name}) { + if (lc $secondary_index eq $secondary_index) { + $self->{hash}->{$primary_index}->{$secondary_index}->{_name} = $secondary_index; + } else { + if (exists $self->{hash}->{$primary_index}->{lc $secondary_index}) { + Carp::croak "Cannot update $self->{name} $primary_index sub-object $secondary_index; duplicate object found"; + } + + my $data = delete $self->{hash}->{$primary_index}->{$secondary_index}; + $data->{_name} = $secondary_index; + $secondary_index = lc $secondary_index; + $self->{hash}->{$primary_index}->{$secondary_index} = $data; + } + } + } + } } sub save { my $self = shift; my $filename; - if (@_) { $filename = shift; } else { $filename = $self->filename; } + if (@_) { $filename = shift; } else { $filename = $self->{filename}; } if (not defined $filename) { Carp::carp "No $self->{name} filename specified -- skipping saving to file.\n"; @@ -109,77 +127,61 @@ sub clear { $self->{hash} = {}; } -sub find_index { - my $self = shift; - my ($primary_index_key, $secondary_index_key) = map {lc} @_; - - return undef if not defined $primary_index_key; - - return undef if not exists $self->hash->{$primary_index_key}; - - return $primary_index_key if not defined $secondary_index_key; - - foreach my $index (keys %{ $self->hash->{$primary_index_key} }) { - return $index if $secondary_index_key eq lc $index; - } - - return undef; -} - sub levenshtein_matches { - my ($self, $primary_index_key, $secondary_index_key, $distance, $strictnamespace) = @_; + my ($self, $primary_index, $secondary_index, $distance, $strictnamespace) = @_; my $comma = ''; my $result = ""; $distance = 0.60 if not defined $distance; - $primary_index_key = '.*' if not defined $primary_index_key; + $primary_index = '.*' if not defined $primary_index; - if (not $secondary_index_key) { - foreach my $index (sort keys %{ $self->hash }) { - my $distance_result = fastdistance($primary_index_key, $index); - my $length = (length($primary_index_key) > length($index)) ? length $primary_index_key : length $index; + if (not $secondary_index) { + foreach my $index (sort keys %{ $self->{hash} }) { + my $distance_result = fastdistance($primary_index, $index); + my $length = (length $primary_index > length $index) ? length $primary_index : length $index; if ($distance_result / $length < $distance) { - if ($index =~ / /) { - $result .= $comma . "\"$index\""; + my $name = $self->{hash}->{$index}->{_name}; + if ($name =~ / /) { + $result .= $comma . "\"$name\""; } else { - $result .= $comma . $index; + $result .= $comma . $name; } $comma = ", "; } } } else { - my $primary = $self->find_index($primary_index_key); - - if (not $primary) { + my $lc_primary_index = lc $primary_index; + if (not exists $self->{hash}->{$lc_primary_index}) { return 'none'; } my $last_header = ""; my $header = ""; - foreach my $index1 (sort keys %{ $self->hash }) { - $header = "[$index1] "; - $header = "[global channel] " if $header eq "[.*] "; + foreach my $index1 (sort keys %{ $self->{hash} }) { + $header = "[$self->{hash}->{$index1}->{_name}] "; + $header = '[global] ' if $header eq '[.*] '; if ($strictnamespace) { - next unless $index1 eq '.*' or $index1 eq $primary; - $header = "" unless $header eq '[global channel] '; + next unless $index1 eq '.*' or $index1 eq $lc_primary_index; + $header = "" unless $header eq '[global] '; } - 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; + foreach my $index2 (sort keys %{ $self->{hash}->{$index1} }) { + my $distance_result = fastdistance($secondary_index, $index2); + my $length = (length $secondary_index > length $index2) ? length $secondary_index : length $index2; if ($distance_result / $length < $distance) { + my $name = $self->{hash}->{$index1}->{$index2}->{_name}; $header = "" if $last_header eq $header; $last_header = $header; $comma = '; ' if $comma ne '' and $header ne ''; - if ($index2 =~ / /) { - $result .= $comma . $header . "\"$index2\""; + if ($name =~ / /) { + $result .= $comma . $header . "\"$name\""; } else { - $result .= $comma . $header . $index2; + $result .= $comma . $header . $name; } $comma = ", "; } @@ -193,31 +195,34 @@ sub levenshtein_matches { } sub set { - my ($self, $primary_index_key, $secondary_index_key, $key, $value, $dont_save) = @_; + my ($self, $primary_index, $secondary_index, $key, $value, $dont_save) = @_; + my $lc_primary_index = lc $primary_index; + my $lc_secondary_index = lc $secondary_index; - my $primary = $self->find_index($primary_index_key); - - if (not $primary) { - my $result = "No such $self->{name} object [$primary_index_key]; similiar matches: "; - $result .= $self->levenshtein_matches($primary_index_key); + if (not exists $self->{hash}->{$lc_primary_index}) { + my $result = "$self->{name}: $primary_index not found; similiar matches: "; + $result .= $self->levenshtein_matches($primary_index); return $result; } - my $secondary = $self->find_index($primary, $secondary_index_key); - - if (not $secondary) { - my $secondary_text = $secondary_index_key =~ / / ? "\"$secondary_index_key\"" : $secondary_index_key; - my $result = "No such $self->{name} object [$primary_index_key] $secondary_text; similiar matches: "; - $result .= $self->levenshtein_matches($primary, $secondary_index_key); + if (not exists $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}) { + my $secondary_text = $secondary_index =~ / / ? "\"$secondary_index\"" : $secondary_index; + my $result = "$self->{name}: [$self->{hash}->{$lc_primary_index}->{_name}] $secondary_text not found; similiar matches: "; + $result .= $self->levenshtein_matches($primary_index, $secondary_index); return $result; } + my $name1 = $self->{hash}->{$lc_primary_index}->{_name}; + my $name2 = $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}->{_name}; + + $name1 = 'global' if $name1 eq '.*'; + $name2 = "\"$name2\"" if $name2 =~ / /; + if (not defined $key) { - my $secondary_text = $secondary =~ / / ? "\"$secondary\"" : $secondary; - my $result = "[" . ($primary eq '.*' ? 'global' : $primary) . "] $secondary_text keys:\n"; + my $result = "[$name1] $name2 keys:\n"; my $comma = ''; - foreach my $key (sort keys %{ $self->hash->{$primary}->{$secondary} }) { - $result .= $comma . "$key => " . $self->hash->{$primary}->{$secondary}->{$key}; + foreach my $key (sort keys %{ $self->{hash}->{$lc_primary_index}->{$lc_secondary_index} }) { + $result .= $comma . "$key => " . $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}->{$key}; $comma = ";\n"; } $result .= "none" if ($comma eq ''); @@ -225,104 +230,114 @@ sub set { } if (not defined $value) { - $value = $self->hash->{$primary}->{$secondary}->{$key}; + $value = $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}->{$key}; } else { - $self->hash->{$primary}->{$secondary}->{$key} = $value; + $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}->{$key} = $value; $self->save unless $dont_save; } - $primary = 'global' if $primary eq '.*'; - $secondary = "\"$secondary\"" if $secondary =~ / /; - return "[$primary] $secondary: '$key' " . (defined $value ? "set to '$value'" : "is not set."); + return "[$name1] $name2: '$key' " . (defined $value ? "set to '$value'" : "is not set."); } sub unset { - my ($self, $primary_index_key, $secondary_index_key, $key) = @_; + my ($self, $primary_index, $secondary_index, $key) = @_; + my $lc_primary_index = lc $primary_index; + my $lc_secondary_index = lc $secondary_index; - my $primary = $self->find_index($primary_index_key); - - if (not $primary) { - my $result = "No such $self->{name} object group '$primary_index_key'; similiar matches: "; - $result .= $self->levenshtein_matches($primary_index_key); + if (not exists $self->{hash}->{$lc_primary_index}) { + my $result = "$self->{name}: $primary_index not found; similiar matches: "; + $result .= $self->levenshtein_matches($primary_index); return $result; } - my $secondary = $self->find_index($primary, $secondary_index_key); + my $name1 = $self->{hash}->{$lc_primary_index}->{_name}; + $name1 = 'global' if $name1 eq '.*'; - if (not $secondary) { - my $result = "No such $self->{name} object '$secondary_index_key'; similiar matches: "; - $result .= $self->levenshtein_matches($primary, $secondary_index_key); + if (not exists $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}) { + my $result = "$self->{name}: [$name1] $secondary_index not found; similiar matches: "; + $result .= $self->levenshtein_matches($primary_index, $secondary_index); return $result; } - delete $self->hash->{$primary}->{$secondary}->{$key}; + delete $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}->{$key}; $self->save(); - $primary = 'global' if $primary eq '.*'; - $secondary = "\"$secondary\"" if $secondary =~ / /; - return "[$self->{name}] ($primary) $secondary: '$key' unset."; + my $name2 = $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}->{_name}; + $name2 = "\"$name2\"" if $name2 =~ / /; + + return "$self->{name}: [$name1] $name2: '$key' unset."; } sub add { - my ($self, $primary_index_key, $secondary_index_key, $hash) = @_; + my ($self, $primary_index, $secondary_index, $data, $dont_save) = @_; + my $lc_primary_index = lc $primary_index; + my $lc_secondary_index = lc $secondary_index; - if ($self->hash_add($primary_index_key, $secondary_index_key, $hash)) { - $self->save(); - } else { - return "Error occurred adding new $self->{name} object."; + if (exists $self->{hash}->{$lc_primary_index} and exists $self->{$lc_primary_index}->{$lc_secondary_index}) { + return "Error: entry already exists"; } - return "'$secondary_index_key' added to $primary_index_key [$self->{name}]."; + if (not exists $self->{hash}->{$lc_primary_index}) { + $self->{hash}->{$lc_primary_index}->{_name} = $primary_index; # preserve case + } + + $data->{_name} = $secondary_index; # preserve case + $self->{hash}->{$lc_primary_index}->{$lc_secondary_index} = {%$data}; + $self->save() unless $dont_save; + + my $name1 = $self->{hash}->{$lc_primary_index}->{_name}; + my $name2 = $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}->{_name}; + $name1 = 'global' if $name1 eq '.*'; + $name2 = "\"$name2\"" if $name2 =~ / /; + return "$self->{name}: [$name1]: $name2 added."; } sub remove { - my ($self, $primary_index_key, $secondary_index_key) = @_; + my ($self, $primary_index, $secondary_index) = @_; + my $lc_primary_index = lc $primary_index; + my $lc_secondary_index = lc $secondary_index; - my $primary = $self->find_index($primary_index_key); - - if (not $primary) { - my $result = "No such $self->{name} object group '$primary_index_key'; similiar matches: "; - $result .= $self->levenshtein_matches($primary_index_key); + if (not exists $self->{hash}->{$lc_primary_index}) { + my $result = "$self->{name}: $primary_index not found; similiar matches: "; + $result .= $self->levenshtein_matches($primary_index); return $result; } - if (not $secondary_index_key) { - delete $self->hash->{$primary}; + if (not $secondary_index) { + my $data = delete $self->{hash}->{$lc_primary_index}; + my $name = $data->{_name}; + $name = 'global' if $name eq '.*'; $self->save; - return "'$primary' group removed from $self->{name}."; + return "$self->{name}: $name removed."; } - my $secondary = $self->find_index($primary, $secondary_index_key); + my $name1 = $self->{hash}->{$lc_primary_index}->{_name}; + $name1 = 'global' if $name1 eq '.*'; - if (not $secondary) { - my $result = "No such $self->{name} object '$secondary_index_key'; similiar matches: "; - $result .= $self->levenshtein_matches($primary, $secondary_index_key); + if (not exists $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}) { + my $result = "$self->{name}: [$name1] $secondary_index not found; similiar matches: "; + $result .= $self->levenshtein_matches($primary_index, $secondary_index); return $result; } - delete $self->hash->{$primary}->{$secondary}; + my $data = delete $self->{hash}->{$lc_primary_index}->{$lc_secondary_index}; + my $name2 = $data->{_name}; + $name2 = "\"$name2\"" if $name2 =~ / /; # remove primary group if no more secondaries - if (scalar keys %{ $self->hash->{$primary} } == 0) { - delete $self->hash->{$primary}; + if (keys %{ $self->{hash}->{$lc_primary_index} } == 0) { + delete $self->{hash}->{$lc_primary_index}; } $self->save(); - return "'$secondary' removed from $primary group [$self->{name}]."; + return "$self->{name}: [$name1] $name2 removed."; } -# Getters and setters - -sub hash { - my $self = shift; - return $self->{hash}; -} - -sub filename { - my $self = shift; - - if (@_) { $self->{filename} = shift; } - return $self->{filename}; +sub exists { + my ($self, $primary_index, $secondary_index) = @_; + $primary_index = lc $primary_index; + $secondary_index = lc $secondary_index; + return (exists $self->{hash}->{$primary_index} and exists $self->{hash}->{$primary_index}->{$secondary_index}); } 1; diff --git a/PBot/FactoidCommands.pm b/PBot/FactoidCommands.pm index 1670684a..812c9b80 100644 --- a/PBot/FactoidCommands.pm +++ b/PBot/FactoidCommands.pm @@ -25,18 +25,6 @@ use JSON; use PBot::Utils::SafeFilename; -sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to FactoidCommands should be key/value pairs, not hash reference"); - } - - my ($class, %conf) = @_; - - my $self = bless {}, $class; - $self->initialize(%conf); - return $self; -} - our %factoid_metadata_levels = ( created_on => 90, enabled => 10, @@ -59,44 +47,47 @@ our %factoid_metadata_levels = ( # all others are allowed to be factset by anybody/default to level 0 ); +sub new { + Carp::croak("Options to FactoidCommands should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH'; + my ($class, %conf) = @_; + my $self = bless {}, $class; + $self->initialize(%conf); + return $self; +} + sub initialize { my ($self, %conf) = @_; - my $pbot = delete $conf{pbot}; - if (not defined $pbot) { - Carp::croak("Missing pbot reference to FactoidCommands"); - } + $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to FactoidCommands"); - $self->{pbot} = $pbot; + $self->{pbot}->{registry}->add_default('text', 'general', 'module_repo', $conf{module_repo} // 'https://github.com/pragma-/pbot/blob/master/modules/'); - $pbot->{registry}->add_default('text', 'general', 'module_repo', $conf{module_repo} // 'https://github.com/pragma-/pbot/blob/master/modules/'); - - $pbot->{commands}->register(sub { return $self->factadd(@_) }, "learn", 0); - $pbot->{commands}->register(sub { return $self->factadd(@_) }, "factadd", 0); - $pbot->{commands}->register(sub { return $self->factrem(@_) }, "forget", 0); - $pbot->{commands}->register(sub { return $self->factrem(@_) }, "factrem", 0); - $pbot->{commands}->register(sub { return $self->factshow(@_) }, "factshow", 0); - $pbot->{commands}->register(sub { return $self->factinfo(@_) }, "factinfo", 0); - $pbot->{commands}->register(sub { return $self->factlog(@_) }, "factlog", 0); - $pbot->{commands}->register(sub { return $self->factundo(@_) }, "factundo", 0); - $pbot->{commands}->register(sub { return $self->factredo(@_) }, "factredo", 0); - $pbot->{commands}->register(sub { return $self->factset(@_) }, "factset", 0); - $pbot->{commands}->register(sub { return $self->factunset(@_) }, "factunset", 0); - $pbot->{commands}->register(sub { return $self->factchange(@_) }, "factchange", 0); - $pbot->{commands}->register(sub { return $self->factalias(@_) }, "factalias", 0); - $pbot->{commands}->register(sub { return $self->factmove(@_) }, "factmove", 0); - $pbot->{commands}->register(sub { return $self->call_factoid(@_) }, "fact", 0); - $pbot->{commands}->register(sub { return $self->factfind(@_) }, "factfind", 0); - $pbot->{commands}->register(sub { return $self->list(@_) }, "list", 0); - $pbot->{commands}->register(sub { return $self->top20(@_) }, "top20", 0); - $pbot->{commands}->register(sub { return $self->load_module(@_) }, "load", 90); - $pbot->{commands}->register(sub { return $self->unload_module(@_) }, "unload", 90); - $pbot->{commands}->register(sub { return $self->histogram(@_) }, "histogram", 0); - $pbot->{commands}->register(sub { return $self->count(@_) }, "count", 0); + $self->{pbot}->{commands}->register(sub { return $self->factadd(@_) }, "learn", 0); + $self->{pbot}->{commands}->register(sub { return $self->factadd(@_) }, "factadd", 0); + $self->{pbot}->{commands}->register(sub { return $self->factrem(@_) }, "forget", 0); + $self->{pbot}->{commands}->register(sub { return $self->factrem(@_) }, "factrem", 0); + $self->{pbot}->{commands}->register(sub { return $self->factshow(@_) }, "factshow", 0); + $self->{pbot}->{commands}->register(sub { return $self->factinfo(@_) }, "factinfo", 0); + $self->{pbot}->{commands}->register(sub { return $self->factlog(@_) }, "factlog", 0); + $self->{pbot}->{commands}->register(sub { return $self->factundo(@_) }, "factundo", 0); + $self->{pbot}->{commands}->register(sub { return $self->factredo(@_) }, "factredo", 0); + $self->{pbot}->{commands}->register(sub { return $self->factset(@_) }, "factset", 0); + $self->{pbot}->{commands}->register(sub { return $self->factunset(@_) }, "factunset", 0); + $self->{pbot}->{commands}->register(sub { return $self->factchange(@_) }, "factchange", 0); + $self->{pbot}->{commands}->register(sub { return $self->factalias(@_) }, "factalias", 0); + $self->{pbot}->{commands}->register(sub { return $self->factmove(@_) }, "factmove", 0); + $self->{pbot}->{commands}->register(sub { return $self->call_factoid(@_) }, "fact", 0); + $self->{pbot}->{commands}->register(sub { return $self->factfind(@_) }, "factfind", 0); + $self->{pbot}->{commands}->register(sub { return $self->list(@_) }, "list", 0); + $self->{pbot}->{commands}->register(sub { return $self->top20(@_) }, "top20", 0); + $self->{pbot}->{commands}->register(sub { return $self->load_module(@_) }, "load", 90); + $self->{pbot}->{commands}->register(sub { return $self->unload_module(@_) }, "unload", 90); + $self->{pbot}->{commands}->register(sub { return $self->histogram(@_) }, "histogram", 0); + $self->{pbot}->{commands}->register(sub { return $self->count(@_) }, "count", 0); # the following commands have not yet been updated to use the new factoid structure # DO NOT USE!! Factoid corruption may occur. - $pbot->{commands}->register(sub { return $self->add_regex(@_) }, "regex", 999); + $self->{pbot}->{commands}->register(sub { return $self->add_regex(@_) }, "regex", 999); } sub call_factoid { @@ -127,6 +118,9 @@ sub log_factoid { my $self = shift; my ($channel, $trigger, $hostmask, $msg, $dont_save_undo) = @_; + $channel = lc $channel; + $trigger = lc $trigger; + my $channel_path = $channel; $channel_path = 'global' if $channel_path eq '.*'; @@ -166,7 +160,7 @@ sub log_factoid { splice @{$undos->{list}}, $undos->{idx} + 1; } - push @{$undos->{list}}, $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}; + push @{$undos->{list}}, $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}; $undos->{idx}++; eval { store $undos, "$path/$trigger_safe.$channel_path_safe.undo"; }; @@ -264,7 +258,11 @@ sub find_factoid_with_optional_channel { $from_chan = '.*' if $channel eq 'global'; if ($opts{explicit} and $channel =~ /^#/ and $from_chan =~ /^#/ and $channel ne $from_chan) { - return "/say $trigger belongs to $channel, not $from_chan. Please switch to or explicitly specify $channel."; + my $channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $channel_name = 'global' if $channel_name eq '.*'; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + return "/say $trigger_name belongs to $channel_name, not $from_chan. Please switch to or explicitly specify $channel_name."; } return ($channel, $trigger, $remaining_args); @@ -395,8 +393,13 @@ sub factundo { my $path = $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/factlog'; my $undos = eval { retrieve("$path/$trigger_safe.$channel_path_safe.undo"); }; + my $channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $channel_name = 'global' if $channel_name eq '.*'; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + if (not $undos) { - return "There are no undos available for [$channel] $trigger."; + return "There are no undos available for [$channel_name] $trigger_name."; } if (defined $list_undos) { @@ -404,14 +407,14 @@ sub factundo { return $self->list_undo_history($undos, $list_undos); } - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my $admininfo = $self->{pbot}->{admins}->loggedin($channel, "$nick!$user\@$host"); if ($factoids->{$channel}->{$trigger}->{'locked'}) { - return "/say $trigger is locked and cannot be reverted." if not defined $admininfo; + return "/say $trigger_name is locked and cannot be reverted." if not defined $admininfo; if (exists $factoids->{$channel}->{$trigger}->{'effective-level'} and $admininfo->{level} < $factoids->{$channel}->{$trigger}->{'effective-level'}) { - return "/say $trigger is locked with an effective-level higher than your level and cannot be reverted."; + return "/say $trigger_name is locked with an effective-level higher than your level and cannot be reverted."; } } @@ -419,14 +422,14 @@ sub factundo { return "Don't be absurd." if $goto_revision < 1; if ($goto_revision > @{$undos->{list}}) { if (@{$undos->{list}} == 1) { - return "There is only one revision available for [$channel] $trigger."; + return "There is only one revision available for [$channel_name] $trigger_name."; } else { - return "There are " . @{$undos->{list}} . " revisions available for [$channel] $trigger."; + return "There are " . @{$undos->{list}} . " revisions available for [$channel_name] $trigger_name."; } } if ($goto_revision == $undos->{idx} + 1) { - return "[$channel] $trigger is already at revision $goto_revision."; + return "[$channel_name] $trigger_name is already at revision $goto_revision."; } $undos->{idx} = $goto_revision - 1; @@ -434,18 +437,18 @@ sub factundo { $self->{pbot}->{logger}->log("Error storing undo: $@\n") if $@; } else { unless ($deleted) { - return "There are no more undos remaining for [$channel] $trigger." if not $undos->{idx}; + return "There are no more undos remaining for [$channel_name] $trigger_name." if not $undos->{idx}; $undos->{idx}--; eval { store $undos, "$path/$trigger_safe.$channel_path_safe.undo"; }; $self->{pbot}->{logger}->log("Error storing undo: $@\n") if $@; } } - $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger} = $undos->{list}->[$undos->{idx}]; + $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger} = $undos->{list}->[$undos->{idx}]; my $changes = $self->hash_differences_as_string($undos->{list}->[$undos->{idx} + 1], $undos->{list}->[$undos->{idx}]); $self->log_factoid($channel, $trigger, "$nick!$user\@$host", "reverted (undo): $changes", 1); - return "[$channel] $trigger reverted (revision " . ($undos->{idx} + 1) . "): $changes\n"; + return "[$channel_name] $trigger_name reverted (revision " . ($undos->{idx} + 1) . "): $changes\n"; } sub factredo { @@ -480,11 +483,16 @@ sub factredo { my $channel_path_safe = safe_filename $channel_path; my $trigger_safe = safe_filename $trigger; + my $channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $channel_name = 'global' if $channel_name eq '.*'; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + my $path = $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/factlog'; my $undos = eval { retrieve("$path/$trigger_safe.$channel_path_safe.undo"); }; if (not $undos) { - return "There are no redos available for [$channel] $trigger."; + return "There are no redos available for [$channel_name] $trigger_name."; } if (defined $list_undos) { @@ -492,33 +500,33 @@ sub factredo { return $self->list_undo_history($undos, $list_undos); } - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my $admininfo = $self->{pbot}->{admins}->loggedin($channel, "$nick!$user\@$host"); if ($factoids->{$channel}->{$trigger}->{'locked'}) { - return "/say $trigger is locked and cannot be reverted." if not defined $admininfo; + return "/say $trigger_name is locked and cannot be reverted." if not defined $admininfo; if (exists $factoids->{$channel}->{$trigger}->{'effective-level'} and $admininfo->{level} < $factoids->{$channel}->{$trigger}->{'effective-level'}) { - return "/say $trigger is locked with an effective-level higher than your level and cannot be reverted."; + return "/say $trigger_name is locked with an effective-level higher than your level and cannot be reverted."; } } if (not defined $goto_revision and $undos->{idx} + 1 == @{$undos->{list}}) { - return "There are no more redos remaining for [$channel] $trigger."; + return "There are no more redos remaining for [$channel_name] $trigger_name."; } if (defined $goto_revision) { return "Don't be absurd." if $goto_revision < 1; if ($goto_revision > @{$undos->{list}}) { if (@{$undos->{list}} == 1) { - return "There is only one revision available for [$channel] $trigger."; + return "There is only one revision available for [$channel_name] $trigger_name."; } else { - return "There are " . @{$undos->{list}} . " revisions available for [$channel] $trigger."; + return "There are " . @{$undos->{list}} . " revisions available for [$channel_name] $trigger_name."; } } if ($goto_revision == $undos->{idx} + 1) { - return "[$channel] $trigger is already at revision $goto_revision."; + return "[$channel_name] $trigger_name is already at revision $goto_revision."; } $undos->{idx} = $goto_revision - 1; @@ -530,11 +538,11 @@ sub factredo { $self->{pbot}->{logger}->log("Error storing undo: $@\n") if $@; } - $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger} = $undos->{list}->[$undos->{idx}]; + $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger} = $undos->{list}->[$undos->{idx}]; my $changes = $self->hash_differences_as_string($undos->{list}->[$undos->{idx} - 1], $undos->{list}->[$undos->{idx}]); $self->log_factoid($channel, $trigger, "$nick!$user\@$host", "reverted (redo): $changes", 1); - return "[$channel] $trigger restored (revision " . ($undos->{idx} + 1) . "): $changes\n"; + return "[$channel_name] $trigger_name restored (revision " . ($undos->{idx} + 1) . "): $changes\n"; } sub factset { @@ -544,6 +552,9 @@ sub factset { my ($channel, $trigger, $arguments) = $self->find_factoid_with_optional_channel($from, $args, 'factset', usage => 'Usage: factset [channel] [key [value]]', explicit => 1); return $channel if not defined $trigger; # if $trigger is not defined, $channel is an error message + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + my $arglist = $self->{pbot}->{interpreter}->make_args($arguments); my ($key, $value) = $self->{pbot}->{interpreter}->split_args($arglist, 2); @@ -577,8 +588,8 @@ sub factset { } } - if (defined $value and !$level and $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{'locked'}) { - return "/say $trigger is locked; unlock before setting."; + if (defined $value and !$level and $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'locked'}) { + return "/say $trigger_name is locked; unlock before setting."; } if (lc $key eq 'effective-level' and defined $value and $level > 0) { @@ -591,15 +602,15 @@ sub factset { $self->{pbot}->{factoids}->{factoids}->set($channel, $trigger, 'locked', '1'); } - if (lc $key eq 'locked' and exists $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{'effective-level'}) { - if ($level < $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{'effective-level'}) { + if (lc $key eq 'locked' and exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) { + if ($level < $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) { return "You cannot unlock this factoid because its effective-level is greater than your level."; } } } if (defined $owner_channel) { - my $factoid = $self->{pbot}->{factoids}->{factoids}->hash->{$owner_channel}->{$owner_trigger}; + my $factoid = $self->{pbot}->{factoids}->{factoids}->{hash}->{$owner_channel}->{$owner_trigger}; my $owner; my $mask; @@ -614,7 +625,7 @@ sub factset { } if ((defined $value and $key ne 'action') and lc $mask ne lc $owner and $level == 0) { - return "You are not the owner of $trigger."; + return "You are not the owner of $trigger_name."; } } @@ -669,15 +680,15 @@ sub factunset { } } - if (exists $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{'effective-level'}) { + if (exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) { if (lc $key eq 'locked') { - if ($level >= $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{'effective-level'}) { + if ($level >= $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) { $self->{pbot}->{factoids}->{factoids}->unset($channel, $trigger, 'effective-level'); } else { return "You cannot unlock this factoid because its effective-level is higher than your level."; } } elsif (lc $key eq 'effective-level') { - if ($level < $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{'effective-level'}) { + if ($level < $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) { return "You cannot unset the effective-level because it is higher than your level."; } } @@ -685,18 +696,23 @@ sub factunset { my $oldvalue; + my $channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $channel_name = 'global' if $channel_name eq '.*'; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + if (defined $owner_channel) { - my $factoid = $self->{pbot}->{factoids}->{factoids}->hash->{$owner_channel}->{$owner_trigger}; + my $factoid = $self->{pbot}->{factoids}->{factoids}->{hash}->{$owner_channel}->{$owner_trigger}; my ($owner) = $factoid->{'owner'} =~ m/([^!]+)/; if (lc $nick ne lc $owner and $level == 0) { - return "You are not the owner of $trigger."; + return "You are not the owner of $trigger_name."; } - $oldvalue = $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{$key}; + $oldvalue = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{$key}; } - return "[$channel] $trigger: key '$key' does not exist." if not defined $oldvalue; + return "[$channel_name] $trigger_name: key '$key' does not exist." if not defined $oldvalue; my $result = $self->{pbot}->{factoids}->{factoids}->unset($channel, $trigger, $key); @@ -720,10 +736,11 @@ sub list { if ($arguments =~ /^modules$/i) { $text = "Loaded modules: "; - foreach my $channel (sort keys %{ $self->{pbot}->{factoids}->{factoids}->hash }) { - foreach my $command (sort keys %{ $self->{pbot}->{factoids}->{factoids}->hash->{$channel} }) { - if ($self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$command}->{type} eq 'module') { - $text .= "$command "; + foreach my $channel (sort keys %{ $self->{pbot}->{factoids}->{factoids}->{hash} }) { + foreach my $command (sort keys %{ $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel} }) { + next if $command eq '_name'; + if ($self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$command}->{type} eq 'module') { + $text .= "$self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$command}->{_name} "; } } } @@ -743,17 +760,18 @@ sub list { $text = "Admins: "; my $last_channel = ""; my $sep = ""; - foreach my $channel (sort keys %{ $self->{pbot}->{admins}->{admins}->hash }) { + foreach my $channel (sort keys %{ $self->{pbot}->{admins}->{admins}->{hash} }) { next if $from =~ m/^#/ and $channel ne $from and $channel ne '.*'; if ($last_channel ne $channel) { $text .= $sep . ($channel eq ".*" ? "global" : $channel) . ": "; $last_channel = $channel; $sep = ""; } - foreach my $hostmask (sort { $self->{pbot}->{admins}->{admins}->hash->{$channel}->{$a}->{name} cmp $self->{pbot}->{admins}->{admins}->hash->{$channel}->{$b}->{name} } keys %{ $self->{pbot}->{admins}->{admins}->hash->{$channel} }) { + foreach my $hostmask (sort { return 0 if $a eq '_name' or $b eq '_name'; $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$a}->{name} cmp $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$b}->{name} } keys %{ $self->{pbot}->{admins}->{admins}->{hash}->{$channel} }) { + next if $hostmask eq '_name'; $text .= $sep; - $text .= "*" if $self->{pbot}->{admins}->{admins}->hash->{$channel}->{$hostmask}->{loggedin}; - $text .= $self->{pbot}->{admins}->{admins}->hash->{$channel}->{$hostmask}->{name} . " (" . $self->{pbot}->{admins}->{admins}->hash->{$channel}->{$hostmask}->{level} . ")"; + $text .= "*" if $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$hostmask}->{loggedin}; + $text .= $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$hostmask}->{name} . " (" . $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$hostmask}->{level} . ")"; $sep = "; "; } } @@ -800,55 +818,67 @@ sub factmove { return "Source factoid $source not found in channel $src_channel"; } - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $source_channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$found_src_channel}->{_name}; + my $source_trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$found_src_channel}->{$found_source}->{_name}; + $source_channel_name = 'global' if $source_channel_name eq '.*'; + $source_trigger_name = "\"$source_trigger_name\"" if $source_trigger_name =~ / /; + + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my ($owner) = $factoids->{$found_src_channel}->{$found_source}->{'owner'} =~ m/([^!]+)/; if ((lc $nick ne lc $owner) and (not $self->{pbot}->{admins}->loggedin($found_src_channel, "$nick!$user\@$host"))) { $self->{pbot}->{logger}->log("$nick!$user\@$host attempted to move [$found_src_channel] $found_source (not owner)\n"); my $chan = ($found_src_channel eq '.*' ? 'the global channel' : $found_src_channel); - return "You are not the owner of $found_source for $chan"; + return "You are not the owner of $source_trigger_name for $source_channel_name."; } if ($factoids->{$found_src_channel}->{$found_source}->{'locked'}) { - return "/say $found_source is locked; unlock before moving."; + return "/say $source_trigger_name is locked; unlock before moving."; } my ($found_target_channel, $found_target) = $self->{pbot}->{factoids}->find_factoid($target_channel, $target, exact_channel => 1, exact_trigger => 1); if (defined $found_target_channel) { - return "Target factoid $target already exists in channel $target_channel"; + my $target_channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$found_target_channel}->{_name}; + my $target_trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$found_target_channel}->{$found_target}->{_name}; + $target_channel_name = 'global' if $target_channel_name eq '.*'; + $target_trigger_name = "\"$target_trigger_name\"" if $target_trigger_name =~ / /; + return "Target factoid $target_trigger_name already exists in channel $target_channel_name."; } my ($overchannel, $overtrigger) = $self->{pbot}->{factoids}->find_factoid('.*', $target, exact_channel => 1, exact_trigger => 1); - if (defined $overtrigger and $self->{pbot}->{factoids}->{factoids}->hash->{'.*'}->{$overtrigger}->{'nooverride'}) { + if (defined $overtrigger and $self->{pbot}->{factoids}->{factoids}->{hash}->{'.*'}->{$overtrigger}->{'nooverride'}) { + my $override_channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$overchannel}->{_name}; + my $override_trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$overchannel}->{$overtrigger}->{_name}; + $override_channel_name = 'global' if $override_channel_name eq '.*'; + $override_trigger_name = "\"$override_trigger_name\"" if $override_trigger_name =~ / /; $self->{pbot}->{logger}->log("$nick!$user\@$host attempt to override $target\n"); - return "/say $target already exists for the global channel and cannot be overridden for " . ($target_channel eq '.*' ? 'the global channel' : $target_channel) . "."; + return "/say $override_trigger_name already exists for the global channel and cannot be overridden for " . ($target_channel eq '.*' ? 'the global channel' : $target_channel) . "."; } if ($self->{pbot}->{commands}->exists($target)) { return "/say $target already exists as a built-in command."; } - $target_channel = lc $target_channel; $target_channel = '.*' if $target_channel !~ /^#/; - $factoids->{$target_channel}->{$target} = $factoids->{$found_src_channel}->{$found_source}; - delete $factoids->{$found_src_channel}->{$found_source}; - + $factoids->{lc $target_channel}->{lc $target} = delete $factoids->{$found_src_channel}->{$found_source}; + $factoids->{lc $target_channel}->{lc $target}->{_name} = $target; + $factoids->{lc $target_channel}->{_name} = $target_channel if not exists $factoids->{lc $target_channel}->{_name}; $self->{pbot}->{factoids}->save_factoids; $found_src_channel = 'global' if $found_src_channel eq '.*'; $target_channel = 'global' if $target_channel eq '.*'; - if ($src_channel eq $target_channel) { - $self->log_factoid($found_src_channel, $found_source, "$nick!$user\@$host", "renamed from $found_source to $target"); - $self->log_factoid($target_channel, $target, "$nick!$user\@$host", "renamed from $found_source to $target"); - return "[$found_src_channel] $found_source renamed to $target"; + if ($src_channel eq lc $target_channel) { + $self->log_factoid($found_src_channel, $found_source, "$nick!$user\@$host", "renamed from $source_trigger_name to $target"); + $self->log_factoid($target_channel, $target, "$nick!$user\@$host", "renamed from $source_trigger_name to $target"); + return "[$source_channel_name] $source_trigger_name renamed to $target"; } else { - $self->log_factoid($found_src_channel, $found_source, "$nick!$user\@$host", "moved from $found_src_channel/$found_source to $target_channel/$target"); - $self->log_factoid($target_channel, $target, "$nick!$user\@$host", "moved from $found_src_channel/$found_source to $target_channel/$target"); - return "[$found_src_channel] $found_source moved to [$target_channel] $target"; + $self->log_factoid($found_src_channel, $found_source, "$nick!$user\@$host", "moved from $source_channel_name/$source_trigger_name to $target_channel/$target"); + $self->log_factoid($target_channel, $target, "$nick!$user\@$host", "moved from $source_channel_name/$source_trigger_name to $target_channel/$target"); + return "[$source_channel_name] $source_trigger_name moved to [$target_channel] $target"; } } @@ -859,7 +889,7 @@ sub factalias { my ($chan, $alias, $command) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 3); if (defined $chan and not ($chan eq '.*' or $chan =~ m/^#/)) { - # $chan doesn't look like a channel, so shift everything right + # $chan doesn't look like a channel, so shift everything to the right # and replace $chan with $from if (defined $command and length $command) { $command = "$alias $command"; @@ -887,14 +917,18 @@ sub factalias { my ($channel, $alias_trigger) = $self->{pbot}->{factoids}->find_factoid($chan, $alias, exact_channel => 1, exact_trigger => 1); if (defined $alias_trigger) { - $self->{pbot}->{logger}->log("attempt to overwrite existing command\n"); - return "'$alias_trigger' already exists for channel $channel"; + my $alias_channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $alias_trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$alias_trigger}->{_name}; + $alias_channel_name = 'global' if $alias_channel_name eq '.*'; + $alias_trigger_name = "\"$alias_trigger_name\"" if $alias_trigger_name =~ / /; + return "$alias_trigger_name already exists for $alias_channel_name."; } my ($overchannel, $overtrigger) = $self->{pbot}->{factoids}->find_factoid('.*', $alias, exact_channel => 1, exact_trigger => 1); - if (defined $overtrigger and $self->{pbot}->{factoids}->{factoids}->hash->{'.*'}->{$overtrigger}->{'nooverride'}) { - $self->{pbot}->{logger}->log("$nick!$user\@$host attempt to override $alias\n"); - return "/say $alias already exists for the global channel and cannot be overridden for " . ($chan eq '.*' ? 'the global channel' : $chan) . "."; + if (defined $overtrigger and $self->{pbot}->{factoids}->{factoids}->{hash}->{'.*'}->{$overtrigger}->{'nooverride'}) { + my $override_trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$overchannel}->{$overtrigger}->{_name}; + $override_trigger_name = "\"$override_trigger_name\"" if $override_trigger_name =~ / /; + return "/say $override_trigger_name already exists for the global channel and cannot be overridden for " . ($chan eq '.*' ? 'the global channel' : $chan) . "."; } if ($self->{pbot}->{commands}->exists($alias)) { @@ -905,20 +939,21 @@ sub factalias { $self->{pbot}->{logger}->log("$nick!$user\@$host [$chan] aliased $alias => $command\n"); $self->{pbot}->{factoids}->save_factoids(); - return "'$alias' aliases '$command' for " . ($chan eq '.*' ? 'the global channel' : $chan); + return "$alias aliases `$command` for " . ($chan eq '.*' ? 'the global channel' : $chan); } sub add_regex { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my ($keyword, $text) = $arguments =~ /^(.*?)\s+(.*)$/ if defined $arguments; $from = '.*' if not defined $from or $from !~ /^#/; if (not defined $keyword) { $text = ""; - foreach my $trigger (sort keys %{ $factoids->{$from} }) { + foreach my $trigger (sort keys %{ $factoids->{lc $from} }) { + next if $trigger eq '_name'; if ($factoids->{$from}->{$trigger}->{type} eq 'regex') { $text .= $trigger . " "; } @@ -980,6 +1015,7 @@ sub factadd { # the URL is the remaining arguments my ($url) = $self->{pbot}->{interpreter}->split_args(\@arglist, 1); + # FIXME: move this to registry if ($url !~ m/^https?:\/\/(?:sprunge.us|ix.io)\/\w+$/) { return "Invalid URL: acceptable URLs are: http://sprunge.us, http://ix.io"; } @@ -1028,30 +1064,33 @@ sub factadd { my ($channel, $trigger) = $self->{pbot}->{factoids}->find_factoid($from_chan, $keyword, exact_channel => 1, exact_trigger => 1); if (defined $trigger) { + my $channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $channel_name = 'global' if $channel_name eq '.*'; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + if (not $force) { - $self->{pbot}->{logger}->log("$nick!$user\@$host attempt to overwrite $keyword\n"); - return "/say $keyword_text already exists for " . ($from_chan eq '.*' ? 'the global channel' : $from_chan) . "."; + return "/say $trigger_name already exists for $channel_name."; } else { - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; if ($factoids->{$channel}->{$trigger}->{'locked'}) { - return "/say $keyword_text is locked; unlock before overwriting."; + return "/say $trigger_name is locked; unlock before overwriting."; } my ($owner) = $factoids->{$channel}->{$trigger}->{'owner'} =~ m/([^!]+)/; if ((lc $nick ne lc $owner) and (not $self->{pbot}->{admins}->loggedin($channel, "$nick!$user\@$host"))) { - $self->{pbot}->{logger}->log("$nick!$user\@$host attempted to overwrite $trigger [not owner]\n"); - my $chan = ($channel eq '.*' ? 'the global channel' : $channel); - return "You are not the owner of $keyword_text for $chan; cannot overwrite"; + return "You are not the owner of $trigger_name for $channel_name; cannot force overwrite."; } } } ($channel, $trigger) = $self->{pbot}->{factoids}->find_factoid('.*', $keyword, exact_channel => 1, exact_trigger => 1); - if (defined $trigger and $self->{pbot}->{factoids}->{factoids}->hash->{'.*'}->{$trigger}->{'nooverride'}) { - $self->{pbot}->{logger}->log("$nick!$user\@$host attempt to override $keyword_text\n"); - return "/say $keyword_text already exists for the global channel and cannot be overridden for " . ($from_chan eq '.*' ? 'the global channel' : $from_chan) . "."; + if (defined $trigger and $self->{pbot}->{factoids}->{factoids}->{hash}->{'.*'}->{$trigger}->{'nooverride'}) { + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + return "/say $trigger_name already exists for the global channel and cannot be overridden for " . ($from_chan eq '.*' ? 'the global channel' : $from_chan) . "."; } if ($self->{pbot}->{commands}->exists($keyword)) { @@ -1067,7 +1106,7 @@ sub factadd { sub factrem { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my ($from_chan, $from_trig) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 2); @@ -1082,44 +1121,45 @@ sub factrem { $channel = '.*' if $channel eq 'global'; $from_chan = '.*' if $channel eq 'global'; - my $trigger_text = $trigger =~ / / ? "\"$trigger\"" : $trigger; + my $channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $channel_name = 'global' if $channel_name eq '.*'; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; if ($factoids->{$channel}->{$trigger}->{type} eq 'module') { - $self->{pbot}->{logger}->log("$nick!$user\@$host attempted to remove $trigger_text [not factoid]\n"); - return "/say $trigger_text is not a factoid."; + return "/say $trigger_name is not a factoid."; } if ($channel =~ /^#/ and $from_chan =~ /^#/ and $channel ne $from_chan) { - return "/say $trigger_text belongs to $channel, but this is $from_chan. Please switch to $channel or /msg to remove this factoid."; + return "/say $trigger_name belongs to $channel_name, but this is $from_chan. Please switch to $channel_name or use /msg to remove this factoid."; } my ($owner) = $factoids->{$channel}->{$trigger}->{'owner'} =~ m/([^!]+)/; if ((lc $nick ne lc $owner) and (not $self->{pbot}->{admins}->loggedin($channel, "$nick!$user\@$host"))) { - $self->{pbot}->{logger}->log("$nick!$user\@$host attempted to remove $trigger_text [not owner]\n"); - my $chan = ($channel eq '.*' ? 'the global channel' : $channel); - return "You are not the owner of $trigger_text for $chan"; + return "You are not the owner of $trigger_name for $channel_name."; } if ($factoids->{$channel}->{$trigger}->{'locked'}) { - return "/say $trigger_text is locked; unlock before deleting."; + return "/say $trigger_name is locked; unlock before deleting."; } $self->{pbot}->{logger}->log("$nick!$user\@$host removed [$channel][$trigger][" . $factoids->{$channel}->{$trigger}->{action} . "]\n"); $self->{pbot}->{factoids}->remove_factoid($channel, $trigger); $self->log_factoid($channel, $trigger, "$nick!$user\@$host", "deleted", 1); - return "/say $trigger_text removed from " . ($channel eq '.*' ? 'the global channel' : $channel) . "."; + return "/say $trigger_name removed from $channel_name."; } sub histogram { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my %hash; my $factoid_count = 0; foreach my $channel (keys %$factoids) { foreach my $command (keys %{ $factoids->{$channel} }) { + next if $command eq '_name'; if ($factoids->{$channel}->{$command}->{type} eq 'text') { $hash{$factoids->{$channel}->{$command}->{owner}}++; $factoid_count++; @@ -1142,7 +1182,7 @@ sub histogram { sub factshow { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; $stuff->{preserve_whitespace} = 1; @@ -1174,15 +1214,16 @@ sub factshow { my ($channel, $trigger) = $self->find_factoid_with_optional_channel($from, $args, 'factshow', usage => $usage); return $channel if not defined $trigger; # if $trigger is not defined, $channel is an error message - my $trigger_text = $trigger =~ / / ? "\"$trigger\"" : $trigger; + my $channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $channel_name = 'global' if $channel_name eq '.*'; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; - my $result = "$trigger_text: "; + my $result = "$trigger_name: "; if ($paste) { $result .= $self->{pbot}->{webpaste}->paste($factoids->{$channel}->{$trigger}->{action}, no_split => 1); - $channel = 'global' if $channel eq '.*'; - $chan = 'global' if $chan eq '.*'; - $result = "[$channel] $result" if $channel ne $chan; + $result = "[$channel_name] $result" if $channel ne lc $chan; return $result; } @@ -1192,9 +1233,7 @@ sub factshow { $result .= ' [module]'; } - $channel = 'global' if $channel eq '.*'; - $chan = 'global' if $chan eq '.*'; - $result = "[$channel] $result" if $channel ne $chan; + $result = "[$channel_name] $result" if $channel ne lc $chan; return $result; } @@ -1283,7 +1322,7 @@ sub factlog { sub factinfo { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my ($chan, $trig) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 2); @@ -1295,35 +1334,38 @@ sub factinfo { my ($channel, $trigger) = $self->find_factoid_with_optional_channel($from, $arguments, 'factinfo'); return $channel if not defined $trigger; # if $trigger is not defined, $channel is an error message + my $channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $channel_name = 'global' if $channel_name eq '.*'; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + my $created_ago = ago(gettimeofday - $factoids->{$channel}->{$trigger}->{created_on}); my $ref_ago = ago(gettimeofday - $factoids->{$channel}->{$trigger}->{last_referenced_on}) if defined $factoids->{$channel}->{$trigger}->{last_referenced_on}; - $chan = ($channel eq '.*' ? 'global channel' : $channel); - # factoid if ($factoids->{$channel}->{$trigger}->{type} eq 'text') { - return "/say $trigger: Factoid submitted by " . $factoids->{$channel}->{$trigger}->{owner} . " for $chan on " . localtime($factoids->{$channel}->{$trigger}->{created_on}) . " [$created_ago], " . (defined $factoids->{$channel}->{$trigger}->{edited_by} ? "last edited by $factoids->{$channel}->{$trigger}->{edited_by} on " . localtime($factoids->{$channel}->{$trigger}->{edited_on}) . " [" . ago(gettimeofday - $factoids->{$channel}->{$trigger}->{edited_on}) . "], " : "") . "referenced " . $factoids->{$channel}->{$trigger}->{ref_count} . " times (last by " . $factoids->{$channel}->{$trigger}->{ref_user} . (exists $factoids->{$channel}->{$trigger}->{last_referenced_on} ? " on " . localtime($factoids->{$channel}->{$trigger}->{last_referenced_on}) . " [$ref_ago]" : "") . ")"; + return "/say $trigger_name: Factoid submitted by " . $factoids->{$channel}->{$trigger}->{owner} . " for $channel_name on " . localtime($factoids->{$channel}->{$trigger}->{created_on}) . " [$created_ago], " . (defined $factoids->{$channel}->{$trigger}->{edited_by} ? "last edited by $factoids->{$channel}->{$trigger}->{edited_by} on " . localtime($factoids->{$channel}->{$trigger}->{edited_on}) . " [" . ago(gettimeofday - $factoids->{$channel}->{$trigger}->{edited_on}) . "], " : "") . "referenced " . $factoids->{$channel}->{$trigger}->{ref_count} . " times (last by " . $factoids->{$channel}->{$trigger}->{ref_user} . (exists $factoids->{$channel}->{$trigger}->{last_referenced_on} ? " on " . localtime($factoids->{$channel}->{$trigger}->{last_referenced_on}) . " [$ref_ago]" : "") . ")"; } # module if ($factoids->{$channel}->{$trigger}->{type} eq 'module') { my $module_repo = $self->{pbot}->{registry}->get_value('general', 'module_repo'); $module_repo .= "$factoids->{$channel}->{$trigger}->{workdir}/" if exists $factoids->{$channel}->{$trigger}->{workdir}; - return "/say $trigger: Module loaded by " . $factoids->{$channel}->{$trigger}->{owner} . " for $chan on " . localtime($factoids->{$channel}->{$trigger}->{created_on}) . " [$created_ago] -> $module_repo" . $factoids->{$channel}->{$trigger}->{action} . ", used " . $factoids->{$channel}->{$trigger}->{ref_count} . " times (last by " . $factoids->{$channel}->{$trigger}->{ref_user} . (exists $factoids->{$channel}->{$trigger}->{last_referenced_on} ? " on " . localtime($factoids->{$channel}->{$trigger}->{last_referenced_on}) . " [$ref_ago]" : "") . ")"; + return "/say $trigger_name: Module loaded by " . $factoids->{$channel}->{$trigger}->{owner} . " for $channel_name on " . localtime($factoids->{$channel}->{$trigger}->{created_on}) . " [$created_ago] -> $module_repo" . $factoids->{$channel}->{$trigger}->{action} . ", used " . $factoids->{$channel}->{$trigger}->{ref_count} . " times (last by " . $factoids->{$channel}->{$trigger}->{ref_user} . (exists $factoids->{$channel}->{$trigger}->{last_referenced_on} ? " on " . localtime($factoids->{$channel}->{$trigger}->{last_referenced_on}) . " [$ref_ago]" : "") . ")"; } # regex if ($factoids->{$channel}->{$trigger}->{type} eq 'regex') { - return "/say $trigger: Regex created by " . $factoids->{$channel}->{$trigger}->{owner} . " for $chan on " . localtime($factoids->{$channel}->{$trigger}->{created_on}) . " [$created_ago], " . (defined $factoids->{$channel}->{$trigger}->{edited_by} ? "last edited by $factoids->{$channel}->{$trigger}->{edited_by} on " . localtime($factoids->{$channel}->{$trigger}->{edited_on}) . " [" . ago(gettimeofday - $factoids->{$channel}->{$trigger}->{edited_on}) . "], " : "") . " used " . $factoids->{$channel}->{$trigger}->{ref_count} . " times (last by " . $factoids->{$channel}->{$trigger}->{ref_user} . (exists $factoids->{$channel}->{$trigger}->{last_referenced_on} ? " on " . localtime($factoids->{$channel}->{$trigger}->{last_referenced_on}) . " [$ref_ago]" : "") . ")"; + return "/say $trigger_name: Regex created by " . $factoids->{$channel}->{$trigger}->{owner} . " for $channel_name on " . localtime($factoids->{$channel}->{$trigger}->{created_on}) . " [$created_ago], " . (defined $factoids->{$channel}->{$trigger}->{edited_by} ? "last edited by $factoids->{$channel}->{$trigger}->{edited_by} on " . localtime($factoids->{$channel}->{$trigger}->{edited_on}) . " [" . ago(gettimeofday - $factoids->{$channel}->{$trigger}->{edited_on}) . "], " : "") . " used " . $factoids->{$channel}->{$trigger}->{ref_count} . " times (last by " . $factoids->{$channel}->{$trigger}->{ref_user} . (exists $factoids->{$channel}->{$trigger}->{last_referenced_on} ? " on " . localtime($factoids->{$channel}->{$trigger}->{last_referenced_on}) . " [$ref_ago]" : "") . ")"; } - return "/say $arguments is not a factoid or a module"; + return "/say $arguments is not a factoid or a module."; } sub top20 { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my %hash = (); my $text = ""; my $i = 0; @@ -1337,9 +1379,10 @@ sub top20 { if (not defined $args) { foreach my $chan (sort keys %{ $factoids }) { next if lc $chan ne lc $channel; - foreach my $command (sort {$factoids->{$chan}->{$b}{ref_count} <=> $factoids->{$chan}->{$a}{ref_count}} keys %{ $factoids->{$chan} }) { - if ($factoids->{$chan}->{$command}{ref_count} > 0 and $factoids->{$chan}->{$command}{type} eq 'text') { - $text .= "$command ($factoids->{$chan}->{$command}{ref_count}) "; + foreach my $command (sort {$factoids->{$chan}->{$b}->{ref_count} <=> $factoids->{$chan}->{$a}->{ref_count}} keys %{ $factoids->{$chan} }) { + next if $command eq '_name'; + if ($factoids->{$chan}->{$command}->{ref_count} > 0 and $factoids->{$chan}->{$command}->{type} eq 'text') { + $text .= "$factoids->{$chan}->{$command}->{_name} ($factoids->{$chan}->{$command}->{ref_count}) "; $i++; last if $i >= 20; } @@ -1353,10 +1396,11 @@ sub top20 { foreach my $chan (sort keys %{ $factoids }) { next if lc $chan ne lc $channel; foreach my $command (sort { $factoids->{$chan}->{$b}{created_on} <=> $factoids->{$chan}->{$a}{created_on} } keys %{ $factoids->{$chan} }) { + next if $command eq '_name'; my $ago = concise ago gettimeofday - $factoids->{$chan}->{$command}->{created_on}; my $owner = $factoids->{$chan}->{$command}->{owner}; $owner =~ s/!.*$//; - $text .= " $command [$ago by $owner]\n"; + $text .= " $factoids->{$chan}->{$command}->{_name} [$ago by $owner]\n"; $i++; last if $i >= 50; } @@ -1369,13 +1413,14 @@ sub top20 { my $user = lc $args; foreach my $chan (sort keys %{ $factoids }) { next if lc $chan ne lc $channel; - foreach my $command (sort { ($factoids->{$chan}->{$b}{last_referenced_on} || 0) <=> ($factoids->{$chan}->{$a}{last_referenced_on} || 0) } keys %{ $factoids->{$chan} }) { - if ($factoids->{$chan}->{$command}{ref_user} =~ /\Q$args\E/i) { - if ($user ne lc $factoids->{$chan}->{$command}{ref_user} && not $user =~ /$factoids->{$chan}->{$command}{ref_user}/i) { - $user .= " ($factoids->{$chan}->{$command}{ref_user})"; + foreach my $command (sort { ($factoids->{$chan}->{$b}->{last_referenced_on} || 0) <=> ($factoids->{$chan}->{$a}->{last_referenced_on} || 0) } keys %{ $factoids->{$chan} }) { + next if $command eq '_name'; + if ($factoids->{$chan}->{$command}->{ref_user} =~ /\Q$args\E/i) { + if ($user ne lc $factoids->{$chan}->{$command}->{ref_user} && not $user =~ /$factoids->{$chan}->{$command}->{ref_user}/i) { + $user .= " ($factoids->{$chan}->{$command}->{ref_user})"; } my $ago = $factoids->{$chan}->{$command}{last_referenced_on} ? concise ago(gettimeofday - $factoids->{$chan}->{$command}{last_referenced_on}) : "unknown"; - $text .= " $command [$ago]\n"; + $text .= " $factoids->{$chan}->{$command}->{_name} [$ago]\n"; $i++; last if $i >= 20; } @@ -1389,7 +1434,7 @@ sub top20 { sub count { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my $i = 0; my $total = 0; @@ -1402,6 +1447,7 @@ sub count { eval { foreach my $channel (keys %{ $factoids }) { foreach my $command (keys %{ $factoids->{$channel} }) { + next if $command eq '_name'; next if $factoids->{$channel}->{$command}->{type} ne 'text'; $total++; if ($factoids->{$channel}->{$command}->{owner} =~ /^\Q$arguments\E$/i) { @@ -1426,7 +1472,7 @@ sub count { sub factfind { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my $usage = "Usage: factfind [-channel channel] [-owner regex] [-editby regex] [-refby regex] [-regex] [text]"; @@ -1506,24 +1552,21 @@ sub factfind { foreach my $chan (sort keys %{ $factoids }) { next if defined $channel and $chan !~ /^$channel$/i; foreach my $trigger (sort keys %{ $factoids->{$chan} }) { + next if $trigger eq '_name'; if ($factoids->{$chan}->{$trigger}->{type} eq 'text' or $factoids->{$chan}->{$trigger}->{type} eq 'regex') { if ($factoids->{$chan}->{$trigger}->{owner} =~ /^$owner$/i && $factoids->{$chan}->{$trigger}->{ref_user} =~ /^$refby$/i && (exists $factoids->{$chan}->{$trigger}->{edited_by} ? $factoids->{$chan}->{$trigger}->{edited_by} =~ /^$editby$/i : 1)) { next if ($arguments ne "" && $factoids->{$chan}->{$trigger}->{action} !~ /$regex/i && $trigger !~ /$regex/i); - $i++; - if ($chan ne $last_chan) { - $text .= $chan eq '.*' ? "[global channel] " : "[$chan] "; + $text .= $chan eq '.*' ? "[global channel] " : "[$factoids->{$chan}->{_name}] "; $last_chan = $chan; } - if ($trigger =~ / /) { - $text .= "\"$trigger\" "; - } else { - $text .= "$trigger "; - } - $last_trigger = $trigger; + my $trigger_name = $factoids->{$chan}->{$trigger}->{_name}; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + $text .= "$trigger_name "; + $last_trigger = $trigger_name; } } } @@ -1534,19 +1577,18 @@ sub factfind { if ($i == 1) { chop $text; - return "Found one factoid submitted for " . ($last_chan eq '.*' ? 'global channel' : $last_chan) . " " . $argtype . ": $last_trigger is $factoids->{$last_chan}->{$last_trigger}->{action}"; + return "Found one factoid submitted for " . ($last_chan eq '.*' ? 'global channel' : $factoids->{$last_chan}->{_name}) . " " . $argtype . ": $last_trigger is $factoids->{$last_chan}->{$last_trigger}->{action}"; } else { return "Found $i factoids " . $argtype . ": $text" unless $i == 0; - my $chans = (defined $channel ? ($channel eq '.*' ? 'global channel' : $channel) : 'any channels'); - return "No factoids " . $argtype . " submitted for $chans"; + return "No factoids " . $argtype . " submitted for $chans."; } } sub factchange { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids_hash = $self->{pbot}->{factoids}->{factoids}->{hash}; my ($channel, $trigger, $keyword, $delim, $tochange, $changeto, $modifier, $url); $stuff->{preserve_whitespace} = 1; @@ -1629,25 +1671,31 @@ sub factchange { return "/say $keyword not found in channel $from_chan."; } + my $channel_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{_name}; + my $trigger_name = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + $channel_name = 'global' if $channel_name eq '.*'; + $trigger_name = "\"$trigger_name\"" if $trigger_name =~ / /; + $from_chan = '.*' if $from_chan eq 'global'; if ($channel =~ /^#/ and $from_chan =~ /^#/ and $channel ne $from_chan) { - return "/say $trigger belongs to $channel, but this is $from_chan. Please switch to $channel or use /msg to change this factoid."; + return "/say $trigger_name belongs to $channel_name, but this is $from_chan. Please switch to $channel_name or use /msg to change this factoid."; } my $admininfo = $self->{pbot}->{admins}->loggedin($channel, "$nick!$user\@$host"); - if ($factoids->{$channel}->{$trigger}->{'locked'}) { - return "/say $trigger is locked and cannot be changed." if not defined $admininfo; + if ($factoids_hash->{$channel}->{$trigger}->{'locked'}) { + return "/say $trigger_name is locked and cannot be changed." if not defined $admininfo; - if (exists $factoids->{$channel}->{$trigger}->{'effective-level'} - and $admininfo->{level} < $factoids->{$channel}->{$trigger}->{'effective-level'}) { - return "/say $trigger is locked with an effective-level higher than your level and cannot be changed."; + if (exists $factoids_hash->{$channel}->{$trigger}->{'effective-level'} + and $admininfo->{level} < $factoids_hash->{$channel}->{$trigger}->{'effective-level'}) { + return "/say $trigger_name is locked with an effective-level higher than your level and cannot be changed."; } } - my $action = $factoids->{$channel}->{$trigger}->{action}; + my $action = $factoids_hash->{$channel}->{$trigger}->{action}; if (defined $url) { + # FIXME: move this to registry if ($url !~ m/^https?:\/\/(?:sprunge.us|ix.io)\/\w+$/) { return "Invalid URL: acceptable URLs are: http://sprunge.us, http://ix.io"; } @@ -1714,66 +1762,72 @@ sub factchange { return ""; }; - return "/msg $nick Change $trigger: $@" if $@; + if ($@) { + my $err = $@; + $err =~ s/ at PBot\/FactoidCommand.*$//; + return "/msg $nick Change $trigger_name failed: $err"; + } return $ret if length $ret; } if (length $action > 8000 and not defined $admininfo) { - return "Change $trigger failed; result is too long."; + return "Change $trigger_name failed; result is too long."; } if (not length $action) { - return "Change $trigger failed; factoids cannot be empty."; + return "Change $trigger_name failed; factoids cannot be empty."; } $self->{pbot}->{logger}->log("($from) $nick!$user\@$host: changed '$trigger' 's/$tochange/$changeto/\n"); - $factoids->{$channel}->{$trigger}->{action} = $action; - $factoids->{$channel}->{$trigger}->{edited_by} = "$nick!$user\@$host"; - $factoids->{$channel}->{$trigger}->{edited_on} = gettimeofday; + $factoids_hash->{$channel}->{$trigger}->{action} = $action; + $factoids_hash->{$channel}->{$trigger}->{edited_by} = "$nick!$user\@$host"; + $factoids_hash->{$channel}->{$trigger}->{edited_on} = gettimeofday; $self->{pbot}->{factoids}->save_factoids(); - $self->log_factoid($channel, $trigger, "$nick!$user\@$host", "changed to $factoids->{$channel}->{$trigger}->{action}"); - return "Changed: $trigger is " . $factoids->{$channel}->{$trigger}->{action}; + $self->log_factoid($channel, $trigger, "$nick!$user\@$host", "changed to $factoids_hash->{$channel}->{$trigger}->{action}"); + return "Changed: $trigger_name is " . $factoids_hash->{$channel}->{$trigger}->{action}; } +# FIXME: these two functions need to use $stuff->{arglist} + sub load_module { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; my ($keyword, $module) = $arguments =~ /^(.*?)\s+(.*)$/ if defined $arguments; if (not defined $module) { return "Usage: load "; } - if (not exists($factoids->{'.*'}->{$keyword})) { + if (not exists($factoids->{'.*'}->{lc $keyword})) { $self->{pbot}->{factoids}->add_factoid('module', '.*', "$nick!$user\@$host", $keyword, $module); - $factoids->{'.*'}->{$keyword}->{add_nick} = 1; - $factoids->{'.*'}->{$keyword}->{nooverride} = 1; + $factoids->{'.*'}->{lc $keyword}->{add_nick} = 1; + $factoids->{'.*'}->{lc $keyword}->{nooverride} = 1; $self->{pbot}->{logger}->log("$nick!$user\@$host loaded module $keyword => $module\n"); $self->{pbot}->{factoids}->save_factoids(); return "Loaded module $keyword => $module"; } else { - return "There is already a keyword named $keyword."; + return "There is already a keyword named $factoids->{'.*'}->{$keyword}->{_name}."; } } sub unload_module { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; - my $factoids = $self->{pbot}->{factoids}->{factoids}->hash; + my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; if (not defined $arguments) { return "Usage: unload "; - } elsif (not exists $factoids->{'.*'}->{$arguments}) { + } elsif (not exists $factoids->{'.*'}->{lc $arguments}) { return "/say $arguments not found."; - } elsif ($factoids->{'.*'}->{$arguments}{type} ne 'module') { - return "/say $arguments is not a module."; + } elsif ($factoids->{'.*'}->{lc $arguments}->{type} ne 'module') { + return "/say " . $factoids->{'.*'}->{lc $arguments}->{_name} . " is not a module."; } else { - delete $factoids->{'.*'}->{$arguments}; + my $data = delete $factoids->{'.*'}->{lc $arguments}; $self->{pbot}->{factoids}->save_factoids(); $self->{pbot}->{logger}->log("$nick!$user\@$host unloaded module $arguments\n"); - return "/say $arguments unloaded."; + return "/say $data->{_name} unloaded."; } } diff --git a/PBot/FactoidModuleLauncher.pm b/PBot/FactoidModuleLauncher.pm index c873afcf..0fc2deee 100644 --- a/PBot/FactoidModuleLauncher.pm +++ b/PBot/FactoidModuleLauncher.pm @@ -74,7 +74,7 @@ sub execute_module { $stuff->{keyword} = $trigger; $stuff->{trigger} = $trigger; - my $module = $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{action}; + my $module = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{action}; my $module_dir = $self->{pbot}->{registry}->get_value('general', 'module_dir'); $self->{pbot}->{logger}->log("(" . (defined $stuff->{from} ? $stuff->{from} : "(undef)") . "): $stuff->{nick}!$stuff->{user}\@$stuff->{host}: Executing module [$stuff->{command}] $module $stuff->{arguments}\n"); @@ -108,8 +108,8 @@ sub execute_module { Carp::croak("Could not chdir to '$module_dir': $!"); } - if (exists $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{workdir}) { - chdir $self->{pbot}->{factoids}->{factoids}->hash->{$channel}->{$trigger}->{workdir}; + if (exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{workdir}) { + chdir $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{workdir}; } my ($exitval, $stdout, $stderr) = eval { @@ -191,7 +191,7 @@ sub module_pipe_reader { $self->{pbot}->{interpreter}->handle_result($stuff, $stuff->{result}); } else { # don't override nick if already set - if (exists $stuff->{special} and $stuff->{special} ne 'code-factoid' and exists $self->{pbot}->{factoids}->{factoids}->hash->{$stuff->{channel}}->{$stuff->{trigger}}->{add_nick} and $self->{pbot}->{factoids}->{factoids}->hash->{$stuff->{channel}}->{$stuff->{trigger}}->{add_nick} != 0) { + if (exists $stuff->{special} and $stuff->{special} ne 'code-factoid' and exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$stuff->{channel}}->{$stuff->{trigger}}->{add_nick} and $self->{pbot}->{factoids}->{factoids}->{hash}->{$stuff->{channel}}->{$stuff->{trigger}}->{add_nick} != 0) { $stuff->{nickoverride} = $stuff->{nick}; $stuff->{no_nickoverride} = 0; $stuff->{force_nickoverride} = 1; diff --git a/PBot/Factoids.pm b/PBot/Factoids.pm index 8b6fe014..e36475e9 100644 --- a/PBot/Factoids.pm +++ b/PBot/Factoids.pm @@ -34,12 +34,8 @@ use PBot::Utils::Indefinite; use PBot::Utils::ValidateString; sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to Factoids should be key/value pairs, not hash reference"); - } - + Carp::croak("Options to Factoids should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH'; my ($class, %conf) = @_; - my $self = bless {}, $class; $self->initialize(%conf); return $self; @@ -49,13 +45,13 @@ sub initialize { my ($self, %conf) = @_; my $filename = $conf{filename}; - my $pbot = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - $self->{factoids} = PBot::DualIndexHashObject->new(name => 'Factoids', filename => $filename, pbot => $pbot); + $self->{factoids} = PBot::DualIndexHashObject->new(name => 'Factoids', filename => $filename, pbot => $self->{pbot}); - $self->{pbot} = $pbot; - $self->{commands} = PBot::FactoidCommands->new(pbot => $pbot); - $self->{factoidmodulelauncher} = PBot::FactoidModuleLauncher->new(pbot => $pbot); + $self->{pbot} = $self->{pbot}; + $self->{commands} = PBot::FactoidCommands->new(pbot => $self->{pbot}); + $self->{factoidmodulelauncher} = PBot::FactoidModuleLauncher->new(pbot => $self->{pbot}); $self->{pbot}->{registry}->add_default('text', 'factoids', 'default_rate_limit', 15); $self->{pbot}->{registry}->add_default('text', 'factoids', 'max_name_length', 100); @@ -64,7 +60,6 @@ sub initialize { $self->{pbot}->{atexit}->register(sub { $self->save_factoids; return; }); - $self->{compartments} = {}; $self->load_factoids; } @@ -74,12 +69,13 @@ sub load_factoids { $self->{factoids}->load; my ($text, $regex, $modules); - foreach my $channel (keys %{ $self->{factoids}->hash }) { - foreach my $trigger (keys %{ $self->{factoids}->hash->{$channel} }) { - $self->{pbot}->{logger}->log("Missing type for $channel->$trigger\n") if not $self->{factoids}->hash->{$channel}->{$trigger}->{type}; - $text++ if $self->{factoids}->hash->{$channel}->{$trigger}->{type} eq 'text'; - $regex++ if $self->{factoids}->hash->{$channel}->{$trigger}->{type} eq 'regex'; - $modules++ if $self->{factoids}->hash->{$channel}->{$trigger}->{type} eq 'module'; + foreach my $channel (keys %{ $self->{factoids}->{hash} }) { + foreach my $trigger (keys %{ $self->{factoids}->{hash}->{$channel} }) { + next if $trigger eq '_name'; + $self->{pbot}->{logger}->log("Missing type for $channel->$trigger\n") if not $self->{factoids}->{hash}->{$channel}->{$trigger}->{type}; + $text++ if $self->{factoids}->{hash}->{$channel}->{$trigger}->{type} eq 'text'; + $regex++ if $self->{factoids}->{hash}->{$channel}->{$trigger}->{type} eq 'regex'; + $modules++ if $self->{factoids}->{hash}->{$channel}->{$trigger}->{type} eq 'module'; } } $self->{pbot}->{logger}->log(" " . ($text + $regex + $modules) . " factoids loaded ($text text, $regex regexs, $modules modules).\n"); @@ -94,7 +90,6 @@ sub add_default_factoids { sub save_factoids { my $self = shift; - $self->{factoids}->save; $self->export_factoids; } @@ -105,18 +100,28 @@ sub add_factoid { $type = lc $type; $channel = '.*' if $channel !~ /^#/; - $channel = lc $channel; - $self->{factoids}->hash->{$channel}->{$trigger}->{enabled} = 1; - $self->{factoids}->hash->{$channel}->{$trigger}->{type} = $type; - $self->{factoids}->hash->{$channel}->{$trigger}->{action} = $action; - $self->{factoids}->hash->{$channel}->{$trigger}->{owner} = $owner; - $self->{factoids}->hash->{$channel}->{$trigger}->{created_on} = gettimeofday; - $self->{factoids}->hash->{$channel}->{$trigger}->{ref_count} = 0; - $self->{factoids}->hash->{$channel}->{$trigger}->{ref_user} = "nobody"; - $self->{factoids}->hash->{$channel}->{$trigger}->{rate_limit} = $self->{pbot}->{registry}->get_value('factoids', 'default_rate_limit'); + my $data; - $self->save_factoids unless $dont_save; + if (exists $self->{factoids}->{hash}->{lc $channel}->{lc $trigger}) { + # only update action field if force-adding it through factadd -f + $data = $self->{factoids}->{hash}->{lc $channel}->{lc $trigger}; + $data->{action} = $action; + $data->{type} = $type; + } else { + $data = { + enabled => 1, + type => $type, + action => $action, + owner => $owner, + created_on => scalar gettimeofday, + ref_count => 0, + ref_user => "nobody", + rate_limit => $self->{pbot}->{registry}->get_value('factoids', 'default_rate_limit') + }; + } + + $self->{factoids}->add($channel, $trigger, $data, $dont_save); unless ($dont_save) { $self->{commands}->log_factoid($channel, $trigger, $owner, "created: $action"); @@ -129,11 +134,12 @@ sub remove_factoid { $channel = '.*' if $channel !~ /^#/; $channel = lc $channel; + $trigger = lc $trigger; - delete $self->{factoids}->hash->{$channel}->{$trigger}; + delete $self->{factoids}->{hash}->{$channel}->{$trigger}; - if (not scalar keys %{ $self->{factoids}->hash->{$channel} }) { - delete $self->{factoids}->hash->{$channel}; + if (not scalar keys %{ $self->{factoids}->{hash}->{$channel} }) { + delete $self->{factoids}->{hash}->{$channel}; } $self->save_factoids; @@ -160,16 +166,18 @@ sub export_factoids { my $i = 0; my $table_id = 1; - foreach my $channel (sort keys %{ $self->{factoids}->hash }) { - next if not scalar keys %{ $self->{factoids}->hash->{$channel} }; - my $chan = $channel eq '.*' ? 'global' : $channel; + foreach my $channel (sort keys %{ $self->{factoids}->{hash} }) { + next if not scalar keys %{ $self->{factoids}->{hash}->{$channel} }; + my $chan = $self->{factoids}->{hash}->{$channel}->{_name}; + $chan = 'global' if $chan eq '.*'; print FILE "" . encode_entities($chan) . "
\n"; } - foreach my $channel (sort keys %{ $self->{factoids}->hash }) { - next if not scalar keys %{ $self->{factoids}->hash->{$channel} }; - my $chan = $channel eq '.*' ? 'global' : $channel; + foreach my $channel (sort keys %{ $self->{factoids}->{hash} }) { + next if not scalar keys %{ $self->{factoids}->{hash}->{$channel} }; + my $chan = $self->{factoids}->{hash}->{$channel}->{_name}; + $chan = 'global' if $chan eq '.*'; print FILE "\n"; print FILE "
\n

" . encode_entities($chan) . "

\n
\n"; print FILE "\n"; @@ -185,8 +193,10 @@ sub export_factoids { print FILE "\n\n\n"; $table_id++; - foreach my $trigger (sort keys %{ $self->{factoids}->hash->{$channel} }) { - if ($self->{factoids}->hash->{$channel}->{$trigger}->{type} eq 'text') { + foreach my $trigger (sort keys %{ $self->{factoids}->{hash}->{$channel} }) { + next if $trigger eq '_name'; + my $trigger_name = $self->{factoids}->{hash}->{$channel}->{$trigger}->{_name}; + if ($self->{factoids}->{hash}->{$channel}->{$trigger}->{type} eq 'text') { $i++; if ($i % 2) { print FILE "\n"; @@ -194,12 +204,12 @@ sub export_factoids { print FILE "\n"; } - print FILE "\n"; - print FILE "\n"; + print FILE "\n"; + print FILE "\n"; - print FILE "\n"; + print FILE "\n"; - my $action = $self->{factoids}->hash->{$channel}->{$trigger}->{action}; + my $action = $self->{factoids}->{hash}->{$channel}->{$trigger}->{action}; if ($action =~ m/https?:\/\/[^ ]+/) { $action =~ s/(.*?)http(s?:\/\/[^ ]+)/encode_entities($1) . "http" . encode_entities($2) . "<\/a>"/ge; @@ -208,27 +218,27 @@ sub export_factoids { $action = encode_entities($action); } - if (exists $self->{factoids}->hash->{$channel}->{$trigger}->{action_with_args}) { - my $with_args = $self->{factoids}->hash->{$channel}->{$trigger}->{action_with_args}; + if (exists $self->{factoids}->{hash}->{$channel}->{$trigger}->{action_with_args}) { + my $with_args = $self->{factoids}->{hash}->{$channel}->{$trigger}->{action_with_args}; $with_args =~ s/(.*?)http(s?:\/\/[^ ]+)/encode_entities($1) . "http" . encode_entities($2) . "<\/a>"/ge; $with_args =~ s/(.*)<\/a>(.*$)/"$1<\/a>" . encode_entities($2)/e; - print FILE "\n"; + print FILE "\n"; } else { - print FILE "\n"; + print FILE "\n"; } - if (exists $self->{factoids}->hash->{$channel}->{$trigger}->{edited_by}) { - print FILE "\n"; - print FILE "\n"; + if (exists $self->{factoids}->{hash}->{$channel}->{$trigger}->{edited_by}) { + print FILE "\n"; + print FILE "\n"; } else { print FILE "\n"; print FILE "\n"; } - print FILE "\n"; + print FILE "\n"; - if (exists $self->{factoids}->hash->{$channel}->{$trigger}->{last_referenced_on}) { - print FILE "\n"; + if (exists $self->{factoids}->{hash}->{$channel}->{$trigger}->{last_referenced_on}) { + print FILE "\n"; } else { print FILE "\n"; } @@ -281,6 +291,7 @@ sub find_factoid { $from = '.*' if not defined $from or $from !~ /^#/; $from = lc $from; + $keyword = lc $keyword; $self->{pbot}->{logger}->log("from: $from\n") if $debug; @@ -293,7 +304,7 @@ sub find_factoid { $self->{pbot}->{logger}->log("string: $string\n") if $debug; return undef if $self->{pbot}->{commands}->exists($keyword); # check factoids - foreach my $channel (sort keys %{ $self->{factoids}->hash }) { + foreach my $channel (sort keys %{ $self->{factoids}->{hash} }) { if ($opts{exact_channel}) { if ($opts{exact_trigger} == 1) { next unless $from eq lc $channel; @@ -302,11 +313,12 @@ sub find_factoid { } } - foreach my $trigger (keys %{ $self->{factoids}->hash->{$channel} }) { - if ($keyword =~ m/^\Q$trigger\E$/i) { + foreach my $trigger (keys %{ $self->{factoids}->{hash}->{$channel} }) { + next if $trigger eq '_name'; + if ($keyword eq $trigger) { $self->{pbot}->{logger}->log("return $channel: $trigger\n") if $debug; - if ($opts{find_alias} && $self->{factoids}->hash->{$channel}->{$trigger}->{action} =~ /^\/call\s+(.*)$/ms) { + if ($opts{find_alias} && $self->{factoids}->{hash}->{$channel}->{$trigger}->{action} =~ /^\/call\s+(.*)$/ms) { my $command; if (length $arguments) { $command = "$1 $arguments"; @@ -329,19 +341,20 @@ sub find_factoid { # then check regex factoids if (not $opts{exact_trigger}) { - foreach my $channel (sort keys %{ $self->{factoids}->hash }) { + foreach my $channel (sort keys %{ $self->{factoids}->{hash} }) { if ($opts{exact_channel}) { next unless $from eq lc $channel or $channel eq '.*'; } - foreach my $trigger (sort keys %{ $self->{factoids}->hash->{$channel} }) { - if ($self->{factoids}->hash->{$channel}->{$trigger}->{type} eq 'regex') { + foreach my $trigger (sort keys %{ $self->{factoids}->{hash}->{$channel} }) { + next if $trigger eq '_name'; + if ($self->{factoids}->{hash}->{$channel}->{$trigger}->{type} eq 'regex') { $self->{pbot}->{logger}->log("checking regex $string =~ m/$trigger/i\n") if $debug >= 2; if ($string =~ m/$trigger/i) { $self->{pbot}->{logger}->log("return regex $channel: $trigger\n") if $debug; if ($opts{find_alias}) { - my $command = $self->{factoids}->hash->{$channel}->{$trigger}->{action}; + my $command = $self->{factoids}->{hash}->{$channel}->{$trigger}->{action}; my $arglist = $self->{pbot}->{interpreter}->make_args($command); ($keyword, $arguments) = $self->{pbot}->{interpreter}->split_args($arglist, 2); $string = $keyword . (length $arguments ? " $arguments" : ""); @@ -475,14 +488,14 @@ sub expand_factoid_vars { my ($var_chan, $var) = ($factoids[0]->[0], $factoids[0]->[1]); - if ($self->{factoids}->hash->{$var_chan}->{$var}->{action} =~ m{^/call (.*)}ms) { + if ($self->{factoids}->{hash}->{$var_chan}->{$var}->{action} =~ m{^/call (.*)}ms) { $test_v = $1; next if ++$recurse > 100; goto ALIAS; } - if ($self->{factoids}->hash->{$var_chan}->{$var}->{type} eq 'text') { - my $change = $self->{factoids}->hash->{$var_chan}->{$var}->{action}; + if ($self->{factoids}->{hash}->{$var_chan}->{$var}->{type} eq 'text') { + my $change = $self->{factoids}->{hash}->{$var_chan}->{$var}->{action}; my @list = $self->{pbot}->{interpreter}->split_line($change); my @mylist; for (my $i = 0; $i <= $#list; $i++) { @@ -695,7 +708,7 @@ sub expand_action_arguments { sub execute_code_factoid_using_vm { my ($self, $stuff) = @_; - unless (exists $self->{factoids}->hash->{$stuff->{channel}}->{$stuff->{keyword}}->{interpolate} and $self->{factoids}->hash->{$stuff->{channel}}->{$stuff->{keyword}}->{interpolate} eq '0') { + unless (exists $self->{factoids}->{hash}->{lc $stuff->{channel}}->{lc $stuff->{keyword}}->{interpolate} and $self->{factoids}->{hash}->{lc $stuff->{channel}}->{lc $stuff->{keyword}}->{interpolate} eq '0') { if ($stuff->{code} =~ m/(?:\$\{?nick\b|\$\{?args\b|\$\{?arg\[)/ and length $stuff->{arguments}) { $stuff->{no_nickoverride} = 1; } else { @@ -705,7 +718,7 @@ sub execute_code_factoid_using_vm { $stuff->{action} = $stuff->{code}; $stuff->{code} = $self->expand_factoid_vars($stuff); - if ($self->{factoids}->hash->{$stuff->{channel}}->{$stuff->{keyword}}->{'allow_empty_args'}) { + if ($self->{factoids}->{hash}->{lc $stuff->{channel}}->{lc $stuff->{keyword}}->{'allow_empty_args'}) { $stuff->{code} = $self->expand_action_arguments($stuff->{code}, $stuff->{arguments}, ''); } else { $stuff->{code} = $self->expand_action_arguments($stuff->{code}, $stuff->{arguments}, $stuff->{nick}); @@ -716,8 +729,8 @@ sub execute_code_factoid_using_vm { my %h = (nick => $stuff->{nick}, channel => $stuff->{from}, lang => $stuff->{lang}, code => $stuff->{code}, arguments => $stuff->{arguments}, factoid => "$stuff->{channel}:$stuff->{keyword}"); - if (exists $self->{factoids}->hash->{$stuff->{channel}}->{$stuff->{keyword}}->{'persist-key'}) { - $h{'persist-key'} = $self->{factoids}->hash->{$stuff->{channel}}->{$stuff->{keyword}}->{'persist-key'}; + if (exists $self->{factoids}->{hash}->{lc $stuff->{channel}}->{lc $stuff->{keyword}}->{'persist-key'}) { + $h{'persist-key'} = $self->{factoids}->{hash}->{lc $stuff->{channel}}->{lc $stuff->{keyword}}->{'persist-key'}; } my $json = encode_json \%h; @@ -766,7 +779,7 @@ sub interpreter { $stuff->{ref_from} = ""; } - if (defined $channel and not $channel eq '.*' and not lc $channel eq $stuff->{from}) { + if (defined $channel and not $channel eq '.*' and not $channel eq lc $stuff->{from}) { $stuff->{ref_from} = $channel; } @@ -782,11 +795,12 @@ sub interpreter { my ($fwd_chan, $fwd_trig); # build string of which channels contain the keyword, keeping track of the last one and count - foreach my $chan (keys %{ $self->{factoids}->hash }) { - foreach my $trig (keys %{ $self->{factoids}->hash->{$chan} }) { - my $type = $self->{factoids}->hash->{$chan}->{$trig}->{type}; - if (($type eq 'text' or $type eq 'module') and lc $trig eq $lc_keyword) { - $chans .= $comma . $chan; + foreach my $chan (keys %{ $self->{factoids}->{hash} }) { + foreach my $trig (keys %{ $self->{factoids}->{hash}->{$chan} }) { + next if $trig eq '_name'; + my $type = $self->{factoids}->{hash}->{$chan}->{$trig}->{type}; + if (($type eq 'text' or $type eq 'module') and $trig eq $lc_keyword) { + $chans .= $comma . $self->{factoids}->{hash}->{$chan}->{_name}; $comma = ", "; $found++; $fwd_chan = $chan; @@ -801,7 +815,7 @@ sub interpreter { # if multiple channels have this keyword, then ask user to disambiguate if ($found > 1) { return undef if $stuff->{referenced}; - return $ref_from . "Ambiguous keyword '$original_keyword' exists in multiple channels (use 'fact ' to choose one): $chans"; + return $ref_from . "Ambiguous keyword '$original_keyword' exists in multiple channels (use 'fact $original_keyword' to choose one): $chans"; } # if there's just one other channel that has this keyword, trigger that instance elsif ($found == 1) { @@ -849,53 +863,53 @@ sub interpreter { $stuff->{channel} = $channel; $stuff->{original_keyword} = $original_keyword; - return undef if $stuff->{referenced} and $self->{factoids}->hash->{$channel}->{$keyword}->{noembed}; + return undef if $stuff->{referenced} and $self->{factoids}->{hash}->{$channel}->{$keyword}->{noembed}; - if (exists $self->{factoids}->hash->{$channel}->{$keyword}->{locked_to_channel}) { - if ($stuff->{ref_from} ne "") { # called from annother channel + if (exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{locked_to_channel}) { + if ($stuff->{ref_from} ne "") { # called from another channel return "$keyword may be invoked only in $stuff->{ref_from}."; } } - if (exists $self->{factoids}->hash->{$channel}->{$keyword}->{last_referenced_on}) { - if (exists $self->{factoids}->hash->{$channel}->{$keyword}->{last_referenced_in}) { - if ($self->{factoids}->hash->{$channel}->{$keyword}->{last_referenced_in} eq $stuff->{from}) { + if (exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{last_referenced_on}) { + if (exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{last_referenced_in}) { + if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{last_referenced_in} eq $stuff->{from}) { my $ratelimit = $self->{pbot}->{registry}->get_value($stuff->{from}, 'ratelimit_override'); - $ratelimit = $self->{factoids}->hash->{$channel}->{$keyword}->{rate_limit} if not defined $ratelimit; - if (gettimeofday - $self->{factoids}->hash->{$channel}->{$keyword}->{last_referenced_on} < $ratelimit) { + $ratelimit = $self->{factoids}->{hash}->{$channel}->{$keyword}->{rate_limit} if not defined $ratelimit; + if (gettimeofday - $self->{factoids}->{hash}->{$channel}->{$keyword}->{last_referenced_on} < $ratelimit) { my $ref_from = $stuff->{ref_from} ? "[$stuff->{ref_from}] " : ""; - return "/msg $stuff->{nick} $ref_from'$keyword' is rate-limited; try again in " . duration ($ratelimit - int(gettimeofday - $self->{factoids}->hash->{$channel}->{$keyword}->{last_referenced_on})) . "." unless $self->{pbot}->{admins}->loggedin($channel, "$stuff->{nick}!$stuff->{user}\@$stuff->{host}"); + return "/msg $stuff->{nick} $ref_from'$keyword' is rate-limited; try again in " . duration ($ratelimit - int(gettimeofday - $self->{factoids}->{hash}->{$channel}->{$keyword}->{last_referenced_on})) . "." unless $self->{pbot}->{admins}->loggedin($channel, "$stuff->{nick}!$stuff->{user}\@$stuff->{host}"); } } } } - $self->{factoids}->hash->{$channel}->{$keyword}->{ref_count}++; - $self->{factoids}->hash->{$channel}->{$keyword}->{ref_user} = "$stuff->{nick}!$stuff->{user}\@$stuff->{host}"; - $self->{factoids}->hash->{$channel}->{$keyword}->{last_referenced_on} = gettimeofday; - $self->{factoids}->hash->{$channel}->{$keyword}->{last_referenced_in} = $stuff->{from} || "stdin"; + $self->{factoids}->{hash}->{$channel}->{$keyword}->{ref_count}++; + $self->{factoids}->{hash}->{$channel}->{$keyword}->{ref_user} = "$stuff->{nick}!$stuff->{user}\@$stuff->{host}"; + $self->{factoids}->{hash}->{$channel}->{$keyword}->{last_referenced_on} = gettimeofday; + $self->{factoids}->{hash}->{$channel}->{$keyword}->{last_referenced_in} = $stuff->{from} || "stdin"; my $action; - if (exists $self->{factoids}->hash->{$channel}->{$keyword}->{usage} and not length $stuff->{arguments} and $self->{factoids}->hash->{$channel}->{$keyword}->{requires_arguments}) { + if (exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{usage} and not length $stuff->{arguments} and $self->{factoids}->{hash}->{$channel}->{$keyword}->{requires_arguments}) { $stuff->{alldone} = 1; - my $usage = $self->{factoids}->hash->{$channel}->{$keyword}->{usage}; + my $usage = $self->{factoids}->{hash}->{$channel}->{$keyword}->{usage}; $usage =~ s/\$0|\$\{0\}/$keyword/g; return $usage; } - if (length $stuff->{arguments} and exists $self->{factoids}->hash->{$channel}->{$keyword}->{action_with_args}) { - $action = $self->{factoids}->hash->{$channel}->{$keyword}->{action_with_args}; + if (length $stuff->{arguments} and exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{action_with_args}) { + $action = $self->{factoids}->{hash}->{$channel}->{$keyword}->{action_with_args}; } else { - $action = $self->{factoids}->hash->{$channel}->{$keyword}->{action}; + $action = $self->{factoids}->{hash}->{$channel}->{$keyword}->{action}; } if ($action =~ m{^/code\s+([^\s]+)\s+(.+)$}msi) { my ($lang, $code) = ($1, $2); - if (exists $self->{factoids}->hash->{$channel}->{$keyword}->{usage} and not length $stuff->{arguments}) { + if (exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{usage} and not length $stuff->{arguments}) { $stuff->{alldone} = 1; - my $usage = $self->{factoids}->hash->{$channel}->{$keyword}->{usage}; + my $usage = $self->{factoids}->{hash}->{$channel}->{$keyword}->{usage}; $usage =~ s/\$0|\$\{0\}/$keyword/g; return $usage; } @@ -926,14 +940,14 @@ sub handle_action { my $ref_from = $stuff->{ref_from} ? "[$stuff->{ref_from}] " : ""; - unless (exists $self->{factoids}->hash->{$channel}->{$keyword}->{interpolate} and $self->{factoids}->hash->{$channel}->{$keyword}->{interpolate} eq '0') { + unless (exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{interpolate} and $self->{factoids}->{hash}->{$channel}->{$keyword}->{interpolate} eq '0') { my ($root_channel, $root_keyword) = $self->find_factoid($stuff->{ref_from} ? $stuff->{ref_from} : $stuff->{from}, $stuff->{root_keyword}, arguments => $stuff->{arguments}, exact_channel => 1); if (not defined $root_channel or not defined $root_keyword) { $root_channel = $channel; $root_keyword = $keyword; } - if (not length $stuff->{keyword_override} and length $self->{factoids}->hash->{$root_channel}->{$root_keyword}->{keyword_override}) { - $stuff->{keyword_override} = $self->{factoids}->hash->{$root_channel}->{$root_keyword}->{keyword_override}; + if (not length $stuff->{keyword_override} and length $self->{factoids}->{hash}->{$root_channel}->{$root_keyword}->{keyword_override}) { + $stuff->{keyword_override} = $self->{factoids}->{hash}->{$root_channel}->{$root_keyword}->{keyword_override}; } $stuff->{action} = $action; $action = $self->expand_factoid_vars($stuff); @@ -941,7 +955,7 @@ sub handle_action { if (length $stuff->{arguments}) { if ($action =~ m/\$\{?args/ or $action =~ m/\$\{?arg\[/) { - unless (defined $self->{factoids}->hash->{$channel}->{$keyword}->{interpolate} and $self->{factoids}->hash->{$channel}->{$keyword}->{interpolate} eq '0') { + unless (defined $self->{factoids}->{hash}->{$channel}->{$keyword}->{interpolate} and $self->{factoids}->{hash}->{$channel}->{$keyword}->{interpolate} eq '0') { $action = $self->expand_action_arguments($action, $stuff->{arguments}, $stuff->{nick}); $stuff->{no_nickoverride} = 1; } else { @@ -950,7 +964,7 @@ sub handle_action { $stuff->{arguments} = ""; $stuff->{original_arguments} = ""; } 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_similar($stuff->{from}, $stuff->{arguments}); if ($target and $action !~ /\$\{?(?:nick|args)\b/) { @@ -963,12 +977,12 @@ sub handle_action { } } else { # no arguments supplied, replace $args with $nick/$tonick, etc - if (exists $self->{factoids}->hash->{$channel}->{$keyword}->{usage}) { - $action = "/say " . $self->{factoids}->hash->{$channel}->{$keyword}->{usage}; + if (exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{usage}) { + $action = "/say " . $self->{factoids}->{hash}->{$channel}->{$keyword}->{usage}; $action =~ s/\$0|\$\{0\}/$keyword/g; $stuff->{alldone} = 1; } else { - if ($self->{factoids}->hash->{$channel}->{$keyword}->{'allow_empty_args'}) { + if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{'allow_empty_args'}) { $action = $self->expand_action_arguments($action, undef, ''); } else { $action = $self->expand_action_arguments($action, undef, $stuff->{nick}); @@ -981,13 +995,13 @@ sub handle_action { if ($action =~ /^\/call\s+(.*)$/ms) { my $command = $1; $command =~ s/\n$//; - unless ($self->{factoids}->hash->{$channel}->{$keyword}->{'require_explicit_args'}) { + unless ($self->{factoids}->{hash}->{$channel}->{$keyword}->{'require_explicit_args'}) { my $args = $stuff->{arguments}; $command .= " $args" if length $args and not $stuff->{special} eq 'code-factoid'; $stuff->{arguments} = ''; } - unless ($self->{factoids}->hash->{$channel}->{$keyword}->{'no_keyword_override'}) { + unless ($self->{factoids}->{hash}->{$channel}->{$keyword}->{'no_keyword_override'}) { if ($command =~ s/\s*--keyword-override=([^ ]+)\s*//) { $stuff->{keyword_override} = $1; } @@ -998,12 +1012,12 @@ sub handle_action { $self->{pbot}->{logger}->log("[" . (defined $stuff->{from} ? $stuff->{from} : "stdin") . "] ($stuff->{nick}!$stuff->{user}\@$stuff->{host}) [$keyword_text] aliased to: [$command]\n"); - if (defined $self->{factoids}->hash->{$channel}->{$keyword}->{'effective-level'}) { - if ($self->{factoids}->hash->{$channel}->{$keyword}->{'locked'}) { + if (defined $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'}) { + if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{'locked'}) { $self->{pbot}->{logger}->log("Effective-level set to $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'}\n"); - $stuff->{'effective-level'} = $self->{factoids}->hash->{$channel}->{$keyword}->{'effective-level'}; + $stuff->{'effective-level'} = $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'}; } else { - $self->{pbot}->{logger}->log("Ignoring effective-level of $self->{factoids}->hash->{$channel}->{$keyword}->{'effective-level'} on unlocked factoid\n"); + $self->{pbot}->{logger}->log("Ignoring effective-level of $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'} on unlocked factoid\n"); } } @@ -1012,24 +1026,24 @@ sub handle_action { $self->{pbot}->{logger}->log("(" . (defined $stuff->{from} ? $stuff->{from} : "(undef)") . "): $stuff->{nick}!$stuff->{user}\@$stuff->{host}: $keyword_text: action: \"$action\"\n"); - if ($self->{factoids}->hash->{$channel}->{$keyword}->{enabled} == 0) { + if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{enabled} == 0) { $self->{pbot}->{logger}->log("$keyword_text disabled.\n"); return "/msg $stuff->{nick} ${ref_from}$keyword_text is currently disabled."; } - unless (exists $self->{factoids}->hash->{$channel}->{$keyword}->{interpolate} and $self->{factoids}->hash->{$channel}->{$keyword}->{interpolate} eq '0') { + unless (exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{interpolate} and $self->{factoids}->{hash}->{$channel}->{$keyword}->{interpolate} eq '0') { my ($root_channel, $root_keyword) = $self->find_factoid($stuff->{ref_from} ? $stuff->{ref_from} : $stuff->{from}, $stuff->{root_keyword}, arguments => $stuff->{arguments}, exact_channel => 1); if (not defined $root_channel or not defined $root_keyword) { $root_channel = $channel; $root_keyword = $keyword; } - if (not length $stuff->{keyword_override} and length $self->{factoids}->hash->{$root_channel}->{$root_keyword}->{keyword_override}) { - $stuff->{keyword_override} = $self->{factoids}->hash->{$root_channel}->{$root_keyword}->{keyword_override}; + if (not length $stuff->{keyword_override} and length $self->{factoids}->{hash}->{$root_channel}->{$root_keyword}->{keyword_override}) { + $stuff->{keyword_override} = $self->{factoids}->{hash}->{$root_channel}->{$root_keyword}->{keyword_override}; } $stuff->{action} = $action; $action = $self->expand_factoid_vars($stuff); - if ($self->{factoids}->hash->{$channel}->{$keyword}->{'allow_empty_args'}) { + if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{'allow_empty_args'}) { $action = $self->expand_action_arguments($action, $stuff->{arguments}, ''); } else { $action = $self->expand_action_arguments($action, $stuff->{arguments}, $stuff->{nick}); @@ -1038,8 +1052,8 @@ sub handle_action { return $action if $stuff->{special} eq 'code-factoid'; - if ($self->{factoids}->hash->{$channel}->{$keyword}->{type} eq 'module') { - my $preserve_whitespace = $self->{factoids}->hash->{$channel}->{$keyword}->{preserve_whitespace}; + if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{type} eq 'module') { + my $preserve_whitespace = $self->{factoids}->{hash}->{$channel}->{$keyword}->{preserve_whitespace}; $preserve_whitespace = 0 if not defined $preserve_whitespace; $stuff->{preserve_whitespace} = $preserve_whitespace; @@ -1053,7 +1067,7 @@ sub handle_action { return ""; } } - elsif ($self->{factoids}->hash->{$channel}->{$keyword}->{type} eq 'text') { + elsif ($self->{factoids}->{hash}->{$channel}->{$keyword}->{type} eq 'text') { # Don't allow user-custom /msg factoids, unless factoid triggered by admin if ($action =~ m/^\/msg/i) { my $admin = $self->{pbot}->{admins}->loggedin($stuff->{from}, "$stuff->{nick}!$stuff->{user}\@$stuff->{host}"); @@ -1074,12 +1088,12 @@ sub handle_action { if ($action =~ m/^\/(?:say|me|msg)/i) { return $action; } elsif ($action =~ s/^\/kick\s+//) { - if (not exists $self->{factoids}->hash->{$channel}->{$keyword}->{'effective-level'}) { + if (not exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'}) { $stuff->{authorized} = 0; return "/say $stuff->{nick}: $keyword_text doesn't have the effective-level to do that."; } my $level = 10; - if ($self->{factoids}->hash->{$channel}->{$keyword}->{'effective-level'} >= $level) { + if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'} >= $level) { $stuff->{authorized} = 1; return "/kick " . $action; } else { @@ -1090,7 +1104,7 @@ sub handle_action { return "/say $keyword_text is $action"; } } - } elsif ($self->{factoids}->hash->{$channel}->{$keyword}->{type} eq 'regex') { + } elsif ($self->{factoids}->{hash}->{$channel}->{$keyword}->{type} eq 'regex') { my $result = eval { my $string = "$stuff->{original_keyword}" . (defined $stuff->{arguments} ? " $stuff->{arguments}" : ""); my $cmd; diff --git a/PBot/HashObject.pm b/PBot/HashObject.pm index dfd4d1cd..43388d27 100644 --- a/PBot/HashObject.pm +++ b/PBot/HashObject.pm @@ -2,7 +2,9 @@ # Author: pragma_ # # Purpose: Provides a hash-table object with an abstracted API that includes -# setting and deleting values, saving to and loading from files, etc. +# setting and deleting values, saving to and loading from files, etc. Provides +# case-insensitive access to the index key while preserving original case when +# displaying index key. # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this @@ -20,12 +22,8 @@ use Carp (); use JSON; sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to HashObject should be key/value pairs, not hash reference"); - } - + Carp::croak("Options to HashObject should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH'; my ($class, %conf) = @_; - my $self = bless {}, $class; $self->initialize(%conf); return $self; @@ -33,34 +31,17 @@ sub new { sub initialize { my ($self, %conf) = @_; - - $self->{name} = delete $conf{name} // 'hash object'; - $self->{filename} = delete $conf{filename} // Carp::carp("Missing filename to HashObject, will not be able to save to or load from file."); - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + $self->{name} = $conf{name} // 'hash object'; + $self->{filename} = $conf{filename} // Carp::carp("Missing filename to HashObject, will not be able to save to or load from file."); + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{hash} = {}; } -sub hash_add { - my ($self, $index_key, $hash) = @_; - - if (defined $hash) { - if (exists $self->hash->{$index_key}) { - return undef; - } - - foreach my $key (keys %$hash) { - $self->hash->{$index_key}{$key} = $hash->{$key}; - } - return 1; - } - return undef; -} - sub load { my $self = shift; my $filename; - if (@_) { $filename = shift; } else { $filename = $self->filename; } + if (@_) { $filename = shift; } else { $filename = $self->{filename}; } if (not defined $filename) { Carp::carp "No $self->{name} filename specified -- skipping loading from file"; @@ -81,13 +62,31 @@ sub load { $self->{hash} = decode_json $contents; close FILE; + + # update existing entries to use _name to preserve case + # and lowercase any non-lowercased entries + foreach my $index (keys %{ $self->{hash} }) { + if (not exists $self->{hash}->{$index}->{_name}) { + if (lc $index eq $index) { + $self->{hash}->{$index}->{_name} = $index; + } else { + if (exists $self->{hash}->{lc $index}) { + Carp::croak "Cannot update $self->{name} object $index; duplicate object found"; + } + + my $data = delete $self->{hash}->{$index}; + $data->{_name} = $index; + $self->{hash}->{lc $index} = $data; + } + } + } } sub save { my $self = shift; my $filename; - if (@_) { $filename = shift; } else { $filename = $self->filename; } + if (@_) { $filename = shift; } else { $filename = $self->{filename}; } if (not defined $filename) { Carp::carp "No $self->{name} filename specified -- skipping saving to file.\n"; @@ -109,40 +108,14 @@ sub clear { $self->{hash} = {}; } -sub find_hash { - my ($self, $keyword) = @_; - - my $result = eval { - foreach my $index (keys %{ $self->hash }) { - if ($keyword =~ m/^\Q$index\E$/i) { - return $index; - } - } - - return undef; - }; - - if ($@) { - $self->{pbot}->{logger}->log("find_hash: bad regex: $@\n"); - return undef; - } - - return $result; -} - sub levenshtein_matches { my ($self, $keyword) = @_; my $comma = ''; my $result = ""; - foreach my $index (sort keys %{ $self->hash }) { + foreach my $index (sort keys %{ $self->{hash} }) { my $distance = fastdistance($keyword, $index); - - # print "Distance $distance for $keyword (" , (length $keyword) , ") vs $index (" , length $index , ")\n"; - - my $length = (length($keyword) > length($index)) ? length $keyword : length $index; - - # print "Percentage: ", $distance / $length, "\n"; + my $length = (length $keyword > length $index) ? length $keyword : length $index; if ($length != 0 && $distance / $length < 0.50) { $result .= $comma . $index; @@ -157,20 +130,20 @@ sub levenshtein_matches { sub set { my ($self, $index, $key, $value, $dont_save) = @_; + my $lc_index = lc $index; - my $hash_index = $self->find_hash($index); - - if (not $hash_index) { - my $result = "No such $self->{name} object '$index'; similiar matches: "; + if (not exists $self->{hash}->{$lc_index}) { + my $result = "$self->{name}: $index not found; similiar matches: "; $result .= $self->levenshtein_matches($index); return $result; } if (not defined $key) { - my $result = "[$self->{name}] $hash_index keys: "; + my $result = "[$self->{name}] $self->{hash}->{$lc_index}->{_name} keys: "; my $comma = ''; - foreach my $k (sort keys %{ $self->hash->{$hash_index} }) { - $result .= $comma . "$k => " . $self->hash->{$hash_index}{$k}; + foreach my $k (sort keys %{ $self->{hash}->{$lc_index} }) { + next if $k eq '_name'; + $result .= $comma . "$k => " . $self->{hash}->{$lc_index}->{$k}; $comma = "; "; } $result .= "none" if ($comma eq ''); @@ -178,73 +151,59 @@ sub set { } if (not defined $value) { - $value = $self->hash->{$hash_index}{$key}; + $value = $self->{hash}->{$lc_index}->{$key}; } else { - $self->hash->{$hash_index}{$key} = $value; - $self->save() unless $dont_save; + $self->{hash}->{$lc_index}->{$key} = $value; + $self->save unless $dont_save; } - return "[$self->{name}] $hash_index: '$key' " . (defined $value ? "set to '$value'" : "is not set."); + return "[$self->{name}] $self->{hash}->{$lc_index}->{_name}: '$key' " . (defined $value ? "set to '$value'" : "is not set."); } sub unset { my ($self, $index, $key) = @_; + my $lc_index = lc $index; - my $hash_index = $self->find_hash($index); - - if (not $hash_index) { - my $result = "No such $self->{name} object '$index'; similiar matches: "; + if (not exists $self->{hash}->{$lc_index}) { + my $result = "$self->{name}: $index not found; similiar matches: "; $result .= $self->levenshtein_matches($index); return $result; } - delete $self->hash->{$hash_index}{$key}; - $self->save(); + delete $self->{hash}->{$lc_index}->{$key}; + $self->save; - return "[$self->{name}] $hash_index: '$key' unset."; + return "[$self->{name}] $self->{hash}->{$lc_index}->{_name}: '$key' unset."; } sub add { - my ($self, $index_key, $hash) = @_; + my ($self, $index, $data) = @_; + my $lc_index = lc $index; - if ($self->hash_add($index_key, $hash)) { - $self->save(); - } else { - return "Error occurred adding new $self->{name} object."; + if (exists $self->{hash}->{$lc_index}) { + return "Error: $self->{hash}->{$lc_index}->{_name} already exists in $self->{name}."; } - return "'$index_key' added to $self->{name}."; + $data->{_name} = $index; # preserve case of index + $self->{hash}->{$lc_index} = {%$data}; + $self->save; + return "$index added to $self->{name}."; } sub remove { my ($self, $index) = @_; + my $lc_index = lc $index; - my $hash_index = $self->find_hash($index); - - if (not $hash_index) { - my $result = "No such $self->{name} object '$index'; similiar matches: "; - $result .= $self->levenshtein_matches($index); + if (not exists $self->{hash}->{$lc_index}) { + my $result = "$self->{name}: $index not found; similiar matches: "; + $result .= $self->levenshtein_matches($lc_index); return $result; } - delete $self->hash->{$hash_index}; - $self->save(); + my $data = delete $self->{hash}->{$lc_index}; + $self->save; - return "'$hash_index' removed from $self->{name}."; -} - -# Getters and setters - -sub hash { - my $self = shift; - return $self->{hash}; -} - -sub filename { - my $self = shift; - - if (@_) { $self->{filename} = shift; } - return $self->{filename}; + return "$data->{_name} removed from $self->{name}."; } 1; diff --git a/PBot/IRCHandlers.pm b/PBot/IRCHandlers.pm index 8654b744..5f890d5e 100644 --- a/PBot/IRCHandlers.pm +++ b/PBot/IRCHandlers.pm @@ -297,7 +297,7 @@ sub on_mode { if ($mode eq "+b") { if ($nick eq "ChanServ" or $target =~ m/##fix_your_connection$/i) { if ($self->{pbot}->{chanops}->can_gain_ops($channel)) { - $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$target}{timeout} = gettimeofday + $self->{pbot}->{registry}->get_value('bantracker', 'chanserv_ban_timeout'); + $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{lc $channel}->{lc $target}->{timeout} = gettimeofday + $self->{pbot}->{registry}->get_value('bantracker', 'chanserv_ban_timeout'); $self->{pbot}->{chanops}->{unban_timeout}->save; } } elsif ($target =~ m/^\*!\*@/ or $target =~ m/^\*!.*\@gateway\/web/i) { @@ -308,9 +308,9 @@ sub on_mode { } if ($timeout && $self->{pbot}->{chanops}->can_gain_ops($channel)) { - if (not exists $self->{pbot}->{chanops}->{unban_timeout}->hash->{lc $channel}->{lc $target}) { + if (not exists $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{lc $channel}->{lc $target}) { $self->{pbot}->{logger}->log("Temp ban for $target in $channel.\n"); - $self->{pbot}->{chanops}->{unban_timeout}->hash->{$channel}->{$target}{timeout} = gettimeofday + $timeout; + $self->{pbot}->{chanops}->{unban_timeout}->{hash}->{lc $channel}->{lc $target}->{timeout} = gettimeofday + $timeout; $self->{pbot}->{chanops}->{unban_timeout}->save; } } @@ -319,7 +319,7 @@ sub on_mode { elsif ($mode eq "+q") { if ($nick ne $event->{conn}->nick) { # bot muted if ($self->{pbot}->{chanops}->can_gain_ops($channel)) { - $self->{pbot}->{chanops}->{unmute_timeout}->hash->{$channel}->{$target}{timeout} = gettimeofday + $self->{pbot}->{registry}->get_value('bantracker', 'mute_timeout'); + $self->{pbot}->{chanops}->{unmute_timeout}->{hash}->{lc $channel}->{lc $target}->{timeout} = gettimeofday + $self->{pbot}->{registry}->get_value('bantracker', 'mute_timeout'); $self->{pbot}->{chanops}->{unmute_timeout}->save; } } diff --git a/PBot/Interpreter.pm b/PBot/Interpreter.pm index a222480e..37a21145 100644 --- a/PBot/Interpreter.pm +++ b/PBot/Interpreter.pm @@ -850,11 +850,11 @@ sub handle_result { my ($chan, $trigger) = $self->{pbot}->{factoids}->find_factoid($stuff->{from}, $cmd, arguments => $args, exact_channel => 1, exact_trigger => 0, find_alias => 1); if (defined $trigger) { if ($stuff->{preserve_whitespace} == 0) { - $stuff->{preserve_whitespace} = $self->{pbot}->{factoids}->{factoids}->hash->{$chan}->{$trigger}->{preserve_whitespace}; + $stuff->{preserve_whitespace} = $self->{pbot}->{factoids}->{factoids}->{hash}->{$chan}->{$trigger}->{preserve_whitespace}; $stuff->{preserve_whitespace} = 0 if not defined $stuff->{preserve_whitespace}; } - $use_output_queue = $self->{pbot}->{factoids}->{factoids}->hash->{$chan}->{$trigger}->{use_output_queue}; + $use_output_queue = $self->{pbot}->{factoids}->{factoids}->{hash}->{$chan}->{$trigger}->{use_output_queue}; $use_output_queue = 0 if not defined $use_output_queue; } } diff --git a/PBot/NickList.pm b/PBot/NickList.pm index da104ee1..920af598 100644 --- a/PBot/NickList.pm +++ b/PBot/NickList.pm @@ -96,7 +96,7 @@ sub update_timestamp { $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 => gettimeofday }; + $self->{nicklist}->{$channel}->{$nick} = { nick => $orig_nick, timestamp => scalar gettimeofday }; } } diff --git a/PBot/Refresher.pm b/PBot/Refresher.pm index 621a3557..f6a0e71e 100644 --- a/PBot/Refresher.pm +++ b/PBot/Refresher.pm @@ -64,8 +64,8 @@ sub refresh { # update version factoid my $version = $self->{pbot}->{version}->version(); - if ($self->{pbot}->{factoids}->{factoids}->hash->{'.*'}->{'version'}->{'action'} ne "/say $version") { - $self->{pbot}->{factoids}->{factoids}->hash->{'.*'}->{'version'}->{'action'} = "/say $version"; + if ($self->{pbot}->{factoids}->{factoids}->{hash}->{'.*'}->{'version'}->{'action'} ne "/say $version") { + $self->{pbot}->{factoids}->{factoids}->{hash}->{'.*'}->{'version'}->{'action'} = "/say $version"; $self->{pbot}->{logger}->log("New version: $version\n"); } diff --git a/PBot/Registry.pm b/PBot/Registry.pm index e644b22f..8b62651c 100644 --- a/PBot/Registry.pm +++ b/PBot/Registry.pm @@ -49,9 +49,10 @@ sub initialize { sub load { my $self = shift; $self->{registry}->load; - foreach my $section (keys %{ $self->{registry}->hash }) { - foreach my $item (keys %{ $self->{registry}->hash->{$section} }) { - $self->process_trigger($section, $item, $self->{registry}->hash->{$section}->{$item}->{value}); + foreach my $section (keys %{ $self->{registry}->{hash} }) { + foreach my $item (keys %{ $self->{registry}->{hash}->{$section} }) { + next if $item eq '_name'; + $self->process_trigger($section, $item, $self->{registry}->{hash}->{$section}->{$item}->{value}); } } } @@ -70,16 +71,25 @@ sub add { my $self = shift; my ($type, $section, $item, $value, $is_default) = @_; + my $lc_section = lc $section; + my $lc_item = lc $item; + $type = lc $type; - $section = lc $section; - $item = lc $item; if ($is_default) { - return if exists $self->{registry}->hash->{$section} and exists $self->{registry}->hash->{$section}->{$item}; + return if exists $self->{registry}->{hash}->{$lc_section} and exists $self->{registry}->{hash}->{$lc_section}->{$lc_item}; } - $self->{registry}->hash->{$section}->{$item}->{value} = $value; - $self->{registry}->hash->{$section}->{$item}->{type} = $type unless exists $self->{registry}->hash->{$section}->{$item}->{type}; + if (not exists $self->{registry}->{hash}->{$lc_section}) { + $self->{registry}->{hash}->{$lc_section}->{_name} = $section; + } + + if (not exists $self->{registry}->{hash}->{$lc_section}->{$lc_item}) { + $self->{registry}->{hash}->{$lc_section}->{$lc_item}->{_name} = $item; + } + + $self->{registry}->{hash}->{$lc_section}->{$lc_item}->{value} = $value; + $self->{registry}->{hash}->{$lc_section}->{$lc_item}->{type} = $type unless exists $self->{registry}->{hash}->{$lc_section}->{$lc_item}->{type}; $self->process_trigger($section, $item, $value) unless $is_default; $self->save unless $is_default; @@ -90,11 +100,12 @@ sub remove { my ($section, $item) = @_; $section = lc $section; + $item = lc $item; - delete $self->{registry}->hash->{$section}->{$item}; + delete $self->{registry}->{hash}->{$section}->{$item}; - if (not scalar keys %{ $self->{registry}->hash->{$section} }) { - delete $self->{registry}->hash->{$section}; + if (not scalar keys %{ $self->{registry}->{hash}->{$section} }) { + delete $self->{registry}->{hash}->{$section}; } $self->save; @@ -113,9 +124,9 @@ sub set { $key = lc $key if defined $key; if ($is_default) { - return if exists $self->{registry}->hash->{$section} - and exists $self->{registry}->hash->{$section}->{$item} - and exists $self->{registry}->hash->{$section}->{$item}->{$key}; + return if exists $self->{registry}->{hash}->{$section} + and exists $self->{registry}->{hash}->{$section}->{$item} + and exists $self->{registry}->{hash}->{$section}->{$item}->{$key}; } my $oldvalue = $self->get_value($section, $item, 1) if defined $value; @@ -146,19 +157,22 @@ sub unset { sub get_value { my ($self, $section, $item, $as_text, $stuff) = @_; + $section = lc $section; + $item = lc $item; my $key = $item; if (defined $stuff and exists $stuff->{nick}) { - if (exists $self->{registry}->hash->{$section} and exists $self->{registry}->hash->{$section}->{"$item.nick.$stuff->{nick}"}) { - $key = "$item.nick.$stuff->{nick}"; + my $stuff_nick = lc $stuff->{nick}; + if (exists $self->{registry}->{hash}->{$section} and exists $self->{registry}->{hash}->{$section}->{"$item.nick.$stuff_nick"}) { + $key = "$item.nick.$stuff_nick"; } } - if (exists $self->{registry}->hash->{$section} and exists $self->{registry}->hash->{$section}->{$key}) { - if (not $as_text and $self->{registry}->hash->{$section}->{$key}->{type} eq 'array') { - return split /\s*,\s*/, $self->{registry}->hash->{$section}->{$key}->{value}; + if (exists $self->{registry}->{hash}->{$section} and exists $self->{registry}->{hash}->{$section}->{$key}) { + if (not $as_text and $self->{registry}->{hash}->{$section}->{$key}->{type} eq 'array') { + return split /\s*,\s*/, $self->{registry}->{hash}->{$section}->{$key}->{value}; } else { - return $self->{registry}->hash->{$section}->{$key}->{value}; + return $self->{registry}->{hash}->{$section}->{$key}->{value}; } } return undef; @@ -166,20 +180,23 @@ sub get_value { sub get_array_value { my ($self, $section, $item, $index, $stuff) = @_; + $section = lc $section; + $item = lc $item; my $key = $item; if (defined $stuff and exists $stuff->{nick}) { - if (exists $self->{registry}->hash->{$section} and exists $self->{registry}->hash->{$section}->{"$item.nick.$stuff->{nick}"}) { - $key = "$item.nick.$stuff->{nick}"; + my $stuff_nick = lc $stuff->{nick}; + if (exists $self->{registry}->{hash}->{$section} and exists $self->{registry}->{hash}->{$section}->{"$item.nick.$stuff_nick"}) { + $key = "$item.nick.$stuff_nick"; } } - if (exists $self->{registry}->hash->{$section} and exists $self->{registry}->hash->{$section}->{$key}) { - if ($self->{registry}->hash->{$section}->{$key}->{type} eq 'array') { - my @array = split /\s*,\s*/, $self->{registry}->hash->{$section}->{$key}->{value}; + if (exists $self->{registry}->{hash}->{$section} and exists $self->{registry}->{hash}->{$section}->{$key}) { + if ($self->{registry}->{hash}->{$section}->{$key}->{type} eq 'array') { + my @array = split /\s*,\s*/, $self->{registry}->{hash}->{$section}->{$key}->{value}; return $array[$index >= $#array ? $#array : $index]; } else { - return $self->{registry}->hash->{$section}->{$key}->{value}; + return $self->{registry}->{hash}->{$section}->{$key}->{value}; } } return undef; @@ -187,13 +204,14 @@ sub get_array_value { sub add_trigger { my ($self, $section, $item, $subref) = @_; - - $self->{triggers}->{$section}->{$item} = $subref; + $self->{triggers}->{lc $section}->{lc $item} = $subref; } sub process_trigger { my $self = shift; my ($section, $item) = @_; + $section = lc $section; + $item = lc $item; if (exists $self->{triggers}->{$section} and exists $self->{triggers}->{$section}->{$item}) { return &{ $self->{triggers}->{$section}->{$item} }(@_); diff --git a/PBot/RegistryCommands.pm b/PBot/RegistryCommands.pm index 773e2ffa..e410a586 100644 --- a/PBot/RegistryCommands.pm +++ b/PBot/RegistryCommands.pm @@ -87,11 +87,14 @@ sub regunset { return $usage; } - if (not exists $self->{pbot}->{registry}->{registry}->hash->{$section}) { + $section = lc $section; + $item = lc $item; + + if (not exists $self->{pbot}->{registry}->{registry}->{hash}->{$section}) { return "No such registry section $section."; } - if (not exists $self->{pbot}->{registry}->{registry}->hash->{$section}->{$item}) { + if (not exists $self->{pbot}->{registry}->{registry}->{hash}->{$section}->{$item}) { return "No such item $item in section $section."; } @@ -152,7 +155,7 @@ sub regunsetmeta { sub regshow { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $registry = $self->{pbot}->{registry}->{registry}->hash; + my $registry = $self->{pbot}->{registry}->{registry}->{hash}; my $usage = "Usage: regshow
."; @@ -169,6 +172,9 @@ sub regshow { return $usage; } + $section = lc $section; + $item = lc $item; + if (not exists $registry->{$section}) { return "No such registry section $section."; } @@ -193,7 +199,7 @@ sub regshow { sub regfind { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; - my $registry = $self->{pbot}->{registry}->{registry}->hash; + my $registry = $self->{pbot}->{registry}->{registry}->{hash}; my $usage = "Usage: regfind [-showvalues] [-section section] "; @@ -214,14 +220,17 @@ sub regfind { return $usage; } + $section = lc $section; + my ($text, $last_item, $last_section, $i); $last_section = ""; $i = 0; eval { use re::engine::RE2 -strict => 1; foreach my $section_key (sort keys %{ $registry }) { - next if defined $section and $section_key !~ /^$section$/i; + next if defined $section and $section_key ne $section; foreach my $item_key (sort keys %{ $registry->{$section_key} }) { + next if $item_key eq '_name'; if ($registry->{$section_key}->{$item_key}->{private}) { # do not match on value if private next if $item_key !~ /$arguments/i; @@ -289,7 +298,10 @@ sub regchange { return "Usage: regchange
. s///"; } - my $registry = $self->{pbot}->{registry}->{registry}->hash; + $section = lc $section; + $item = lc $item; + + my $registry = $self->{pbot}->{registry}->{registry}->{hash}; if (not exists $registry->{$section}) { return "No such registry section $section."; diff --git a/PBot/Utils/SafeFilename.pm b/PBot/Utils/SafeFilename.pm index 8d0c2218..a85e4153 100644 --- a/PBot/Utils/SafeFilename.pm +++ b/PBot/Utils/SafeFilename.pm @@ -20,7 +20,7 @@ sub safe_filename { } } - return $safe; + return lc $safe; } 1; diff --git a/Plugins/AntiAway.pm b/Plugins/AntiAway.pm index b1394974..a4daf015 100644 --- a/Plugins/AntiAway.pm +++ b/Plugins/AntiAway.pm @@ -46,7 +46,7 @@ sub on_nickchange { my $kick_msg = $self->{pbot}->{registry}->get_value('antiaway', 'kick_msg'); my $channels = $self->{pbot}->{nicklist}->get_channels($newnick); foreach my $chan (@$channels) { - next if not exists $self->{pbot}->{channels}->{channels}->hash->{$chan} or not $self->{pbot}->{channels}->{channels}->hash->{$chan}{chanop}; + next if not exists $self->{pbot}->{channels}->{channels}->{hash}->{$chan} or not $self->{pbot}->{channels}->{channels}->{hash}->{$chan}->{chanop}; $self->{pbot}->{logger}->log("$newnick matches bad away nick regex, kicking from $chan\n"); $self->{pbot}->{chanops}->add_op_command($chan, "kick $chan $newnick $kick_msg"); $self->{pbot}->{chanops}->gain_ops($chan); diff --git a/Plugins/AutoRejoin.pm b/Plugins/AutoRejoin.pm index df4d0faf..bd40bce8 100644 --- a/Plugins/AutoRejoin.pm +++ b/Plugins/AutoRejoin.pm @@ -61,7 +61,7 @@ sub on_kick { my ($nick, $user, $host, $target, $channel, $reason) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->to, $event->{event}->{args}[0], $event->{event}->{args}[1]); return 0 if not $self->{pbot}->{channels}->is_active($channel); - return 0 if $self->{pbot}->{channels}->{channels}->hash->{$channel}->{noautorejoin}; + return 0 if $self->{pbot}->{channels}->{channels}->{hash}->{lc $channel}->{noautorejoin}; if ($target eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { $self->rejoin_channel($channel); @@ -75,7 +75,7 @@ sub on_part { my ($nick, $user, $host, $channel) = ($event->{event}->nick, $event->{event}->user, $event->{event}->host, $event->{event}->to); return 0 if not $self->{pbot}->{channels}->is_active($channel); - return 0 if $self->{pbot}->{channels}->{channels}->hash->{$channel}->{noautorejoin}; + return 0 if $self->{pbot}->{channels}->{channels}->{hash}->{lc $channel}->{noautorejoin}; if ($nick eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { $self->rejoin_channel($channel); diff --git a/Plugins/Spinach.pm b/Plugins/Spinach.pm index 80211344..ada600ce 100644 --- a/Plugins/Spinach.pm +++ b/Plugins/Spinach.pm @@ -206,12 +206,12 @@ sub load_metadata { debug_state => 0, }; - if (not exists $self->{metadata}->hash->{settings}) { - $self->{metadata}->hash->{settings} = $defaults; + if (not exists $self->{metadata}->{hash}->{settings}) { + $self->{metadata}->{hash}->{settings} = $defaults; } else { foreach my $key (keys %$defaults) { - if (not exists $self->{metadata}->hash->{settings}->{$key}) { - $self->{metadata}->hash->{settings}->{$key} = $defaults->{$key}; + if (not exists $self->{metadata}->{hash}->{settings}->{$key}) { + $self->{metadata}->{hash}->{settings}->{$key} = $defaults->{$key}; } } } @@ -748,10 +748,11 @@ sub spinach_cmd { my @truth_count = split /\s/, $self->{state_data}->{current_question}->{answer}; my @lie_count = split /\s/, $arguments; +=cut if (@truth_count > 1 and @lie_count == 1) { return "/msg $nick Your lie cannot be one word for this question. Please try again."; } - +=cut my $found_truth = 0; if (not $self->validate_lie($self->{state_data}->{current_question}->{answer}, $arguments)) { @@ -887,33 +888,33 @@ sub spinach_cmd { return "Bad filter: No categories match. Try again."; } - $self->{metadata}->hash->{filter}->{"category_" . $_ . "_filter"} = $args; + $self->{metadata}->{hash}->{filter}->{"category_" . $_ . "_filter"} = $args; $self->save_metadata; return "Spinach $_ filter set."; } when ('clear') { - delete $self->{metadata}->hash->{filter}; + delete $self->{metadata}->{hash}->{filter}; $self->save_metadata; return "Spinach filter cleared."; } when ('show') { - if (not exists $self->{metadata}->hash->{filter}->{category_include_filter} - and not exists $self->{metadata}->hash->{filter}->{category_exclude_filter}) { + if (not exists $self->{metadata}->{hash}->{filter}->{category_include_filter} + and not exists $self->{metadata}->{hash}->{filter}->{category_exclude_filter}) { return "There is no Spinach filter set."; } my $text = "Spinach "; my $comma = ""; - if (exists $self->{metadata}->hash->{filter}->{category_include_filter}) { - $text .= "include filter set to: " . $self->{metadata}->hash->{filter}->{category_include_filter}; + if (exists $self->{metadata}->{hash}->{filter}->{category_include_filter}) { + $text .= "include filter set to: " . $self->{metadata}->{hash}->{filter}->{category_include_filter}; $comma = "; "; } - if (exists $self->{metadata}->hash->{filter}->{category_exclude_filter}) { - $text .= $comma . "exclude filter set to: " . $self->{metadata}->hash->{filter}->{category_exclude_filter}; + if (exists $self->{metadata}->{hash}->{filter}->{category_exclude_filter}) { + $text .= $comma . "exclude filter set to: " . $self->{metadata}->{hash}->{filter}->{category_exclude_filter}; } return $text; @@ -2427,7 +2428,7 @@ sub getplayers { if ($state->{first_tock}) { $tock = 15; } else { - $tock = 90; + $tock = 300; } if ($state->{ticks} % $tock == 0) {
" . encode_entities($self->{factoids}->hash->{$channel}->{$trigger}->{owner}) . "" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->hash->{$channel}->{$trigger}->{created_on}) . "" . encode_entities($self->{factoids}->{hash}->{$channel}->{$trigger}->{owner}) . "" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->{hash}->{$channel}->{$trigger}->{created_on}) . "" . $self->{factoids}->hash->{$channel}->{$trigger}->{ref_count} . "" . $self->{factoids}->{hash}->{$channel}->{$trigger}->{ref_count} . "" . encode_entities($trigger) . " is $action

with_args: " . encode_entities($with_args) . "
" . encode_entities($trigger_name) . " is $action

with_args: " . encode_entities($with_args) . "
" . encode_entities($trigger) . " is $action" . encode_entities($trigger_name) . " is $action" . $self->{factoids}->hash->{$channel}->{$trigger}->{edited_by} . "" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->hash->{$channel}->{$trigger}->{edited_on}) . "" . $self->{factoids}->{hash}->{$channel}->{$trigger}->{edited_by} . "" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->{hash}->{$channel}->{$trigger}->{edited_on}) . "" . encode_entities($self->{factoids}->hash->{$channel}->{$trigger}->{ref_user}) . "" . encode_entities($self->{factoids}->{hash}->{$channel}->{$trigger}->{ref_user}) . "" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->hash->{$channel}->{$trigger}->{last_referenced_on}) . "" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->{hash}->{$channel}->{$trigger}->{last_referenced_on}) . "