3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-11-26 05:49:27 +01:00

Fix all potential case-sensitivity issues

Misc clean-ups and refactors
This commit is contained in:
Pragmatic Software 2020-01-14 18:10:53 -08:00
parent 30fb36ba0a
commit 02552081d5
23 changed files with 898 additions and 872 deletions

View File

@ -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 <channel> <mask> [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 <channel> <mask> <flag>" 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 <channel> <mask> [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 <channel> <mask>" 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);

View File

@ -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 <namespace> <regex> [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 <namespace> <regex> <flag>" 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 <namespace> <regex>" 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 <namespace> <regex>" 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";

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}
}
}

View File

@ -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";

View File

@ -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);

View File

@ -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 <channel> [key [value]]";
}
return "Usage: chanset <channel> [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 <channel> <key>";
}
return "Usage: chanunset <channel> <key>" if not defined $channel or not defined $key;
return $self->{channels}->unset($channel, $key);
}
@ -73,12 +61,13 @@ sub add {
return "Usage: chanadd <channel>";
}
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;

View File

@ -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;

View File

@ -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] <factoid> [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 <keyword> <module>";
}
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 <keyword>";
} 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.";
}
}

View File

@ -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;

View File

@ -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 "<a href='#" . encode_entities($chan) . "'>" . encode_entities($chan) . "</a><br>\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 "<a name='" . encode_entities($chan) . "'></a>\n";
print FILE "<hr>\n<h3>" . encode_entities($chan) . "</h3>\n<hr>\n";
print FILE "<table border=\"0\" id=\"table$table_id\" class=\"tablesorter\">\n";
@ -185,8 +193,10 @@ sub export_factoids {
print FILE "</tr>\n</thead>\n<tbody>\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 "<tr bgcolor=\"#dddddd\">\n";
@ -194,12 +204,12 @@ sub export_factoids {
print FILE "<tr>\n";
}
print FILE "<td>" . encode_entities($self->{factoids}->hash->{$channel}->{$trigger}->{owner}) . "</td>\n";
print FILE "<td>" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->hash->{$channel}->{$trigger}->{created_on}) . "</td>\n";
print FILE "<td>" . encode_entities($self->{factoids}->{hash}->{$channel}->{$trigger}->{owner}) . "</td>\n";
print FILE "<td>" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->{hash}->{$channel}->{$trigger}->{created_on}) . "</td>\n";
print FILE "<td>" . $self->{factoids}->hash->{$channel}->{$trigger}->{ref_count} . "</td>\n";
print FILE "<td>" . $self->{factoids}->{hash}->{$channel}->{$trigger}->{ref_count} . "</td>\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) . "<a href='http" . encode_entities($2) . "'>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) . "<a href='http" . encode_entities($2) . "'>http" . encode_entities($2) . "<\/a>"/ge;
$with_args =~ s/(.*)<\/a>(.*$)/"$1<\/a>" . encode_entities($2)/e;
print FILE "<td width=100%><b>" . encode_entities($trigger) . "</b> is $action<br><br><b>with_args:</b> " . encode_entities($with_args) . "</td>\n";
print FILE "<td width=100%><b>" . encode_entities($trigger_name) . "</b> is $action<br><br><b>with_args:</b> " . encode_entities($with_args) . "</td>\n";
} else {
print FILE "<td width=100%><b>" . encode_entities($trigger) . "</b> is $action</td>\n";
print FILE "<td width=100%><b>" . encode_entities($trigger_name) . "</b> is $action</td>\n";
}
if (exists $self->{factoids}->hash->{$channel}->{$trigger}->{edited_by}) {
print FILE "<td>" . $self->{factoids}->hash->{$channel}->{$trigger}->{edited_by} . "</td>\n";
print FILE "<td>" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->hash->{$channel}->{$trigger}->{edited_on}) . "</td>\n";
if (exists $self->{factoids}->{hash}->{$channel}->{$trigger}->{edited_by}) {
print FILE "<td>" . $self->{factoids}->{hash}->{$channel}->{$trigger}->{edited_by} . "</td>\n";
print FILE "<td>" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->{hash}->{$channel}->{$trigger}->{edited_on}) . "</td>\n";
} else {
print FILE "<td></td>\n";
print FILE "<td></td>\n";
}
print FILE "<td>" . encode_entities($self->{factoids}->hash->{$channel}->{$trigger}->{ref_user}) . "</td>\n";
print FILE "<td>" . encode_entities($self->{factoids}->{hash}->{$channel}->{$trigger}->{ref_user}) . "</td>\n";
if (exists $self->{factoids}->hash->{$channel}->{$trigger}->{last_referenced_on}) {
print FILE "<td>" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->hash->{$channel}->{$trigger}->{last_referenced_on}) . "</td>\n";
if (exists $self->{factoids}->{hash}->{$channel}->{$trigger}->{last_referenced_on}) {
print FILE "<td>" . encode_entities(strftime "%Y/%m/%d %H:%M:%S", localtime $self->{factoids}->{hash}->{$channel}->{$trigger}->{last_referenced_on}) . "</td>\n";
} else {
print FILE "<td></td>\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 <channel> <keyword>' to choose one): $chans";
return $ref_from . "Ambiguous keyword '$original_keyword' exists in multiple channels (use 'fact <channel> $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;

View File

@ -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;

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 };
}
}

View File

@ -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");
}

View File

@ -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} }(@_);

View File

@ -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 <section>.<item>";
@ -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] <regex>";
@ -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 <section>.<item> s/<pattern>/<replacement>/";
}
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.";

View File

@ -20,7 +20,7 @@ sub safe_filename {
}
}
return $safe;
return lc $safe;
}
1;

View File

@ -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);

View File

@ -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);

View File

@ -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) {