From 6267cc04d2bd8f8ae0c72066e8b9777885d5017d Mon Sep 17 00:00:00 2001 From: Pragmatic Software Date: Mon, 3 Feb 2020 17:19:04 -0800 Subject: [PATCH] Finished replacing admin-levels with user-capabilities [FIN commit 2 of 2]; misc clean-ups --- PBot/AntiFlood.pm | 2 +- PBot/AntiSpam.pm | 2 +- PBot/BanTracker.pm | 11 +- PBot/BlackList.pm | 2 +- PBot/Capabilities.pm | 57 ++++++++- PBot/ChanOps.pm | 6 +- PBot/Channels.pm | 15 ++- PBot/DualIndexHashObject.pm | 2 - PBot/FactoidCommands.pm | 162 ++++++++++-------------- PBot/FactoidModuleLauncher.pm | 14 +-- PBot/Factoids.pm | 25 +--- PBot/FuncCommand.pm | 8 +- PBot/HashObject.pm | 2 - PBot/IRCHandlers.pm | 10 +- PBot/IgnoreListCommands.pm | 19 +-- PBot/Interpreter.pm | 42 ++----- PBot/LagChecker.pm | 1 - PBot/Logger.pm | 29 ++--- PBot/MessageHistory.pm | 15 +-- PBot/MessageHistory_SQLite.pm | 10 +- PBot/NickList.pm | 14 +-- PBot/PBot.pm | 49 +++----- PBot/Plugins.pm | 24 +--- PBot/Registry.pm | 11 +- PBot/RegistryCommands.pm | 42 ++----- PBot/SelectHandler.pm | 9 +- PBot/StdinReader.pm | 2 - PBot/Timer.pm | 2 - PBot/Users.pm | 145 +++++++++++---------- PBot/VERSION.pm | 1 - Plugins/AntiAway.pm | 5 +- Plugins/AntiKickAutoRejoin.pm | 4 - Plugins/AntiNickSpam.pm | 3 - Plugins/AntiRepeat.pm | 8 -- Plugins/AutoRejoin.pm | 7 -- Plugins/Battleship.pm | 2 +- Plugins/Connect4.pm | 2 +- Plugins/Counter.pm | 51 +------- Plugins/Example.pm | 5 +- Plugins/GoogleSearch.pm | 2 - Plugins/MagicCommand.pm | 3 - Plugins/ParseDate.pm | 1 - Plugins/Quotegrabs.pm | 7 +- Plugins/RemindMe.pm | 9 -- Plugins/UrlTitles.pm | 8 +- Plugins/Weather.pm | 2 - data/commands | 230 ++++++++++++++++++---------------- data/factoids | 29 ++--- 48 files changed, 426 insertions(+), 685 deletions(-) diff --git a/PBot/AntiFlood.pm b/PBot/AntiFlood.pm index c69aef52..295a0440 100644 --- a/PBot/AntiFlood.pm +++ b/PBot/AntiFlood.pm @@ -78,7 +78,7 @@ sub initialize { $self->{pbot}->{registry}->add_default('text', 'antiflood', 'debug_checkban', $conf{debug_checkban} // 0); $self->{pbot}->{commands}->register(sub { return $self->unbanme(@_) }, "unbanme", 0); - $self->{pbot}->{commands}->register(sub { return $self->whitelist(@_) }, "whitelist", 10); + $self->{pbot}->{commands}->register(sub { return $self->whitelist(@_) }, "whitelist", 1); $self->{pbot}->{event_dispatcher}->register_handler('irc.whoisaccount', sub { $self->on_whoisaccount(@_) }); $self->{pbot}->{event_dispatcher}->register_handler('irc.whoisuser', sub { $self->on_whoisuser(@_) }); diff --git a/PBot/AntiSpam.pm b/PBot/AntiSpam.pm index 18642a53..befecbe6 100644 --- a/PBot/AntiSpam.pm +++ b/PBot/AntiSpam.pm @@ -41,7 +41,7 @@ sub initialize { $self->{keywords}->load; $self->{pbot}->{registry}->add_default('text', 'antispam', 'enforce', $conf{enforce_antispam} // 1); - $self->{pbot}->{commands}->register(sub { return $self->antispam_cmd(@_) }, "antispam", 10); + $self->{pbot}->{commands}->register(sub { return $self->antispam_cmd(@_) }, "antispam", 1); } sub is_spam { diff --git a/PBot/BanTracker.pm b/PBot/BanTracker.pm index 0a692a62..dd5b6332 100644 --- a/PBot/BanTracker.pm +++ b/PBot/BanTracker.pm @@ -24,12 +24,8 @@ $Data::Dumper::Sortkeys = 1; use Carp (); sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to BanTracker should be key/value pairs, not hash reference"); - } - + Carp::croak("Options to BanTracker 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; @@ -38,14 +34,14 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to BanTracker"); + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to BanTracker"); $self->{banlist} = {}; $self->{pbot}->{registry}->add_default('text', 'bantracker', 'chanserv_ban_timeout', '604800'); $self->{pbot}->{registry}->add_default('text', 'bantracker', 'mute_timeout', '604800'); $self->{pbot}->{registry}->add_default('text', 'bantracker', 'debug', '0'); - $self->{pbot}->{commands}->register(sub { $self->dumpbans(@_) }, "dumpbans", 60); + $self->{pbot}->{commands}->register(sub { $self->dumpbans(@_) }, "dumpbans", 1); $self->{pbot}->{event_dispatcher}->register_handler('irc.endofnames', sub { $self->get_banlist(@_) }); $self->{pbot}->{event_dispatcher}->register_handler('irc.banlist', sub { $self->on_banlist_entry(@_) }); @@ -54,7 +50,6 @@ sub initialize { sub dumpbans { my ($self, $from, $nick, $user, $host, $arguments) = @_; - my $bans = Dumper($self->{banlist}); return $bans; } diff --git a/PBot/BlackList.pm b/PBot/BlackList.pm index 11008750..bcdffb56 100644 --- a/PBot/BlackList.pm +++ b/PBot/BlackList.pm @@ -33,7 +33,7 @@ sub initialize { $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{filename} = $conf{filename}; $self->{blacklist} = {}; - $self->{pbot}->{commands}->register(sub { $self->blacklist(@_) }, "blacklist", 10); + $self->{pbot}->{commands}->register(sub { $self->blacklist(@_) }, "blacklist", 1); $self->load_blacklist; } diff --git a/PBot/Capabilities.pm b/PBot/Capabilities.pm index 56193bdf..7ee9f497 100644 --- a/PBot/Capabilities.pm +++ b/PBot/Capabilities.pm @@ -53,8 +53,9 @@ sub has { my ($self, $cap, $subcap, $depth) = @_; my $cap_data = $self->{caps}->get_data($cap); return 0 if not defined $cap_data; + return 1 if $cap eq $subcap; - $depth //= 2; + $depth //= 10; if (--$depth <= 0) { $self->{pbot}->{logger}->log("Max recursion reached for PBot::Capabilities->has()\n"); return 0; @@ -74,7 +75,7 @@ sub userhas { return 1 if $user->{$cap}; foreach my $key (keys %{$user}) { next if $key eq '_name'; - return 1 if $self->has($key, $cap, 10); + return 1 if $self->has($key, $cap); } return 0; } @@ -109,6 +110,15 @@ sub add { } sub remove { + my ($self, $cap) = @_; + $cap = lc $cap; + foreach my $c (keys %{$self->{caps}->{hash}}) { + next if $c eq '_name'; + foreach my $sub_cap (keys %{$self->{caps}->{hash}->{$c}}) { + delete $self->{caps}->{hash}->{$c}->{$sub_cap} if $sub_cap eq $cap; + } + delete $self->{caps}->{hash}->{$c} if $c eq $cap; + } } sub rebuild_botowner_capabilities { @@ -122,7 +132,7 @@ sub rebuild_botowner_capabilities { sub list { my ($self, $capability) = @_; - $capability = lc $capability; + $capability = lc $capability if defined $capability; return "No such capability $capability." if defined $capability and not exists $self->{caps}->{hash}->{$capability}; my @caps; @@ -135,7 +145,7 @@ sub list { $result = 'Capabilities: '; } else { @caps = sort keys %{$self->{caps}->{hash}->{$capability}}; - return "Capability $capability has no sub-capabilities." if @caps == 1; + return "Capability $capability has no sub-capabilities." if not @caps or @caps == 1; $result = "Sub-capabilities for $capability: "; } @@ -144,7 +154,7 @@ sub list { foreach my $cap (@caps) { next if $cap eq '_name'; my $count = keys(%{$self->{caps}->{hash}->{$cap}}) - 1; - if ($count) { + if ($count > 0) { push @cap_group, "$cap [$count]" if $count; } else { push @cap_standalone, $cap; @@ -166,6 +176,41 @@ sub capcmd { } when ('userhas') { + my ($hostmask, $cap) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 2); + return "Usage: cap userhas [capability]" if not defined $hostmask; + $cap = lc $cap if defined $cap; + + my $u = $self->{pbot}->{users}->find_user('.*', $hostmask); + return "No such user $hostmask." if not defined $u; + + if (defined $cap) { + return "Try again. No such capability $cap." if not $self->exists($cap); + if ($self->userhas($u, $cap)) { + return "Yes. User $u->{name} has capability $cap."; + } else { + return "No. User $u->{name} does not have capability $cap."; + } + } else { + my $result = "User $u->{name} has capabilities: "; + my @groups; + my @single; + foreach my $key (sort keys %{$u}) { + next if $key eq '_name'; + next if not $self->exists($key); + my $count = keys (%{$self->{caps}->{hash}->{$key}}) - 1; + if ($count > 0) { + push @groups, "$key [$count]"; + } else { + push @single, $key; + } + } + if (@groups or @single) { + $result .= join ', ', @groups, @single; + } else { + $result = "User $u->{name} has no capabilities."; + } + return $result; + } } when ('add') { @@ -175,7 +220,7 @@ sub capcmd { } default { - $result = "Usage: cap list [capability] | cap add [sub-capability] | cap remove [sub-capability] | cap userhas "; + $result = "Usage: cap list [capability] | cap add [sub-capability] | cap remove [sub-capability] | cap userhas [capability]"; } } return $result; diff --git a/PBot/ChanOps.pm b/PBot/ChanOps.pm index 725f67e2..8cf50448 100644 --- a/PBot/ChanOps.pm +++ b/PBot/ChanOps.pm @@ -19,10 +19,7 @@ use Time::HiRes qw(gettimeofday); use Time::Duration qw(concise duration); 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); @@ -31,7 +28,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to ChanOps"); $self->{unban_timeout} = PBot::DualIndexHashObject->new( diff --git a/PBot/Channels.pm b/PBot/Channels.pm index 36cbdb1f..ed0c05ef 100644 --- a/PBot/Channels.pm +++ b/PBot/Channels.pm @@ -27,19 +27,18 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{channels} = PBot::HashObject->new(pbot => $self->{pbot}, name => 'Channels', filename => $conf{filename}); $self->load_channels; - $self->{pbot}->{commands}->register(sub { $self->join(@_) }, "join", 40); - $self->{pbot}->{commands}->register(sub { $self->part(@_) }, "part", 40); - $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->join(@_) }, "join", 1); + $self->{pbot}->{commands}->register(sub { $self->part(@_) }, "part", 1); + $self->{pbot}->{commands}->register(sub { $self->set(@_) }, "chanset", 1); + $self->{pbot}->{commands}->register(sub { $self->unset(@_) }, "chanunset", 1); + $self->{pbot}->{commands}->register(sub { $self->add(@_) }, "chanadd", 1); + $self->{pbot}->{commands}->register(sub { $self->remove(@_) }, "chanrem", 1); + $self->{pbot}->{commands}->register(sub { $self->list(@_) }, "chanlist", 1); } sub join { diff --git a/PBot/DualIndexHashObject.pm b/PBot/DualIndexHashObject.pm index ba9c5e0c..399c6344 100644 --- a/PBot/DualIndexHashObject.pm +++ b/PBot/DualIndexHashObject.pm @@ -40,7 +40,6 @@ sub initialize { sub load { my ($self, $filename) = @_; - $filename = $self->{filename} if not defined $filename; if (not defined $filename) { @@ -104,7 +103,6 @@ sub load { sub save { my $self = shift; my $filename; - if (@_) { $filename = shift; } else { $filename = $self->{filename}; } if (not defined $filename) { diff --git a/PBot/FactoidCommands.pm b/PBot/FactoidCommands.pm index b786d2c7..5ea72609 100644 --- a/PBot/FactoidCommands.pm +++ b/PBot/FactoidCommands.pm @@ -25,26 +25,26 @@ use JSON; use PBot::Utils::SafeFilename; -our %factoid_metadata_levels = ( - created_on => 90, - enabled => 10, - last_referenced_in => 90, - last_referenced_on => 90, - modulelauncher_subpattern => 90, - owner => 90, - rate_limit => 10, - ref_count => 90, - ref_user => 90, - type => 90, - edited_by => 90, - edited_on => 90, - locked => 10, - add_nick => 10, - nooverride => 10, - 'effective-level' => 40, - 'persist-key' => 20, - 'interpolate' => 10, - # all others are allowed to be factset by anybody/default to level 0 +our %factoid_metadata_capabilities = ( + created_on => 'botowner', + enabled => 'chanop', + last_referenced_in => 'botowner', + last_referenced_on => 'botowner', + modulelauncher_subpattern => 'botowner', + owner => 'botowner', + rate_limit => 'chanop', + ref_count => 'botowner', + ref_user => 'botowner', + type => 'botowner', + edited_by => 'botowner', + edited_on => 'botowner', + locked => 'chanop', + add_nick => 'chanop', + nooverride => 'chanop', + 'cap-override' => 'botowner', + 'persist-key' => 'admin', + 'interpolate' => 'chanop', + # all others are allowed to be factset by anybody ); sub new { @@ -78,14 +78,14 @@ sub initialize { $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->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->load_module(@_) }, "load", 1); + $self->{pbot}->{commands}->register(sub { return $self->unload_module(@_) }, "unload", 1); $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. - $self->{pbot}->{commands}->register(sub { return $self->add_regex(@_) }, "regex", 999); + $self->{pbot}->{commands}->register(sub { return $self->add_regex(@_) }, "regex", 1); } sub call_factoid { @@ -406,13 +406,12 @@ sub factundo { } my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; - my $admininfo = $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host"); + my $userinfo = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host"); if ($factoids->{$channel}->{$trigger}->{'locked'}) { - return "/say $trigger_name is locked and cannot be reverted." if not defined $admininfo; + return "/say $trigger_name is locked and cannot be reverted." if not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin'); - if (exists $factoids->{$channel}->{$trigger}->{'effective-level'} - and $admininfo->{level} < $factoids->{$channel}->{$trigger}->{'effective-level'}) { - return "/say $trigger_name is locked with an effective-level higher than your level and cannot be reverted."; + if (exists $factoids->{$channel}->{$trigger}->{'cap-override'} and not $self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) { + return "/say $trigger_name is locked with a cap-override and cannot be reverted. Unlock the factoid first."; } } @@ -499,13 +498,12 @@ sub factredo { } my $factoids = $self->{pbot}->{factoids}->{factoids}->{hash}; - my $admininfo = $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host"); + my $userinfo = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host"); if ($factoids->{$channel}->{$trigger}->{'locked'}) { - return "/say $trigger_name is locked and cannot be reverted." if not defined $admininfo; + return "/say $trigger_name is locked and cannot be reverted." if not defined $self->{pbot}->{capabilities}->userhas($userinfo, 'admin'); - if (exists $factoids->{$channel}->{$trigger}->{'effective-level'} - and $admininfo->{level} < $factoids->{$channel}->{$trigger}->{'effective-level'}) { - return "/say $trigger_name is locked with an effective-level higher than your level and cannot be reverted."; + if (exists $factoids->{$channel}->{$trigger}->{'cap-override'} and not $self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) { + return "/say $trigger_name is locked with a cap-override and cannot be reverted. Unlock the factoid first."; } } @@ -559,50 +557,39 @@ sub factset { $channel = '.*' if $channel !~ /^#/; my ($owner_channel, $owner_trigger) = $self->{pbot}->{factoids}->find_factoid($channel, $trigger, exact_channel => 1, exact_trigger => 1); - my $admininfo; + my $userinfo; if (defined $owner_channel) { - $admininfo = $self->{pbot}->{users}->loggedin_admin($owner_channel, "$nick!$user\@$host"); + $userinfo = $self->{pbot}->{users}->loggedin($owner_channel, "$nick!$user\@$host"); } else { - $admininfo = $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host"); - } - - my $level = 0; - my $meta_level = 0; - - if (defined $admininfo) { - $level = $admininfo->{level}; + $userinfo = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host"); } + my $meta_cap; if (defined $key) { - if (defined $factoid_metadata_levels{$key}) { - $meta_level = $factoid_metadata_levels{$key}; + if (defined $factoid_metadata_capabilities{$key}) { + $meta_cap = $factoid_metadata_capabilities{$key}; } - if ($meta_level > 0) { - if ($level == 0) { - return "You must be an admin to set $key"; - } elsif ($level < $meta_level) { - return "You must be at least level $meta_level to set $key"; + if (defined $meta_cap) { + if (not $self->{pbot}->{capabilities}->userhas($userinfo, $meta_cap)) { + return "Your user account must have the $meta_cap capability to set $key."; } } - if (defined $value and !$level and $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'locked'}) { + if (defined $value and !$self->{pbot}->{capabilities}->userhas($userinfo, 'admin') 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) { - if ($value > $level) { - return "You cannot set `effective-level` greater than your level, which is $level."; - } elsif ($value < 0) { - return "You cannot set a negative effective-level."; + if (lc $key eq 'cap-override' and defined $value) { + if (not $self->{pbot}->{capabilities}->exists($value)) { + return "No such capability $value."; } - $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'}) { - return "You cannot unlock this factoid because its effective-level is greater than your level."; + if (lc $key eq 'locked' and exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'cap-override'}) { + if (not $self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) { + return "/say $trigger_name has a cap-override and cannot be unlocked until the override is removed."; } } } @@ -622,7 +609,7 @@ sub factset { $mask = $nick; } - if ((defined $value and $key ne 'action' and $key ne 'action_with_args') and lc $mask ne lc $owner and $level == 0) { + if ((defined $value and $key ne 'action' and $key ne 'action_with_args') and lc $mask ne lc $owner and not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin')) { return "You are not the owner of $trigger_name."; } } @@ -651,43 +638,30 @@ sub factunset { my ($owner_channel, $owner_trigger) = $self->{pbot}->{factoids}->find_factoid($channel, $trigger, exact_channel => 1, exact_trigger => 1); - my $admininfo; - + my $userinfo; if (defined $owner_channel) { - $admininfo = $self->{pbot}->{users}->loggedin_admin($owner_channel, "$nick!$user\@$host"); + $userinfo = $self->{pbot}->{users}->loggedin($owner_channel, "$nick!$user\@$host"); } else { - $admininfo = $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host"); + $userinfo = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host"); } - my $level = 0; - my $meta_level = 0; - - if (defined $admininfo) { - $level = $admininfo->{level}; + my $meta_cap; + if (exists $factoid_metadata_capabilities{$key}) { + $meta_cap = $factoid_metadata_capabilities{$key}; } - if (defined $factoid_metadata_levels{$key}) { - $meta_level = $factoid_metadata_levels{$key}; - } - - if ($meta_level > 0) { - if ($level == 0) { - return "You must be an admin to unset $key"; - } elsif ($level < $meta_level) { - return "You must be at least level $meta_level to unset $key"; + if (defined $meta_cap) { + if (not $self->{pbot}->{capabilities}->userhas($userinfo, $meta_cap)) { + return "Your user account must have the $meta_cap capability to unset $key."; } } - if (exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) { + if (exists $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'cap-override'}) { if (lc $key eq 'locked') { - if ($level >= $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{'effective-level'}) { - $self->{pbot}->{factoids}->{factoids}->unset($channel, $trigger, 'effective-level'); + if ($self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) { + $self->{pbot}->{factoids}->{factoids}->unset($channel, $trigger, 'cap-override'); } 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'}) { - return "You cannot unset the effective-level because it is higher than your level."; + return "You cannot unlock this factoid because it has a cap-override. Remove the override first."; } } } @@ -704,7 +678,7 @@ sub factunset { my ($owner) = $factoid->{'owner'} =~ m/([^!]+)/; - if ($key ne 'action_with_args' and lc $nick ne lc $owner and $level == 0) { + if ($key ne 'action_with_args' and lc $nick ne lc $owner and not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin')) { return "You are not the owner of $trigger_name."; } $oldvalue = $self->{pbot}->{factoids}->{factoids}->{hash}->{$channel}->{$trigger}->{$key}; @@ -717,7 +691,6 @@ sub factunset { if ($result =~ m/unset/) { $self->log_factoid($channel, $trigger, "$nick!$user\@$host", "unset $key (value: $oldvalue)"); } - return $result; } @@ -1623,13 +1596,12 @@ sub factchange { 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}->{users}->loggedin_admin($channel, "$nick!$user\@$host"); + my $userinfo = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host"); if ($factoids_hash->{$channel}->{$trigger}->{'locked'}) { - return "/say $trigger_name is locked and cannot be changed." if not defined $admininfo; + return "/say $trigger_name is locked and cannot be changed." if not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin'); - 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."; + if (exists $factoids_hash->{$channel}->{$trigger}->{'cap-override'} and not $self->{pbot}->{capabilities}->userhas($userinfo, 'botowner')) { + return "/say $trigger_name is locked with a cap-override set and cannot be changed until the override is removed."; } } @@ -1711,7 +1683,7 @@ sub factchange { return $ret if length $ret; } - if (length $action > 8000 and not defined $admininfo) { + if (length $action > 8000 and not $self->{pbot}->{capabilities}->userhas($userinfo, 'admin')) { return "Change $trigger_name failed; result is too long."; } diff --git a/PBot/FactoidModuleLauncher.pm b/PBot/FactoidModuleLauncher.pm index 0fc2deee..647502e2 100644 --- a/PBot/FactoidModuleLauncher.pm +++ b/PBot/FactoidModuleLauncher.pm @@ -25,12 +25,8 @@ use Encode; $SIG{CHLD} = sub { while (waitpid(-1, WNOHANG) > 0) {} }; sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to Commands 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; @@ -38,13 +34,7 @@ sub new { sub initialize { my ($self, %conf) = @_; - - my $pbot = delete $conf{pbot}; - if (not defined $pbot) { - Carp::croak("Missing pbot reference to PBot::FactoidModuleLauncher"); - } - - $self->{pbot} = $pbot; + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); } sub execute_module { diff --git a/PBot/Factoids.pm b/PBot/Factoids.pm index 19cf91ac..5e21b236 100644 --- a/PBot/Factoids.pm +++ b/PBot/Factoids.pm @@ -1009,12 +1009,12 @@ sub handle_action { $self->{pbot}->{logger}->log("[" . (defined $stuff->{from} ? $stuff->{from} : "stdin") . "] ($stuff->{nick}!$stuff->{user}\@$stuff->{host}) $trigger_name aliased to: $command\n"); - if (defined $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'}) { + if (defined $self->{factoids}->{hash}->{$channel}->{$keyword}->{'cap-override'}) { 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'}; + $self->{pbot}->{logger}->log("Capability override set to $self->{factoids}->{hash}->{$channel}->{$keyword}->{'cap-override'}\n"); + $stuff->{'cap-override'} = $self->{factoids}->{hash}->{$channel}->{$keyword}->{'cap-override'}; } 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 cap-override of $self->{factoids}->{hash}->{$channel}->{$keyword}->{'cap-override'} on unlocked factoid\n"); } } @@ -1068,9 +1068,9 @@ sub handle_action { # Don't allow user-custom /msg factoids, unless factoid triggered by admin if ($action =~ m/^\/msg/i) { my $admin = $self->{pbot}->{users}->loggedin_admin($stuff->{from}, "$stuff->{nick}!$stuff->{user}\@$stuff->{host}"); - if (not $admin or $admin->{level} < 60) { + if (not $admin) { $self->{pbot}->{logger}->log("[ABUSE] Bad factoid (contains /msg): $action\n"); - return "You are not powerful enough to use /msg in a factoid."; + return "You must be an admin to use /msg in a factoid."; } } @@ -1084,19 +1084,6 @@ sub handle_action { } else { if ($action =~ m/^\/(?:say|me|msg)/i) { return $action; - } elsif ($action =~ s/^\/kick\s+//) { - if (not exists $self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'}) { - $stuff->{authorized} = 0; - return "/say $stuff->{nick}: $trigger_name doesn't have the effective-level to do that."; - } - my $level = 10; - if ($self->{factoids}->{hash}->{$channel}->{$keyword}->{'effective-level'} >= $level) { - $stuff->{authorized} = 1; - return "/kick " . $action; - } else { - $stuff->{authorized} = 0; - return "/say $stuff->{nick}: My effective-level isn't high enough to do that."; - } } else { return "/say $trigger_name is $action"; } diff --git a/PBot/FuncCommand.pm b/PBot/FuncCommand.pm index 5f71aa19..6705a8dc 100644 --- a/PBot/FuncCommand.pm +++ b/PBot/FuncCommand.pm @@ -25,14 +25,10 @@ use warnings; use strict; use feature 'unicode_strings'; - use Carp (); 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); @@ -41,7 +37,7 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to FactoidCommands"); + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{pbot}->{commands}->register(sub { return $self->do_func(@_) }, 'func', 0); $self->init_funcs; } diff --git a/PBot/HashObject.pm b/PBot/HashObject.pm index 08f010b2..7cf99a31 100644 --- a/PBot/HashObject.pm +++ b/PBot/HashObject.pm @@ -40,7 +40,6 @@ sub initialize { sub load { my $self = shift; my $filename; - if (@_) { $filename = shift; } else { $filename = $self->{filename}; } if (not defined $filename) { @@ -85,7 +84,6 @@ sub load { sub save { my $self = shift; my $filename; - if (@_) { $filename = shift; } else { $filename = $self->{filename}; } if (not defined $filename) { diff --git a/PBot/IRCHandlers.pm b/PBot/IRCHandlers.pm index f12c8635..7d1d60fe 100644 --- a/PBot/IRCHandlers.pm +++ b/PBot/IRCHandlers.pm @@ -20,12 +20,8 @@ use Data::Dumper; $Data::Dumper::Sortkeys = 1; 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; @@ -33,9 +29,7 @@ sub new { sub initialize { my ($self, %conf) = @_; - - $self->{pbot} = delete $conf{pbot}; - Carp::croak("Missing pbot parameter to " . __FILE__) if not defined $self->{pbot}; + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot parameter to " . __FILE__); $self->{pbot}->{event_dispatcher}->register_handler('irc.welcome', sub { $self->on_connect(@_) }); $self->{pbot}->{event_dispatcher}->register_handler('irc.disconnect', sub { $self->on_disconnect(@_) }); diff --git a/PBot/IgnoreListCommands.pm b/PBot/IgnoreListCommands.pm index e19283d5..8aa0bba9 100644 --- a/PBot/IgnoreListCommands.pm +++ b/PBot/IgnoreListCommands.pm @@ -19,12 +19,8 @@ use Time::Duration; use Carp (); sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to IgnoreListCommands 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,16 +28,9 @@ sub new { sub initialize { my ($self, %conf) = @_; - - my $pbot = delete $conf{pbot}; - if (not defined $pbot) { - Carp::croak("Missing pbot reference to IgnoreListCommands"); - } - - $self->{pbot} = $pbot; - - $pbot->{commands}->register(sub { return $self->ignore_user(@_) }, "ignore", 10); - $pbot->{commands}->register(sub { return $self->unignore_user(@_) }, "unignore", 10); + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + $self->{pbot}->{commands}->register(sub { $self->ignore_user(@_) }, "ignore", 1); + $self->{pbot}->{commands}->register(sub { $self->unignore_user(@_) }, "unignore", 1); } sub ignore_user { diff --git a/PBot/Interpreter.pm b/PBot/Interpreter.pm index 353e561d..fb5fd843 100644 --- a/PBot/Interpreter.pm +++ b/PBot/Interpreter.pm @@ -23,10 +23,7 @@ use Carp (); use PBot::Utils::ValidateString; 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); @@ -35,10 +32,8 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->SUPER::initialize(%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->{pbot}->{registry}->add_default('text', 'general', 'compile_blocks', $conf{compile_blocks} // 1); $self->{pbot}->{registry}->add_default('array', 'general', 'compile_blocks_channels', $conf{compile_blocks_channels} // '.*'); @@ -174,8 +169,8 @@ sub process_line { # check if user is ignored (and command isn't `login`) if ($command !~ /^login / && defined $from && $pbot->{ignorelist}->check_ignore($nick, $user, $host, $from)) { my $admin = $pbot->{users}->loggedin_admin($from, "$nick!$user\@$host"); - if (!defined $admin || $admin->{level} < 10) { - # hostmask ignored + if (!defined $admin) { + # user is ignored return 1; } } @@ -791,7 +786,7 @@ sub handle_result { $stuff->{prepend} = $1; } - if ($stuff->{pipe} and not $stuff->{authorized}) { + if ($stuff->{pipe}) { my ($pipe, $pipe_rest) = (delete $stuff->{pipe}, delete $stuff->{pipe_rest}); if (not $stuff->{alldone}) { $stuff->{command} = "$pipe $result $pipe_rest"; @@ -975,27 +970,6 @@ sub output_result { $pbot->{conn}->privmsg($to, $line) if $to ne $botnick; $pbot->{antiflood}->check_flood($to, $botnick, $pbot->{registry}->get_value('irc', 'username'), 'pbot', $line, 0, 0, 0) if $stuff->{checkflood}; } - } elsif ($stuff->{authorized} && $line =~ s/^\/kick\s+//) { - $pbot->{antiflood}->check_flood($stuff->{from}, $botnick, $pbot->{registry}->get_value('irc', 'username'), 'pbot', '/kick ' . $line, 0, 0, 0) if $stuff->{checkflood}; - my ($victim, $reason) = split /\s+/, $line, 2; - - if (not defined $reason) { - if (open my $fh, '<', $self->{pbot}->{registry}->get_value('general', 'module_dir') . '/insults.txt') { - my @insults = <$fh>; - close $fh; - $reason = $insults[rand @insults]; - chomp $reason; - } else { - $reason = 'Bye!'; - } - } - - if ($self->{pbot}->{chanops}->can_gain_ops($stuff->{from})) { - $self->{pbot}->{chanops}->add_op_command($stuff->{from}, "kick $stuff->{from} $victim $reason"); - $self->{pbot}->{chanops}->gain_ops($stuff->{from}); - } else { - $pbot->{conn}->privmsg($stuff->{from}, "$victim: $reason") if defined $stuff->{from} && $stuff->{from} ne $botnick; - } } else { if (defined $stuff->{nickoverride} and ($stuff->{no_nickoverride} == 0 or $stuff->{force_nickoverride} == 1)) { $line = "$stuff->{nickoverride}: $line"; @@ -1087,9 +1061,9 @@ sub process_command_queue { preserve_whitespace => 0 }; - if (exists $command->{level}) { - $self->{pbot}->{logger}->log("Override command effective-level to $command->{level}\n"); - $stuff->{'effective-level'} = $command->{level}; + if (exists $command->{'cap-override'}) { + $self->{pbot}->{logger}->log("[command queue] Override command capability with $command->{'cap-override'}\n"); + $stuff->{'cap-override'} = $command->{'cap-override'}; } my $result = $self->interpret($stuff); diff --git a/PBot/LagChecker.pm b/PBot/LagChecker.pm index bdce8185..6279e4b0 100644 --- a/PBot/LagChecker.pm +++ b/PBot/LagChecker.pm @@ -30,7 +30,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to LagChecker"); $self->{lag_average} = undef; # average of entries in lag history, in seconds diff --git a/PBot/Logger.pm b/PBot/Logger.pm index a25f281c..2eeedab5 100644 --- a/PBot/Logger.pm +++ b/PBot/Logger.pm @@ -14,45 +14,32 @@ use File::Basename; use Carp (); sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to Logger should be key/value pairs, not hash reference"); - } - + Carp::croak("Options to Logger should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH'; my ($class, %conf) = @_; + my $self = bless {}, $class; - my $pbot = $conf{pbot} // Carp::croak "Missing pbot reference to " . __FILE__; - my $logfile = $conf{filename} // Carp::croak "Missing logfile parameter in " . __FILE__; + $self->{pbot} = $conf{pbot} // Carp::croak "Missing pbot reference to " . __FILE__; + $self->{logfile} = $conf{filename} // Carp::croak "Missing logfile parameter in " . __FILE__; + $self->{start} = time; - my $path = dirname $logfile; + my $path = dirname $self->{logfile}; if (not -d $path) { print "Creating new logfile path: $path\n"; mkdir $path or Carp::croak "Couldn't create logfile path: $!\n"; } - open LOGFILE, ">>$logfile" or Carp::croak "Couldn't open logfile $logfile: $!\n"; + open LOGFILE, ">>$self->{logfile}" or Carp::croak "Couldn't open logfile $self->{logfile}: $!\n"; LOGFILE->autoflush(1); - my $self = bless { - logfile => $logfile, - pbot => $pbot, - start => time, - }, $class; - $self->{pbot}->{atexit}->register(sub { $self->rotate_log; return; }); - return $self; } sub log { my ($self, $text) = @_; my $time = localtime; - $text =~ s/(\P{PosixGraph})/my $ch = $1; if ($ch =~ m{[\s]}) { $ch } else { sprintf "\\x%02X", ord $ch }/ge; - - if (openhandle *LOGFILE) { - print LOGFILE "$time :: $text"; - } - + print LOGFILE "$time :: $text" if openhandle *LOGFILE; print "$time :: $text"; } diff --git a/PBot/MessageHistory.pm b/PBot/MessageHistory.pm index 70525b04..f164b018 100644 --- a/PBot/MessageHistory.pm +++ b/PBot/MessageHistory.pm @@ -26,10 +26,7 @@ use Carp (); use PBot::MessageHistory_SQLite; 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); @@ -38,8 +35,8 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - $self->{filename} = delete $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3'; + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + $self->{filename} = $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3'; $self->{database} = PBot::MessageHistory_SQLite->new(pbot => $self->{pbot}, filename => $self->{filename}); $self->{database}->begin(); @@ -55,9 +52,9 @@ sub initialize { $self->{pbot}->{commands}->register(sub { $self->recall_message(@_) }, "recall", 0); $self->{pbot}->{commands}->register(sub { $self->list_also_known_as(@_) }, "aka", 0); - $self->{pbot}->{commands}->register(sub { $self->rebuild_aliases(@_) }, "rebuildaliases", 90); - $self->{pbot}->{commands}->register(sub { $self->aka_link(@_) }, "akalink", 60); - $self->{pbot}->{commands}->register(sub { $self->aka_unlink(@_) }, "akaunlink", 60); + $self->{pbot}->{commands}->register(sub { $self->rebuild_aliases(@_) }, "rebuildaliases", 1); + $self->{pbot}->{commands}->register(sub { $self->aka_link(@_) }, "akalink", 1); + $self->{pbot}->{commands}->register(sub { $self->aka_unlink(@_) }, "akaunlink", 1); $self->{pbot}->{atexit}->register(sub { $self->{database}->end(); return; }); } diff --git a/PBot/MessageHistory_SQLite.pm b/PBot/MessageHistory_SQLite.pm index 644a29ec..b1f267e6 100644 --- a/PBot/MessageHistory_SQLite.pm +++ b/PBot/MessageHistory_SQLite.pm @@ -22,12 +22,8 @@ use Text::Levenshtein qw/fastdistance/; use Time::Duration; 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; @@ -36,8 +32,8 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference in " . __FILE__); - $self->{filename} = delete $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3'; + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference in " . __FILE__); + $self->{filename} = $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/message_history.sqlite3'; $self->{new_entries} = 0; $self->{pbot}->{registry}->add_default('text', 'messagehistory', 'debug_link', 0); diff --git a/PBot/NickList.pm b/PBot/NickList.pm index 8a2c34fb..b930c556 100644 --- a/PBot/NickList.pm +++ b/PBot/NickList.pm @@ -32,10 +32,9 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - $self->{nicklist} = {}; + $self->{nicklist} = {}; $self->{pbot}->{registry}->add_default('text', 'nicklist', 'debug', '0'); $self->{pbot}->{commands}->register(sub { $self->show_nicklist(@_) }, "nicklist", 0); @@ -57,10 +56,7 @@ sub initialize { sub show_nicklist { my ($self, $from, $nick, $user, $host, $arguments) = @_; my $nicklist; - - if (not length $arguments) { - return "Usage: nicklist [nick]"; - } + return "Usage: nicklist [nick]" if not length $arguments; my @args = split / /, $arguments; @@ -77,7 +73,6 @@ sub show_nicklist { } $nicklist = Dumper($self->{nicklist}->{lc $args[0]}->{lc $args[1]}); } - return $nicklist; } @@ -170,7 +165,6 @@ sub delete_meta { or not exists $self->{nicklist}->{$channel}->{$nick}->{$key}) { return undef; } - return delete $self->{nicklist}->{$channel}->{$nick}->{$key}; } @@ -240,7 +234,6 @@ sub is_present_similar { return $self->{nicklist}->{$channel}->{$person}->{nick}; } } - return 0; } @@ -290,7 +283,6 @@ sub on_namreply { $self->set_meta($channel, $stripped_nick, '+h', 1); } } - return 0; } @@ -327,7 +319,6 @@ sub on_quit { $self->remove_nick($channel, $nick); } } - return 0; } @@ -350,7 +341,6 @@ sub on_nickchange { $self->{nicklist}->{$channel}->{lc $newnick} = $meta; } } - return 0; } diff --git a/PBot/PBot.pm b/PBot/PBot.pm index d80dac5d..72d95ca6 100644 --- a/PBot/PBot.pm +++ b/PBot/PBot.pm @@ -49,10 +49,7 @@ use PBot::Utils::ParseDate; use PBot::FuncCommand; sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to PBot should be key/value pairs, not hash reference"); - } - + Carp::croak("Options to PBot should be key/value pairs, not hash reference") if ref($_[1]) eq 'HASH'; my ($class, %conf) = @_; my $self = bless {}, $class; $self->{atexit} = PBot::Registerable->new(%conf); @@ -63,7 +60,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{startup_timestamp} = time; my $data_dir = $conf{data_dir}; @@ -331,7 +327,7 @@ sub listcmd { my ($from, $nick, $user, $host, $arguments) = @_; my $text; - my $usage = "Usage: list "; + my $usage = "Usage: list "; if (not defined $arguments) { return $usage; @@ -353,8 +349,11 @@ sub listcmd { if ($arguments =~ /^commands$/i) { $text = "Registered commands: "; foreach my $command (sort { $a->{name} cmp $b->{name} } @{ $self->{commands}->{handlers} }) { - $text .= "$command->{name} "; - $text .= "($command->{level}) " if $command->{level} > 0; + if ($command->{requires_cap}) { + $text .= "+$command->{name} "; + } else { + $text .= "$command->{name} "; + } } return $text; } @@ -373,31 +372,17 @@ sub listcmd { foreach my $hostmask (sort { return 0 if $a eq '_name' or $b eq '_name'; $self->{users}->{users}->{hash}->{$channel}->{$a}->{name} cmp $self->{users}->{users}->{hash}->{$channel}->{$b}->{name} } keys %{ $self->{users}->{users}->{hash}->{$channel} }) { next if $hostmask eq '_name'; $text .= $sep; + my $has_cap = 0; + foreach my $key (keys %{$self->{users}->{users}->{hash}->{$channel}->{$hostmask}}) { + next if $key eq '_name'; + if ($self->{capabilities}->exists($key)) { + print "has $key?\n"; + $has_cap = 1; + last; + } + } + $text .= '+' if $has_cap; $text .= $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{name}; - $text .= "(" . $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{level} . ")" if $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{level} > 0; - $sep = " "; - } - $sep = "; "; - } - return $text; - } - - if ($arguments =~ /^admins$/i) { - $text = "Admins: "; - my $last_channel = ""; - my $sep = ""; - foreach my $channel (sort keys %{ $self->{users}->{users}->{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 { return 0 if $a eq '_name' or $b eq '_name'; $self->{users}->{users}->{hash}->{$channel}->{$a}->{name} cmp $self->{users}->{users}->{hash}->{$channel}->{$b}->{name} } keys %{ $self->{users}->{users}->{hash}->{$channel} }) { - next if $hostmask eq '_name'; - next if $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{level} <= 0; - $text .= $sep; - $text .= $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{name} . " (" . $self->{users}->{users}->{hash}->{$channel}->{$hostmask}->{level} . ")"; $sep = " "; } $sep = "; "; diff --git a/PBot/Plugins.pm b/PBot/Plugins.pm index b0955f2c..4fe578ae 100644 --- a/PBot/Plugins.pm +++ b/PBot/Plugins.pm @@ -18,12 +18,8 @@ use File::Basename; use Carp (); 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; @@ -31,20 +27,16 @@ 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->{plugins} = {}; - - $self->{pbot}->{commands}->register(sub { $self->load_cmd(@_) }, "plug", 90); - $self->{pbot}->{commands}->register(sub { $self->unload_cmd(@_) }, "unplug", 90); - $self->{pbot}->{commands}->register(sub { $self->reload_cmd(@_) }, "replug", 90); - $self->{pbot}->{commands}->register(sub { $self->list_cmd(@_) }, "pluglist", 0); + $self->{pbot}->{commands}->register(sub { $self->load_cmd(@_) }, "plug", 1); + $self->{pbot}->{commands}->register(sub { $self->unload_cmd(@_) }, "unplug", 1); + $self->{pbot}->{commands}->register(sub { $self->reload_cmd(@_) }, "replug", 1); + $self->{pbot}->{commands}->register(sub { $self->list_cmd(@_) }, "pluglist", 0); } sub autoload { my ($self, %conf) = @_; - return if $self->{pbot}->{registry}->get_value('plugins', 'noautoload'); my $path = $self->{pbot}->{registry}->get_value('general', 'plugin_dir') // 'Plugins'; @@ -71,7 +63,6 @@ sub autoload { $plugin_count++ if $self->load($plugin, %conf) } - $self->{pbot}->{logger}->log("$plugin_count plugin" . ($plugin_count == 1 ? '' : 's') . " loaded.\n"); } @@ -112,7 +103,6 @@ sub load { $self->{pbot}->{logger}->log("Error loading $plugin: $@\n"); return 0; } - return $ret; } @@ -189,7 +179,6 @@ sub unload_cmd { sub list_cmd { my ($self, $from, $nick, $user, $host, $arguments) = @_; - my $result = "Loaded plugins: "; my $count = 0; my $comma = ''; @@ -201,7 +190,6 @@ sub list_cmd { } $result .= 'none' if $count == 0; - return $result; } diff --git a/PBot/Registry.pm b/PBot/Registry.pm index 8b62651c..db965d46 100644 --- a/PBot/Registry.pm +++ b/PBot/Registry.pm @@ -22,10 +22,7 @@ use PBot::DualIndexHashObject; use PBot::RegistryCommands; sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to " . __FILE__ . " should be item/value pairs, not hash reference"); - } - + Carp::croak("Options to " . __FILE__ . " should be item/value pairs, not hash reference") if ref($_[1]) eq 'HASH'; my ($class, %conf) = @_; my $self = bless {}, $class; $self->initialize(%conf); @@ -34,15 +31,11 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); my $filename = $conf{filename} // Carp::croak("Missing filename reference in " . __FILE__); - $self->{registry} = PBot::DualIndexHashObject->new(name => 'Registry', filename => $filename, pbot => $self->{pbot}); $self->{triggers} = {}; - $self->{pbot}->{atexit}->register(sub { $self->save; return; }); - PBot::RegistryCommands->new(pbot => $self->{pbot}); } @@ -107,7 +100,6 @@ sub remove { if (not scalar keys %{ $self->{registry}->{hash}->{$section} }) { delete $self->{registry}->{hash}->{$section}; } - $self->save; } @@ -139,7 +131,6 @@ sub set { } $self->save if !$dont_save && $result =~ m/set to/ && not $is_default; - return $result; } diff --git a/PBot/RegistryCommands.pm b/PBot/RegistryCommands.pm index d2a7f6c5..4c9d0669 100644 --- a/PBot/RegistryCommands.pm +++ b/PBot/RegistryCommands.pm @@ -17,10 +17,7 @@ use feature 'unicode_strings'; use Carp (); 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); @@ -29,23 +26,19 @@ sub new { sub initialize { my ($self, %conf) = @_; - - my $pbot = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - $self->{pbot} = $pbot; - - $pbot->{commands}->register(sub { return $self->regset(@_) }, "regset", 60); - $pbot->{commands}->register(sub { return $self->regunset(@_) }, "regunset", 60); - $pbot->{commands}->register(sub { return $self->regshow(@_) }, "regshow", 0); - $pbot->{commands}->register(sub { return $self->regsetmeta(@_) }, "regsetmeta", 60); - $pbot->{commands}->register(sub { return $self->regunsetmeta(@_) }, "regunsetmeta", 60); - $pbot->{commands}->register(sub { return $self->regchange(@_) }, "regchange", 60); - $pbot->{commands}->register(sub { return $self->regfind(@_) }, "regfind", 0); + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + $self->{pbot}->{commands}->register(sub { $self->regset(@_) }, "regset", 1); + $self->{pbot}->{commands}->register(sub { $self->regunset(@_) }, "regunset", 1); + $self->{pbot}->{commands}->register(sub { $self->regshow(@_) }, "regshow", 0); + $self->{pbot}->{commands}->register(sub { $self->regsetmeta(@_) }, "regsetmeta", 1); + $self->{pbot}->{commands}->register(sub { $self->regunsetmeta(@_) }, "regunsetmeta", 1); + $self->{pbot}->{commands}->register(sub { $self->regchange(@_) }, "regchange", 1); + $self->{pbot}->{commands}->register(sub { $self->regfind(@_) }, "regfind", 0); } sub regset { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $usage = "Usage: regset
. "; # support "
." syntax in addition to "
" @@ -71,7 +64,6 @@ sub regset { sub regunset { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $usage = "Usage: regunset
."; # support "
." syntax in addition to "
" @@ -106,7 +98,6 @@ sub regunset { sub regsetmeta { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $usage = "Usage: regsetmeta
. [key [value]]"; # support "
." syntax in addition to "
" @@ -125,14 +116,12 @@ sub regsetmeta { $key = undef if not length $key; $value = undef if not length $value; - return $self->{pbot}->{registry}->set($section, $item, $key, $value); } sub regunsetmeta { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; - my $usage = "Usage: regunsetmeta
. "; # support "
." syntax in addition to "
" @@ -148,7 +137,6 @@ sub regunsetmeta { if (not defined $section or not defined $item or not defined $key) { return $usage; } - return $self->{pbot}->{registry}->unset($section, $item, $key); } @@ -156,7 +144,6 @@ sub regshow { my $self = shift; my ($from, $nick, $user, $host, $arguments, $stuff) = @_; my $registry = $self->{pbot}->{registry}->{registry}->{hash}; - my $usage = "Usage: regshow
."; # support "
." syntax in addition to "
" @@ -192,7 +179,6 @@ sub regshow { if ($registry->{$section}->{$item}->{type} eq 'array') { $result .= ' [array]'; } - return $result; } @@ -200,15 +186,11 @@ sub regfind { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; my $registry = $self->{pbot}->{registry}->{registry}->{hash}; - my $usage = "Usage: regfind [-showvalues] [-section section] "; - if (not defined $arguments) { - return $usage; - } + return $usage if not defined $arguments; my ($section, $showvalues); - $section = $1 if $arguments =~ s/-section\s+([^\b\s]+)//i; $showvalues = 1 if $arguments =~ s/-showvalues?//i; @@ -216,9 +198,7 @@ sub regfind { $arguments =~ s/\s+$//; $arguments =~ s/\s+/ /g; - if ($arguments eq "") { - return $usage; - } + return $usage if $arguments eq ""; $section = lc $section if defined $section;; diff --git a/PBot/SelectHandler.pm b/PBot/SelectHandler.pm index 221cc8e2..faeda923 100644 --- a/PBot/SelectHandler.pm +++ b/PBot/SelectHandler.pm @@ -13,12 +13,8 @@ use IO::Select; use Carp (); sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to SelectHandler should be key/value pairs, not hash reference"); - } - + Carp::croak("Options to SelectHandler 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; @@ -26,8 +22,7 @@ sub new { sub initialize { my ($self, %conf) = @_; - - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference in SelectHandler"); + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference in SelectHandler"); $self->{select} = IO::Select->new(); $self->{readers} = {}; $self->{buffers} = {}; diff --git a/PBot/StdinReader.pm b/PBot/StdinReader.pm index 7d004d0f..8b88f0f0 100644 --- a/PBot/StdinReader.pm +++ b/PBot/StdinReader.pm @@ -22,7 +22,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference in StdinReader"); # create implicit bot-admin account for bot @@ -61,7 +60,6 @@ sub stdin_reader { $from = $self->{pbot}->{registry}->get_value('irc', 'botnick') . "!stdin\@pbot"; $text = $self->{pbot}->{registry}->get_value('irc', 'botnick') . " $input"; } - return $self->{pbot}->{interpreter}->process_line($from, $self->{pbot}->{registry}->get_value('irc', 'botnick'), "stdin", "pbot", $text); } diff --git a/PBot/Timer.pm b/PBot/Timer.pm index 51c02d75..48046a39 100644 --- a/PBot/Timer.pm +++ b/PBot/Timer.pm @@ -27,8 +27,6 @@ $SIG{ALRM} = sub { $seconds += $min_timeout; alarm $min_timeout; - # print "ALARM! $seconds $min_timeout\n"; - # call timer func subroutines foreach my $func (@timer_funcs) { &$func; } diff --git a/PBot/Users.pm b/PBot/Users.pm index 1014c5ee..f148ba6c 100644 --- a/PBot/Users.pm +++ b/PBot/Users.pm @@ -27,9 +27,9 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - $self->{users} = PBot::DualIndexHashObject->new(name => 'Users', filename => $conf{filename}, pbot => $conf{pbot}); + $self->{users} = PBot::DualIndexHashObject->new(name => 'Users', filename => $conf{filename}, pbot => $conf{pbot}); $self->load; $self->{pbot}->{commands}->register(sub { return $self->logincmd(@_) }, "login", 0); @@ -74,7 +74,7 @@ sub on_join { } if ($u->{autologin}) { - $self->{pbot}->{logger}->log("$nick!$user\@$host autologin to $u->{name} ($u->{level}) for $channel\n"); + $self->{pbot}->{logger}->log("$nick!$user\@$host autologin to $u->{name} for $channel\n"); $u->{loggedin} = 1; } } @@ -82,19 +82,23 @@ sub on_join { } sub add_user { - my ($self, $name, $channel, $hostmask, $level, $password, $dont_save) = @_; + my ($self, $name, $channel, $hostmask, $capabilities, $password, $dont_save) = @_; $channel = '.*' if $channel !~ m/^#/; - $level //= 0; + $capabilities //= 'none'; $password //= $self->{pbot}->random_nick(16); my $data = { name => $name, - level => $level, password => $password }; - $self->{pbot}->{logger}->log("Adding new user (level $level): name: $name hostmask: $hostmask channel: $channel\n"); + foreach my $cap (split /\s*,\s*/, $capabilities) { + next if $cap eq 'none'; + $data->{$cap} = 1; + } + + $self->{pbot}->{logger}->log("Adding new user (caps: $capabilities): name: $name hostmask: $hostmask channel: $channel\n"); $self->{users}->add($channel, $hostmask, $data, $dont_save); return $data; } @@ -123,10 +127,9 @@ sub load { next if $hostmask eq '_name'; $i++; my $name = $self->{users}->{hash}->{$channel}->{$hostmask}->{name}; - my $level = $self->{users}->{hash}->{$channel}->{$hostmask}->{level}; my $password = $self->{users}->{hash}->{$channel}->{$hostmask}->{password}; - if (not defined $name or not defined $level or not defined $password) { + if (not defined $name or not defined $password) { Carp::croak "A user in $filename is missing critical data\n"; } } @@ -150,16 +153,13 @@ sub find_user_account { foreach my $chan (keys %{ $self->{users}->{hash} }) { if ($channel !~ m/^#/ or $channel =~ m/^$chan$/i) { if (not exists $self->{users}->{hash}->{$chan}->{$hostmask}) { - my $last_level = 0; # find hostmask by account name or wildcard foreach my $mask (keys %{ $self->{users}->{hash}->{$chan} }) { next if $mask eq '_name'; if (lc $self->{users}->{hash}->{$chan}->{$mask}->{name} eq $hostmask) { - if ($last_level <= $self->{users}->{hash}->{$chan}->{$mask}->{level}) { - $found_hostmask = $mask; - $found_channel = $chan; - $last_level = $self->{users}->{hash}->{$chan}->{$mask}->{level}; - } + $found_hostmask = $mask; + $found_channel = $chan; + last; } if ($mask =~ /[*?]/) { @@ -168,26 +168,23 @@ sub find_user_account { $mask_quoted =~ s/\\\*/.*?/g; $mask_quoted =~ s/\\\?/./g; if ($hostmask =~ m/^$mask_quoted$/i) { - if ($last_level <= $self->{users}->{hash}->{$chan}->{$mask}->{level}) { - $found_hostmask = $mask; - $found_channel = $chan; - $last_level = $self->{users}->{hash}->{$chan}->{$mask}->{level}; - } + $found_hostmask = $mask; + $found_channel = $chan; + last; } } } } else { $found_channel = $chan; + last; } } } return ($found_channel, $found_hostmask); } -sub find_admin { - my ($self, $channel, $hostmask, $min_level) = @_; - $min_level //= 1; - +sub find_user { + my ($self, $channel, $hostmask) = @_; ($channel, $hostmask) = $self->find_user_account($channel, $hostmask); $channel = '.*' if not defined $channel; @@ -195,7 +192,7 @@ sub find_admin { $hostmask = lc $hostmask; my $result = eval { - my $admin; + my $user; foreach my $channel_regex (keys %{ $self->{users}->{hash} }) { if ($channel !~ m/^#/ or $channel =~ m/^$channel_regex$/i) { foreach my $hostmask_regex (keys %{ $self->{users}->{hash}->{$channel_regex} }) { @@ -206,51 +203,45 @@ sub find_admin { $hostmask_quoted =~ s/\\\*/.*?/g; $hostmask_quoted =~ s/\\\?/./g; if ($hostmask =~ m/^$hostmask_quoted$/i) { - my $temp = $self->{users}->{hash}->{$channel_regex}->{$hostmask_regex}; - $admin = $temp if $temp->{level} >= $min_level and (not defined $admin or $admin->{level} <= $temp->{level}); + return $self->{users}->{hash}->{$channel_regex}->{$hostmask_regex}; } } else { # direct comparison if ($hostmask eq lc $hostmask_regex) { - my $temp = $self->{users}->{hash}->{$channel_regex}->{$hostmask_regex}; - $admin = $temp if $temp->{level} >= $min_level and (not defined $admin or $admin->{level} <= $temp->{level}); + return $self->{users}->{hash}->{$channel_regex}->{$hostmask_regex}; } } } } } - return $admin; + return undef; }; if ($@) { - $self->{pbot}->{logger}->log("Error in find_admin parameters: $@\n"); + $self->{pbot}->{logger}->log("Error in find_user parameters: $@\n"); } - return $result; } -sub find_user { +sub find_admin { my ($self, $from, $hostmask) = @_; - return $self->find_admin($from, $hostmask, 0); + my $user = $self->find_user($from, $hostmask); + return undef if not defined $user; + return undef if not $self->{pbot}->{capabilities}->userhas($user, 'admin'); + return $user; } sub loggedin { my ($self, $channel, $hostmask) = @_; my $user = $self->find_user($channel, $hostmask); - - if (defined $user and $user->{loggedin}) { - return $user; - } + return $user if defined $user and $user->{loggedin}; return undef; } sub loggedin_admin { my ($self, $channel, $hostmask) = @_; my $user = $self->loggedin($channel, $hostmask); - - if (defined $user and $user->{level} > 0) { - return $user; - } + return $user if defined $user and $self->{pbot}->{capabilities}->userhas($user, 'admin'); return undef; } @@ -293,9 +284,7 @@ sub logincmd { my ($self, $from, $nick, $user, $host, $arguments) = @_; my $channel = $from; - if (not $arguments) { - return "Usage: login [channel] password"; - } + return "Usage: login [channel] password" if not $arguments; if ($arguments =~ m/^([^ ]+)\s+(.+)/) { $channel = $1; @@ -303,10 +292,7 @@ sub logincmd { } my ($user_channel, $user_hostmask) = $self->find_user_account($channel, "$nick!$user\@$host"); - - if (not defined $user_channel) { - return "/msg $nick You do not have a user account."; - } + return "/msg $nick You do not have a user account." if not defined $user_channel; my $u = $self->{users}->{hash}->{$user_channel}->{$user_hostmask}; my $channel_text = $user_channel eq '.*' ? '' : " for $user_channel"; @@ -343,29 +329,35 @@ sub logoutcmd { sub useradd { my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_; - my ($name, $channel, $hostmask, $level, $password) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 5); - $level //= 0; + my ($name, $channel, $hostmask, $capabilities, $password) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 5); + $capabilities //= 'none'; if (not defined $name or not defined $channel or not defined $hostmask) { - return "Usage: useradd [level [password]]"; + return "Usage: useradd [capabilities [password]]"; } $channel = '.*' if $channel !~ /^#/; - my $admin = $self->{pbot}->{users}->find_admin($channel, "$nick!$user\@$host"); + my $u = $self->{pbot}->{users}->find_user($channel, "$nick!$user\@$host"); - if (not $admin) { + if (not defined $u) { $channel = 'global' if $channel eq '.*'; - return "You are not an admin for $channel; cannot add users to that channel.\n"; + return "You do not have a user account for $channel; cannot add users to that channel.\n"; } - # don't allow non-bot-owners to add admins that can also add admins - if ($admin->{level} < 90 and $level > 40) { - return "You may not set admin level higher than 40.\n"; + if ($capabilities ne 'none' and not $self->{pbot}->{capabilities}->userhas($u, 'can-modify-capabilities')) { + return "Your user account does not have the can-modify-capabilities capability. You cannot create user accounts with capabilities."; } - $self->{pbot}->{users}->add_user($name, $channel, $hostmask, $level, $password); - return !$level ? "User added." : "Admin added."; + foreach my $cap (split /\s*,\s*/, $capabilities) { + next if $cap eq 'none'; + return "There is no such capability $cap." if not $self->{pbot}->{capabilities}->exists($cap); + if (not $self->{pbot}->{capabilities}->userhas($u, $cap)) { + return "To set the $cap capability your user account must also have it."; + } + } + $self->{pbot}->{users}->add_user($name, $channel, $hostmask, $capabilities, $password); + return "User added."; } sub userdel { @@ -395,12 +387,12 @@ sub userset { return "Usage: userset [channel] [key [value]]"; } - my $admin = $self->find_admin($channel, "$nick!$user\@$host"); + my $u = $self->find_user($channel, "$nick!$user\@$host"); my $target = $self->find_user($channel, $hostmask); - if (not $admin) { + if (not $u) { $channel = 'global' if $channel eq '.*'; - return "You are not an admin for $channel; cannot modify their users."; + return "You do not have a user account for $channel; cannot modify their users."; } if (not $target) { @@ -411,13 +403,14 @@ sub userset { } } - # don't allow non-bot-owners to add admins that can also add admins - if (defined $key and $key eq 'level' and $admin->{level} < 90 and $value > 40) { - return "You may not set user level higher than 40.\n"; + if (defined $value and not $self->{pbot}->{capabilities}->userhas($u, 'can-modify-capabilities')) { + if ($key =~ m/^can-/i or $self->{pbot}->{capabilities}->exists($key)) { + return "The $key metadata requires the can-modify-capabilities capability, which your user account does not have."; + } } - if (defined $key and $target->{level} > $admin->{level}) { - return "You may not modify users higher in level than you."; + if ($self->{pbot}->{capabilities}->exists($key) and not $self->{pbot}->{capabilities}->userhas($u, $key)) { + return "To set the $key capability your user account must also have it."; } my ($found_channel, $found_hostmask) = $self->find_user_account($channel, $hostmask); @@ -440,12 +433,12 @@ sub userunset { return "Usage: userunset [channel] "; } - my $admin = $self->find_admin($channel, "$nick!$user\@$host"); + my $u = $self->find_admin($channel, "$nick!$user\@$host"); my $target = $self->find_user($channel, $hostmask); - if (not $admin) { + if (not $u) { $channel = 'global' if $channel eq '.*'; - return "You are not an admin for $channel; cannot modify their users."; + return "You do not have a user account for $channel; cannot modify their users."; } if (not $target) { @@ -456,8 +449,14 @@ sub userunset { } } - if ($target->{level} > $admin->{level}) { - return "You may not modify users higher in level than you."; + if (defined $key and not $self->{pbot}->{capabilities}->userhas($u, 'can-modify-capabilities')) { + if ($key =~ m/^can-/i or $self->{pbot}->{capabilities}->exists($key)) { + return "The $key metadata requires the can-modify-capabilities capability, which your user account does not have."; + } + } + + if (defined $key and $self->{pbot}->{capabilities}->exists($key) and not $self->{pbot}->{capabilities}->userhas($u, $key)) { + return "To unset the $key capability your user account must also have it."; } my ($found_channel, $found_hostmask) = $self->find_user_account($channel, $hostmask); diff --git a/PBot/VERSION.pm b/PBot/VERSION.pm index 4cbe650d..cf01c049 100644 --- a/PBot/VERSION.pm +++ b/PBot/VERSION.pm @@ -78,7 +78,6 @@ sub version_cmd { if ($self->{last_check}->{version} > BUILD_REVISION) { $result .= "; new version available: $self->{last_check}->{version} $self->{last_check}->{date}!"; } - return $result; } diff --git a/Plugins/AntiAway.pm b/Plugins/AntiAway.pm index a4daf015..b67d6391 100644 --- a/Plugins/AntiAway.pm +++ b/Plugins/AntiAway.pm @@ -26,8 +26,7 @@ sub new { sub initialize { my ($self, %conf) = @_; - - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); + $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{pbot}->{registry}->add_default('text', 'antiaway', 'bad_nicks', $conf{bad_nicks} // '([[:punct:]](afk|brb|bbl|away|sleep|z+|work|gone|study|out|home|busy|off)[[:punct:]]*$|.+\[.*\]$)'); $self->{pbot}->{registry}->add_default('text', 'antiaway', 'bad_actions', $conf{bad_actions} // '^/me (is (away|gone)|.*auto.?away)'); @@ -52,7 +51,6 @@ sub on_nickchange { $self->{pbot}->{chanops}->gain_ops($chan); } } - return 0; } @@ -70,7 +68,6 @@ sub on_action { $self->{pbot}->{chanops}->add_op_command($channel, "kick $channel $nick $kick_msg"); $self->{pbot}->{chanops}->gain_ops($channel); } - return 0; } diff --git a/Plugins/AntiKickAutoRejoin.pm b/Plugins/AntiKickAutoRejoin.pm index 93b3a63a..d443aa4b 100644 --- a/Plugins/AntiKickAutoRejoin.pm +++ b/Plugins/AntiKickAutoRejoin.pm @@ -28,7 +28,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{pbot}->{registry}->add_default('array', 'antikickautorejoin', 'punishment', '300,900,1800,3600,28800'); @@ -36,7 +35,6 @@ sub initialize { $self->{pbot}->{event_dispatcher}->register_handler('irc.kick', sub { $self->on_kick(@_) }); $self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join(@_) }); - $self->{kicks} = {}; } @@ -54,7 +52,6 @@ sub on_kick { } $self->{kicks}->{$channel}->{$target}->{last_kick} = gettimeofday; - return 0; } @@ -82,7 +79,6 @@ sub on_join { $self->{kicks}->{$channel}->{$nick}->{rejoins}++; } } - return 0; } diff --git a/Plugins/AntiNickSpam.pm b/Plugins/AntiNickSpam.pm index 195f1473..9696bf66 100644 --- a/Plugins/AntiNickSpam.pm +++ b/Plugins/AntiNickSpam.pm @@ -30,10 +30,8 @@ sub new { sub initialize { my ($self, %conf) = @_; $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - $self->{pbot}->{event_dispatcher}->register_handler('irc.public', sub { $self->on_public(@_) }); $self->{pbot}->{event_dispatcher}->register_handler('irc.caction', sub { $self->on_action(@_) }); - $self->{nicks} = {}; } @@ -98,7 +96,6 @@ sub clear_old_nicks { last; } } - delete $self->{nicks}->{$channel} if not @{$self->{nicks}->{$channel}}; } diff --git a/Plugins/AntiRepeat.pm b/Plugins/AntiRepeat.pm index ea89957d..3cf0835c 100644 --- a/Plugins/AntiRepeat.pm +++ b/Plugins/AntiRepeat.pm @@ -28,7 +28,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{pbot}->{registry}->add_default('text', 'antiflood', 'antirepeat', $conf{antirepeat} // 1); @@ -40,17 +39,11 @@ sub initialize { $self->{pbot}->{event_dispatcher}->register_handler('irc.caction', sub { $self->on_public(@_) }); $self->{pbot}->{timer}->register(sub { $self->adjust_offenses }, 60 * 60 * 1, 'antirepeat'); - $self->{offenses} = {}; } sub unload { my $self = shift; - # perform plugin clean-up here - # normally we'd unregister the 'irc.public' event handler; however, the - # event dispatcher will do this automatically for us when it sees there - # is no longer an existing sub. - $self->{pbot}->{timer}->unregister('antirepeat'); } @@ -161,7 +154,6 @@ sub on_public { return 0; } } - return 0; } diff --git a/Plugins/AutoRejoin.pm b/Plugins/AutoRejoin.pm index bd40bce8..ad46c3e7 100644 --- a/Plugins/AutoRejoin.pm +++ b/Plugins/AutoRejoin.pm @@ -28,20 +28,17 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{pbot}->{registry}->add_default('array', 'autorejoin', 'rejoin_delay', '900,1800,3600'); $self->{pbot}->{event_dispatcher}->register_handler('irc.kick', sub { $self->on_kick(@_) }); $self->{pbot}->{event_dispatcher}->register_handler('irc.part', sub { $self->on_part(@_) }); - $self->{rejoins} = {}; } sub rejoin_channel { my ($self, $channel) = @_; - $self->{rejoins}->{$channel}->{rejoins} = 0 if not exists $self->{rejoins}->{$channel}; my $delay = $self->{pbot}->{registry}->get_array_value($channel, 'rejoin_delay', $self->{rejoins}->{$channel}->{rejoins}); @@ -51,8 +48,6 @@ sub rejoin_channel { $delay = duration $delay; $self->{pbot}->{logger}->log("Rejoining $channel in $delay.\n"); - - #$self->{rejoins}->{$channel}->{rejoins}++; $self->{rejoins}->{$channel}->{last_rejoin} = gettimeofday; } @@ -66,7 +61,6 @@ sub on_kick { if ($target eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { $self->rejoin_channel($channel); } - return 0; } @@ -80,7 +74,6 @@ sub on_part { if ($nick eq $self->{pbot}->{registry}->get_value('irc', 'botnick')) { $self->rejoin_channel($channel); } - return 0; } diff --git a/Plugins/Battleship.pm b/Plugins/Battleship.pm index 5990214c..04139fc8 100644 --- a/Plugins/Battleship.pm +++ b/Plugins/Battleship.pm @@ -29,7 +29,7 @@ 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->{pbot}->{commands}->register(sub { $self->battleship_cmd(@_) }, 'battleship', 0); diff --git a/Plugins/Connect4.pm b/Plugins/Connect4.pm index f27f7560..af99e3fe 100644 --- a/Plugins/Connect4.pm +++ b/Plugins/Connect4.pm @@ -29,7 +29,7 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot}; + $self->{pbot} = $conf{pbot}; $self->{pbot}->{commands}->register(sub { $self->connect4_cmd(@_) }, 'connect4', 0); diff --git a/Plugins/Counter.pm b/Plugins/Counter.pm index e7d3502c..79b0c00e 100644 --- a/Plugins/Counter.pm +++ b/Plugins/Counter.pm @@ -27,7 +27,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{pbot}->{commands}->register(sub { $self->counteradd(@_) }, 'counteradd', 0); @@ -40,7 +39,6 @@ sub initialize { $self->{pbot}->{event_dispatcher}->register_handler('irc.public', sub { $self->on_public(@_) }); $self->{filename} = $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/counters.sqlite3'; - $self->create_database; } @@ -130,7 +128,6 @@ sub add_counter { $self->{pbot}->{logger}->log("Add counter failed: $@"); return 0; } - return 1; } @@ -155,7 +152,6 @@ sub reset_counter { $self->{pbot}->{logger}->log("Reset counter failed: $@"); return (undef, undef); } - return ($description, $timestamp); } @@ -179,7 +175,6 @@ sub delete_counter { $self->{pbot}->{logger}->log("Delete counter failed: $@"); return 0; } - return 1; } @@ -196,7 +191,6 @@ sub list_counters { if ($@) { $self->{pbot}->{logger}->log("List counters failed: $@"); } - return map { $_->[0] } @$counters; } @@ -216,7 +210,6 @@ sub get_counter { $self->{pbot}->{logger}->log("Get counter failed: $@"); return undef; } - return ($description, $time, $counter, $created_on, $created_by); } @@ -240,7 +233,6 @@ sub add_trigger { $self->{pbot}->{logger}->log("Add trigger failed: $@"); return 0; } - return 1; } @@ -256,7 +248,6 @@ sub delete_trigger { $sth->bind_param(1, lc $channel); $sth->bind_param(2, lc $trigger); $sth->execute(); - return 1; } @@ -273,7 +264,6 @@ sub list_triggers { if ($@) { $self->{pbot}->{logger}->log("List triggers failed: $@"); } - return @$triggers; } @@ -293,17 +283,12 @@ sub get_trigger { $self->{pbot}->{logger}->log("Get trigger failed: $@"); return undef; } - return $target; } sub counteradd { my ($self, $from, $nick, $user, $host, $arguments) = @_; - - if (not $self->dbi_begin) { - return "Internal error."; - } - + return "Internal error." if not $self->dbi_begin; my ($channel, $name, $description); if ($from !~ m/^#/) { @@ -325,18 +310,13 @@ sub counteradd { } else { $result = "Counter '$name' already exists."; } - $self->dbi_end; return $result; } sub counterdel { my ($self, $from, $nick, $user, $host, $arguments) = @_; - - if (not $self->dbi_begin) { - return "Internal error."; - } - + return "Internal error." if not $self->dbi_begin; my ($channel, $name); if ($from !~ m/^#/) { @@ -358,18 +338,13 @@ sub counterdel { } else { $result = "No such counter."; } - $self->dbi_end; return $result; } sub counterreset { my ($self, $from, $nick, $user, $host, $arguments) = @_; - - if (not $self->dbi_begin) { - return "Internal error."; - } - + return "Internal error." if not $self->dbi_begin; my ($channel, $name); if ($from !~ m/^#/) { @@ -400,11 +375,7 @@ sub counterreset { sub countershow { my ($self, $from, $nick, $user, $host, $arguments) = @_; - - if (not $self->dbi_begin) { - return "Internal error."; - } - + return "Internal error." if not $self->dbi_begin; my ($channel, $name); if ($from !~ m/^#/) { @@ -436,11 +407,7 @@ sub countershow { sub counterlist { my ($self, $from, $nick, $user, $host, $arguments) = @_; - - if (not $self->dbi_begin) { - return "Internal error."; - } - + return "Internal error." if not $self->dbi_begin; my $channel; if ($from !~ m/^#/) { @@ -472,11 +439,7 @@ sub counterlist { sub countertrigger { my ($self, $from, $nick, $user, $host, $arguments) = @_; - - if (not $self->dbi_begin) { - return "Internal error."; - } - + return "Internal error." if not $self->dbi_begin; my $command; ($command, $arguments) = split / /, $arguments, 2; @@ -642,9 +605,7 @@ sub on_public { $self->{pbot}->{logger}->log("Skipping bad trigger $trigger->{trigger}: $@"); } } - $self->dbi_end; - return 0; } diff --git a/Plugins/Example.pm b/Plugins/Example.pm index 789d6e7e..eee0c616 100644 --- a/Plugins/Example.pm +++ b/Plugins/Example.pm @@ -21,10 +21,8 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - - $self->{pbot}->{event_dispatcher}->register_handler('irc.public', sub { $self->on_public(@_) }); + $self->{pbot}->{event_dispatcher}->register_handler('irc.public', sub { $self->on_public(@_) }); } sub unload { @@ -45,7 +43,6 @@ sub on_public { } $self->{pbot}->{logger}->log("Example plugin: got message from $nick!$user\@$host: $msg\n"); - return 0; } diff --git a/Plugins/GoogleSearch.pm b/Plugins/GoogleSearch.pm index 1d1a3540..eea87313 100644 --- a/Plugins/GoogleSearch.pm +++ b/Plugins/GoogleSearch.pm @@ -26,7 +26,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{pbot}->{registry}->add_default('text', 'googlesearch', 'api_key', ''); @@ -107,7 +106,6 @@ sub googlesearch { $comma = " -- "; last if --$matches <= 0; } - return $output; } diff --git a/Plugins/MagicCommand.pm b/Plugins/MagicCommand.pm index 436b54bd..e7bd67a7 100644 --- a/Plugins/MagicCommand.pm +++ b/Plugins/MagicCommand.pm @@ -13,7 +13,6 @@ use warnings; use strict; use feature 'unicode_strings'; - use Carp (); sub new { @@ -26,7 +25,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); $self->{pbot}->{commands}->register(sub { return $self->magic(@_)}, "mc", 90); } @@ -39,7 +37,6 @@ sub unload { sub magic { my $self = shift; my ($from, $nick, $user, $host, $arguments) = @_; - # do something magical! return "Did something magical."; } diff --git a/Plugins/ParseDate.pm b/Plugins/ParseDate.pm index 5290d386..221151cb 100644 --- a/Plugins/ParseDate.pm +++ b/Plugins/ParseDate.pm @@ -10,7 +10,6 @@ use warnings; use strict; use feature 'unicode_strings'; - use Carp (); use Time::Duration qw/duration/; diff --git a/Plugins/Quotegrabs.pm b/Plugins/Quotegrabs.pm index dfe7dd42..ad24658b 100644 --- a/Plugins/Quotegrabs.pm +++ b/Plugins/Quotegrabs.pm @@ -26,12 +26,8 @@ use PBot::Utils::ValidateString; use POSIX qw(strftime); sub new { - if (ref($_[1]) eq 'HASH') { - Carp::croak("Options to Quotegrabs should be key/value pairs, not hash reference"); - } - + Carp::croak("Options to Quotegrabs 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; @@ -39,7 +35,6 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference in Quotegrabs"); $self->{filename} = $conf{quotegrabs_file} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/quotegrabs.sqlite3'; diff --git a/Plugins/RemindMe.pm b/Plugins/RemindMe.pm index 980ebb22..58d41894 100644 --- a/Plugins/RemindMe.pm +++ b/Plugins/RemindMe.pm @@ -30,31 +30,23 @@ sub new { sub initialize { my ($self, %conf) = @_; - $self->{pbot} = delete $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__); - $self->{pbot}->{commands}->register(sub { $self->remindme(@_) }, 'remindme', 0); - $self->{filename} = $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/reminders.sqlite3'; - $self->{pbot}->{timer}->register(sub { $self->check_reminders(@_) }, 1, 'RemindMe'); - $self->dbi_begin; $self->create_database; } sub unload { my $self = shift; - $self->dbi_end; - $self->{pbot}->{commands}->unregister('remindme'); $self->{pbot}->{timer}->unregister('RemindMe'); } sub create_database { my $self = shift; - return if not $self->{dbh}; eval { @@ -154,7 +146,6 @@ sub get_reminder { $self->{pbot}->{logger}->log("List reminders failed: $@"); return undef; } - return $reminder; } diff --git a/Plugins/UrlTitles.pm b/Plugins/UrlTitles.pm index c91bf15d..a857d2a5 100644 --- a/Plugins/UrlTitles.pm +++ b/Plugins/UrlTitles.pm @@ -26,8 +26,7 @@ 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->{pbot}->{registry}->add_default('text', 'general', 'show_url_titles', $conf{show_url_titles} // 1); $self->{pbot}->{registry}->add_default('array', 'general', 'show_url_titles_channels', $conf{show_url_titles_channels} // '.*'); @@ -37,9 +36,7 @@ sub initialize { $self->{pbot}->{event_dispatcher}->register_handler('irc.caction', sub { $self->show_url_titles(@_) }); } -sub unload { - my $self = shift; -} +sub unload {} sub show_url_titles { my ($self, $event_type, $event) = @_; @@ -88,7 +85,6 @@ sub show_url_titles { $self->{pbot}->{factoids}->{factoidmodulelauncher}->execute_module($stuff); } } - return 0; } diff --git a/Plugins/Weather.pm b/Plugins/Weather.pm index 03615629..7e83985d 100644 --- a/Plugins/Weather.pm +++ b/Plugins/Weather.pm @@ -71,7 +71,6 @@ sub weathercmd { if (not length $arguments) { return $usage; } - return $self->get_weather($arguments); } @@ -125,7 +124,6 @@ sub get_weather { last; } } - return $result; } diff --git a/data/commands b/data/commands index 1f09def5..59ef414a 100644 --- a/data/commands +++ b/data/commands @@ -2,532 +2,546 @@ "actiontrigger" : { "_name" : "actiontrigger", "help" : "Adds a new actiontrigger to PBot. See https://github.com/pragma-/pbot/blob/master/doc/ActionTrigger.md", - "level" : 10 + "requires_cap" : 1 }, "aka" : { "_name" : "aka", "help" : "Lists known aliases for a given nick. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#aka", - "level" : 0 + "requires_cap" : 0 }, "akalink" : { "_name" : "akalink", "help" : "Manually link a known alias to a nick. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#akalink", - "level" : 60 + "requires_cap" : 1 }, "akaunlink" : { "_name" : "akaunlink", "help" : "Manually unlink a known alias from a nick. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#akaunlink", - "level" : 60 + "requires_cap" : 1 }, "antispam" : { "_name" : "antispam", "help" : "See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#antispam", - "level" : 10 + "requires_cap" : 1 }, "ban" : { "_name" : "ban", "help" : "Bans a user. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#banmute", - "level" : 10 + "requires_cap" : 1 }, "battleship" : { "_name" : "battleship", "help" : "The classic Battleship board game, modified for IRC. See https://github.com/pragma-/pbot/blob/master/doc/Battleship.md", - "level" : 0 + "requires_cap" : 0 }, "blacklist" : { "_name" : "blacklist", "help" : "Blacklists a hostmask from joining a channel. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#blacklist", - "level" : 10 + "requires_cap" : 1 + }, + "cap" : { + "_name" : "cap", + "help" : "", + "requires_cap" : 0 }, "chanadd" : { "_name" : "chanadd", "help" : "Permanently adds a channel to PBot's list of channels to auto-join and manage. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanadd", - "level" : 40 + "requires_cap" : 1 }, "chanlist" : { "_name" : "chanlist", "help" : "Lists all added channels and their metadata keys and values. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanlist", - "level" : 10 + "requires_cap" : 1 }, "chanrem" : { "_name" : "chanrem", "help" : "Removes a channel from PBot's list of channels to auto-join and manage. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanrem", - "level" : 40 + "requires_cap" : 1 }, "chanset" : { "_name" : "chanset", "help" : "Sets a channel's metadata. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanset and https://github.com/pragma-/pbot/blob/master/doc/Admin.md#channel-metadata-list", - "level" : 40 + "requires_cap" : 1 }, "chanunset" : { "_name" : "chanunset", "help" : "Deletes a channel's metadata key. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#chanunset", - "level" : 40 + "requires_cap" : 1 }, "checkban" : { "_name" : "checkban", "help" : "Shows the reason a mask was banned and how long the ban lasts. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#checkban", - "level" : 0 + "requires_cap" : 0 }, "checkmute" : { "_name" : "checkmute", "help" : "Shows the reason a mask was muted and how long the mute lasts. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#checkmute", - "level" : 0 + "requires_cap" : 0 }, "cmdset" : { "_name" : "cmdset", "help" : "Sets or shows command metadata. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#cmdset", - "level" : 90 + "requires_cap" : 1 }, "cmdunset" : { "_name" : "cmdunset", "help" : "Deletes a channel metadata key. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#cmdunset", - "level" : 90 + "requires_cap" : 1 }, "connect4" : { "_name" : "connect4", "help" : "The classic Connect-4 board game. See https://github.com/pragma-/pbot/blob/master/doc/Connect4.md", - "level" : 0 + "requires_cap" : 0 }, "count" : { "_name" : "count", "help" : "Shows how many factoids and what percentage of the database has submitted. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#count", - "level" : 0 + "requires_cap" : 0 }, "counteradd" : { "_name" : "counteradd", "help" : "Adds a new counter. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#add", - "level" : 0 + "requires_cap" : 0 }, "counterdel" : { "_name" : "counterdel", "help" : "Deletes a counter. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#del", - "level" : 0 + "requires_cap" : 0 }, "counterlist" : { "_name" : "counterlist", "help" : "Lists counters. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#list", - "level" : 0 + "requires_cap" : 0 }, "counterreset" : { "_name" : "counterreset", "help" : "Resets a counter. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#reset", - "level" : 0 + "requires_cap" : 0 }, "countershow" : { "_name" : "countershow", "help" : "Shows a counter's data. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#show", - "level" : 0 + "requires_cap" : 0 }, "countertrigger" : { "_name" : "countertrigger", "help" : "Manages counter triggers. See https://github.com/pragma-/pbot/blob/master/doc/Counter.md#trigger", - "level" : 10 + "requires_cap" : 1 }, "date" : { "_name" : "date", "help" : "Shows date and time for a timezone. Accepts Linux timezone locations. You can set `!my timezone ...` to remember your timezone.", - "level" : 0 + "requires_cap" : 0 }, "delq" : { "_name" : "delq", "help" : "Deletes a quote from the quotegrabs database. See https://github.com/pragma-/pbot/blob/master/doc/Quotegrabs.md#delq", - "level" : 0 + "requires_cap" : 0 }, "deop" : { "_name" : "deop", "help" : "Removes OP status from users. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#deop", - "level" : 10 + "requires_cap" : 1 }, "devoice" : { "_name" : "devoice", "help" : "Removes VOICE status from users. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#devoice", - "level" : 10 + "requires_cap" : 1 }, "die" : { "_name" : "die", "help" : "Tells PBot to disconnect and exit. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#die", - "level" : 90 + "requires_cap" : 1 }, "dumpbans" : { "_name" : "dumpbans", "help" : "Displays PBot's internal banlist data structure. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#dumpbans", - "level" : 60 + "requires_cap" : 1 }, "eval" : { "_name" : "eval", "help" : "Evaluates Perl code within PBot's context. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#eval", - "level" : 99 + "requires_cap" : 1 }, "export" : { "_name" : "export", "help" : "Exports specified list to HTML file. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#export", - "level" : 90 + "requires_cap" : 1 }, "fact" : { "_name" : "fact", "help" : "Displays or invokes a factoid belonging to a specific channel. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#fact", - "level" : 0 + "requires_cap" : 0 }, "factadd" : { "_name" : "factadd", "help" : "Creates a new factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factadd", - "level" : 0 + "requires_cap" : 0 }, "factalias" : { "_name" : "factalias", "help" : "Creates a factoid that acts as an alias for a command. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factalias", - "level" : 0 + "requires_cap" : 0 }, "factchange" : { "_name" : "factchange", "help" : "Changes a factoid using a regular expression. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factchange", - "level" : 0 + "requires_cap" : 0 }, "factfind" : { "_name" : "factfind", "help" : "Searches the database for a factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factfind", - "level" : 0 + "requires_cap" : 0 }, "factinfo" : { "_name" : "factinfo", "help" : "Displays information about a factoid, such as who submitted it and when. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factinfo", - "level" : 0 + "requires_cap" : 0 }, "factlog" : { "_name" : "factlog", "help" : "Displays a factoid's changelog history. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factlog", - "level" : 0 + "requires_cap" : 0 }, "factmove" : { "_name" : "factmove", "help" : "Renames a factoid or moves a factoid to a different channel. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factmove", - "level" : 0 + "requires_cap" : 0 }, "factredo" : { "_name" : "factredo", "help" : "Reverts a factoid to a newer revision. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factredo", - "level" : 0 + "requires_cap" : 0 }, "factrem" : { "_name" : "factrem", "help" : "Deletes a factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factrem", - "level" : 0 + "requires_cap" : 0 }, "factset" : { "_name" : "factset", "help" : "Displays or sets factoid metadata, such as owner, rate-limit, etc. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factset and https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factoid-metadata-list", - "level" : 0 + "requires_cap" : 0 }, "factshow" : { "_name" : "factshow", "help" : "Displays a factoid's literal value without invoking the factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factshow", - "level" : 0 + "requires_cap" : 0 }, "factundo" : { "_name" : "factundo", "help" : "Reverts a factoid to an older revision. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factundo", - "level" : 0 + "requires_cap" : 0 }, "factunset" : { "_name" : "factunset", "help" : "Unsets a factoid metadata key. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factunset and https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factoid-metadata-list", - "level" : 0 + "requires_cap" : 0 }, "forget" : { "_name" : "forget", "help" : "Deletes a factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#forget", - "level" : 0 + "requires_cap" : 0 }, "func" : { "_name" : "func", "help" : "Invokes built-in functions. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#func", - "level" : 0 + "requires_cap" : 0 }, "getq" : { "_name" : "getq", "help" : "Retrieves and displays a specific quote from the quotegrabs database. See https://github.com/pragma-/pbot/blob/master/doc/Quotegrabs.md#getq", - "level" : 0 + "requires_cap" : 0 }, "google" : { "_name" : "google", "help" : "Displays Google search results for a query. See https://github.com/pragma-/pbot/blob/master/doc/Modules.md#google", - "level" : 0 + "requires_cap" : 0 }, "grab" : { "_name" : "grab", "help" : "Grabs a message someone says, and adds it to the quotegrabs database. See https://github.com/pragma-/pbot/blob/master/doc/Quotegrabs.md#grab", - "level" : 0 + "requires_cap" : 0 }, "help" : { "_name" : "help", "help" : "Displays the `help` metadata for commands and factoids.", - "level" : "0" + "requires_cap" : 0 }, "histogram" : { "_name" : "histogram", "help" : "Displays a histogram of the top factoid submitters. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#histogram", - "level" : 0 + "requires_cap" : 0 }, "ignore" : { "_name" : "ignore", "help" : "Ignores a user. If you omit [channel] PBot will ignore the user in all channels, including private messages. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#ignore", - "level" : 10 + "requires_cap" : 1 }, "in" : { "_name" : "in", "help" : "Performs a command in a specific channel. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#in", - "level" : 0 + "requires_cap" : 1 }, "invite" : { "_name" : "invite", "help" : "Invites a user to a channel. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#invite", - "level" : 10 + "requires_cap" : 1 }, "join" : { "_name" : "join", "help" : "Temporarily joins a channel without adding it to PBot's list of channels to manage/auto-join. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#join", - "level" : 40 + "requires_cap" : 1 }, "kick" : { "_name" : "kick", "help" : "Removes a user from the channel. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#kick", - "level" : "10" + "requires_cap" : 1 }, "lagcheck" : { "_name" : "lagcheck", "help" : "Displays history of PING times. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#lagcheck", - "level" : 0 + "requires_cap" : 0 }, "learn" : { "_name" : "learn", "help" : "Creates a new factoid. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#factadd", - "level" : 0 + "requires_cap" : 0 }, "list" : { "_name" : "list", - "help" : "Lists various collections, such as channels or admins. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#list", - "level" : 0 + "help" : "Lists various collections, such as channels or admins. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#list" }, "load" : { "_name" : "load", "help" : "This command loads a module as a PBot command. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#load", - "level" : 90 + "requires_cap" : 1 }, "login" : { "_name" : "login", "help" : "Logs into a PBot admin account. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#login", - "level" : 0 + "requires_cap" : 0 }, "logout" : { "_name" : "logout", "help" : "Logs out of a PBot admin account. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#logout", - "level" : 0 + "requires_cap" : 0 + }, + "mod" : { + "_name" : "mod", + "help" : "Provides restricted moderation abilities to voiced users. They can kick/ban/etc only users that are not admins, whitelisted, voiced or opped.", + "requires_cap" : 0 }, "mode" : { "_name" : "mode", "help" : "Sets or unsets channel or user modes. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#mode", - "level" : 40 + "requires_cap" : 1 }, "mute" : { "_name" : "mute", "help" : "Mutes a user. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#banmute", - "level" : 10 + "requires_cap" : 1 }, "my" : { "_name" : "my", "help" : "Lets users view and manipulate their own user metadata. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#my and https://github.com/pragma-/pbot/blob/master/doc/Admin.md#user-metadata-list", - "level" : 0 + "requires_cap" : 0 }, "nicklist" : { "_name" : "nicklist", "help" : "Dumps the internal nicklist structure. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#nicklist", - "level" : 0 + "requires_cap" : 0 }, "op" : { "_name" : "op", "help" : "Gives channel operator status to users. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#op", - "level" : 10 + "requires_cap" : "1" }, "part" : { "_name" : "part", "help" : "Departs a channel, without removing it from PBot's list of channels to manage/auto-join. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#part", - "level" : 40 + "requires_cap" : 1 }, "plug" : { "_name" : "plug", "help" : "Loads a plugin into PBot. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#plug", - "level" : 90 + "requires_cap" : 1 }, "pluglist" : { "_name" : "pluglist", "help" : "Lists all currently loaded plugins. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#pluglist", - "level" : 0 + "requires_cap" : 0 }, "rebuildaliases" : { "_name" : "rebuildaliases", "help" : "Rebuilds the aka link table. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#rebuildaliases", - "level" : 90 + "requires_cap" : 1 }, "recall" : { "_name" : "recall", "help" : "Recalls previous chat history for a channel. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#recall", - "level" : 0 + "requires_cap" : 0 }, "refresh" : { "_name" : "refresh", "help" : "Refreshes and reloads PBot core modules and plugins. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#refresh", - "level" : 90 + "requires_cap" : 0 }, "regchange" : { "_name" : "regchange", "help" : "Changes the value of a registry item using a regular expression. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regchange", - "level" : 60 + "requires_cap" : 1 }, "regex" : { "_name" : "regex", "help" : "Manages regular expression commands. See https://github.com/pragma-/pbot/blob/master/doc/Commands.md#regex", - "level" : 999 + "requires_cap" : 1 }, "regfind" : { "_name" : "regfind", "help" : "Searches the registry for keywords or values. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regfind", - "level" : 0 + "requires_cap" : 0 }, "regset" : { "_name" : "regset", "help" : "Creates a new registry item or updates an existing item. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regset", - "level" : 60 + "requires_cap" : 1 }, "regsetmeta" : { "_name" : "regsetmeta", "help" : "Sets or displays the metadata for a specific registry key. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regsetmeta and https://github.com/pragma-/pbot/blob/master/doc/Registry.md#metadata-list", - "level" : 60 + "requires_cap" : 1 }, "regshow" : { "_name" : "regshow", "help" : "Displays the type and value of a registry item. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regshow", - "level" : 0 + "requires_cap" : 0 }, "regunset" : { "_name" : "regunset", "help" : "Deletes a registry item from a specific section/channel. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regunset", - "level" : 60 + "requires_cap" : 1 }, "regunsetmeta" : { "_name" : "regunsetmeta", "help" : "Deletes a metadata key from a registry item. See https://github.com/pragma-/pbot/blob/master/doc/Registry.md#regsetmeta and https://github.com/pragma-/pbot/blob/master/doc/Registry.md#metadata", - "level" : 60 + "requires_cap" : 1 }, "reload" : { "_name" : "reload", "help" : "Reloads a data or configuration file. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#reload", - "level" : 90 + "requires_cap" : 1 }, "remindme" : { "_name" : "remindme", "help" : "Manages user reminders. See https://github.com/pragma-/pbot/blob/master/doc/Remindme.md", - "level" : 0 + "requires_cap" : 0 }, "replug" : { "_name" : "replug", "help" : "Reloads a plugin into PBot. The plugin is first unloaded and then it is loaded again. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#replug", - "level" : 90 + "requires_cap" : 1 }, "rq" : { "_name" : "rq", "help" : "Retrieves and displays a random quote from the quotegrabs database. See https://github.com/pragma-/pbot/blob/master/doc/Quotegrabs.md#rq", - "level" : 0 + "requires_cap" : 0 }, "sl" : { "_name" : "sl", "help" : "Sends a raw IRC command to the server. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#sl", - "level" : 90 + "requires_cap" : 1 }, "spinach" : { "_name" : "spinach", "help" : "Manages and interacts with the Spinach trivia game. See https://github.com/pragma-/pbot/blob/master/doc/Spinach.md", - "level" : 0 + "requires_cap" : 0 }, "top20" : { "_name" : "top20", "help" : "Displays the top 20 most popular factoids. See https://github.com/pragma-/pbot/blob/master/doc/Factoids.md#top20", - "level" : 0 + "requires_cap" : 0 }, "unban" : { "_name" : "unban", "help" : "Unbans a user. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unbanunmute", - "level" : 10 + "requires_cap" : 1 }, "unbanme" : { "_name" : "unbanme", "help" : "Removes a join-flood ban. See https://github.com/pragma-/pbot/blob/master/doc/Abuse.md#unbanme", - "level" : 0 + "requires_cap" : 0 }, "unignore" : { "_name" : "unignore", "help" : "Unignores a user. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unignore", - "level" : 10 + "requires_cap" : 1 }, "unload" : { "_name" : "unload", "help" : "Unloads a module and removes its associated command. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unload", - "level" : 90 + "requires_cap" : 1 }, "unmute" : { "_name" : "unmute", "help" : "Unmutes a user. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unbanunmute", - "level" : 10 + "requires_cap" : 1 }, "unplug" : { "_name" : "unplug", "help" : "Unloads a plugin from PBot. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#unplug", - "level" : 90 + "requires_cap" : 1 }, "uptime" : { "_name" : "uptime", - "help" : "", - "level" : 0 + "help" : "Displays the date and time this instance of PBot was started and how long it has been running.", + "requires_cap" : 0 }, "useradd" : { "_name" : "useradd", "help" : "Adds a new user to PBot. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#useradd", - "level" : 60 + "requires_cap" : 1 }, "userdel" : { "_name" : "userdel", "help" : "Removes an user from PBot. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#userdel", - "level" : 60 + "requires_cap" : 1 }, "userset" : { "_name" : "userset", "help" : "Sets metadata for an user account. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#userset and https://github.com/pragma-/pbot/blob/master/doc/Admin.md#user-metadata-list", - "level" : 60 + "requires_cap" : 1 }, "userunset" : { "_name" : "userunset", "help" : "Deletes a metadata key from an user account. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#userunset", - "level" : 60 + "requires_cap" : 1 }, "version" : { "_name" : "version", "help" : "", - "level" : 0 + "requires_cap" : 0 }, "voice" : { "_name" : "voice", "help" : "Sets mode +v on users. Accepts wildcards. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#voice", - "level" : 10 + "requires_cap" : 1 }, "weather" : { "_name" : "weather", "help" : "Fetches and displays weather data. You may set `!my location ...` to remember your location.", - "level" : 0 + "requires_cap" : 0 }, "whitelist" : { "_name" : "whitelist", "help" : "Whitelists a hostmask to be exempted from ban evasions or anti-flood enforcement. See https://github.com/pragma-/pbot/blob/master/doc/Admin.md#whitelist", - "level" : 10 + "requires_cap" : 1 + }, + "wttr" : { + "_name" : "wttr", + "help" : "", + "requires_cap" : "0" } } diff --git a/data/factoids b/data/factoids index 9f2b6096..5ef090d3 100644 --- a/data/factoids +++ b/data/factoids @@ -419,7 +419,7 @@ "created_on" : "1519931258.36575", "edited_by" : "iamgarp!~chaos@unaffiliated/pragmatic-chaos", "edited_on" : "1555805847.25419", - "effective-level" : "100", + "cap-override" : "botowner", "enabled" : "1", "last_referenced_in" : "##spinach", "last_referenced_on" : 1578910468.98602, @@ -10231,10 +10231,10 @@ }, "kickme" : { "_name" : "kickme", - "action" : "/kick $nick", - "action_with_args" : "/kick $nick $args", + "action" : "/call kick $nick", + "action_with_args" : "/call kick $nick $args", "created_on" : "1524347954.63458", - "effective-level" : "10", + "cap-override" : "can-kick", "enabled" : "1", "last_referenced_in" : "##c-offtopic", "last_referenced_on" : 1572992043.13307, @@ -14908,7 +14908,7 @@ "_name" : "removeme", "action" : "/call sl remove $channel $nick :Bye!", "created_on" : 1567373781.37961, - "effective-level" : "90", + "cap-override" : "botowner", "enabled" : "1", "last_referenced_in" : "##c-offtopic", "last_referenced_on" : 1567721282.13253, @@ -15257,7 +15257,7 @@ "created_on" : "1254874530", "edited_by" : "pragma-!~chaos@unaffiliated/pragmatic-chaos", "edited_on" : "1504601046.70364", - "effective-level" : "20", + "cap-override" : "can-kick", "enabled" : "1", "last_referenced_in" : "##c-offtopic", "last_referenced_on" : 1579041706.76328, @@ -15270,7 +15270,7 @@ }, "roulette_outcome" : { "_name" : "roulette_outcome", - "action" : "\"/kick $nick *BANG!*\" \"/say $args: *click*\" \"/say $args: *click*\"", + "action" : "\"/call kick $nick *BANG!*\" \"/say $args: *click*\" \"/say $args: *click*\"", "created_on" : "1254874748", "edited_by" : "pragma-!~chaos@unaffiliated/pragmatic-chaos", "edited_on" : "1492056512.90395", @@ -21511,21 +21511,6 @@ "ref_user" : "k!~krok@unaffiliated/krok", "type" : "text" }, - "wang" : { - "_name" : "wang", - "action" : "/msg mnrmnaugh wtf", - "created_on" : "1519278998.50208", - "effective-level" : "60", - "enabled" : "1", - "last_referenced_in" : "#pbot2", - "last_referenced_on" : "1519979002.921", - "locked" : "1", - "owner" : "pragma-!~chaos@unaffiliated/pragmatic-chaos", - "rate_limit" : "15", - "ref_count" : "8", - "ref_user" : "mnrmnaugh!~mnrmnaugh@unaffiliated/mnrmnaugh", - "type" : "text" - }, "warning-labels" : { "_name" : "warning-labels", "action" : "http://www.myconfinedspace.com/2007/03/23/internet-warning-labels/",