mirror of
https://github.com/pragma-/pbot.git
synced 2024-11-22 20:09:43 +01:00
Replaced admin-levels with user-capabilities [WIP commit 1 of 2]
This commit is contained in:
parent
186f0d3d65
commit
866d802850
@ -417,9 +417,9 @@ sub check_flood {
|
|||||||
$self->{whois_pending}->{$nick} = gettimeofday;
|
$self->{whois_pending}->{$nick} = gettimeofday;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if ($mode == $self->{pbot}->{messagehistory}->{MSG_JOIN} && exists $self->{pbot}->{capabilities}->{'extended-join'}) {
|
if ($mode == $self->{pbot}->{messagehistory}->{MSG_JOIN} && exists $self->{pbot}->{irc_capabilities}->{'extended-join'}) {
|
||||||
# don't WHOIS joins if extended-join capability is active
|
# don't WHOIS joins if extended-join capability is active
|
||||||
} elsif (not exists $self->{pbot}->{capabilities}->{'account-notify'}) {
|
} elsif (not exists $self->{pbot}->{irc_capabilities}->{'account-notify'}) {
|
||||||
if (not exists $self->{whois_pending}->{$nick}) {
|
if (not exists $self->{whois_pending}->{$nick}) {
|
||||||
$self->{pbot}->{messagehistory}->{database}->set_current_nickserv_account($account, '');
|
$self->{pbot}->{messagehistory}->{database}->set_current_nickserv_account($account, '');
|
||||||
$self->{pbot}->{conn}->whois($nick);
|
$self->{pbot}->{conn}->whois($nick);
|
||||||
@ -820,7 +820,7 @@ sub check_bans {
|
|||||||
$self->{pbot}->{messagehistory}->{database}->update_channel_data($message_account, $channel, $channel_data);
|
$self->{pbot}->{messagehistory}->{database}->update_channel_data($message_account, $channel, $channel_data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (not exists $self->{pbot}->{capabilities}->{'account-notify'}) {
|
if (not exists $self->{pbot}->{irc_capabilities}->{'account-notify'}) {
|
||||||
# mark this account as needing check-bans when nickserv account is identified
|
# mark this account as needing check-bans when nickserv account is identified
|
||||||
my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($message_account, $channel, 'validated');
|
my $channel_data = $self->{pbot}->{messagehistory}->{database}->get_channel_data($message_account, $channel, 'validated');
|
||||||
if (not $channel_data->{validated} & $self->{NEEDS_CHECKBAN}) {
|
if (not $channel_data->{validated} & $self->{NEEDS_CHECKBAN}) {
|
||||||
|
163
PBot/Capabilities.pm
Normal file
163
PBot/Capabilities.pm
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
|
|
||||||
|
package PBot::Capabilities;
|
||||||
|
|
||||||
|
# purpose: provides interface to set/remove/modify/query user capabilities.
|
||||||
|
#
|
||||||
|
# Examples:
|
||||||
|
#
|
||||||
|
|
||||||
|
use warnings;
|
||||||
|
use strict;
|
||||||
|
|
||||||
|
use feature 'unicode_strings';
|
||||||
|
|
||||||
|
use feature 'switch';
|
||||||
|
no if $] >= 5.018, warnings => "experimental::smartmatch";
|
||||||
|
|
||||||
|
use PBot::HashObject;
|
||||||
|
use Carp ();
|
||||||
|
|
||||||
|
sub new {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub initialize {
|
||||||
|
my ($self, %conf) = @_;
|
||||||
|
$self->{pbot} = $conf{pbot} // Carp::croak("Missing pbot reference to " . __FILE__);
|
||||||
|
my $filename = $conf{filename} // $self->{pbot}->{registry}->get_value('general', 'data_dir') . '/capabilities';
|
||||||
|
$self->{caps} = PBot::HashObject->new(name => 'Capabilities', filename => $filename, pbot => $self->{pbot});
|
||||||
|
$self->{caps}->load;
|
||||||
|
# 'cap' command registered in PBot.pm because $self->{pbot}->{commands} is not yet loaded.
|
||||||
|
}
|
||||||
|
|
||||||
|
sub has {
|
||||||
|
my ($self, $cap, $subcap, $depth) = @_;
|
||||||
|
my $cap_data = $self->{caps}->get_data($cap);
|
||||||
|
return 0 if not defined $cap_data;
|
||||||
|
|
||||||
|
$depth //= 2;
|
||||||
|
if (--$depth <= 0) {
|
||||||
|
$self->{pbot}->{logger}->log("Max recursion reached for PBot::Capabilities->has()\n");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach my $c (keys %{$cap_data}) {
|
||||||
|
next if $c eq '_name';
|
||||||
|
return 1 if $c eq $subcap;
|
||||||
|
return 1 if $self->has($c, $subcap, $depth);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub userhas {
|
||||||
|
my ($self, $user, $cap) = @_;
|
||||||
|
return 0 if not defined $user;
|
||||||
|
return 1 if $user->{$cap};
|
||||||
|
foreach my $key (keys %{$user}) {
|
||||||
|
next if $key eq '_name';
|
||||||
|
return 1 if $self->has($key, $cap, 10);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub exists {
|
||||||
|
my ($self, $cap) = @_;
|
||||||
|
$cap = lc $cap;
|
||||||
|
foreach my $c (keys %{$self->{caps}->{hash}}) {
|
||||||
|
next if $c eq '_name';
|
||||||
|
return 1 if $c eq $cap;
|
||||||
|
foreach my $sub_cap (keys %{$self->{caps}->{hash}->{$c}}) {
|
||||||
|
return 1 if $sub_cap eq $cap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub add {
|
||||||
|
my ($self, $cap, $subcap, $dontsave) = @_;
|
||||||
|
|
||||||
|
if (not defined $subcap) {
|
||||||
|
if (not $self->{caps}->exists($cap)) {
|
||||||
|
$self->{caps}->add($cap, {}, $dontsave);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($self->{caps}->exists($cap)) {
|
||||||
|
$self->{caps}->set($cap, $subcap, 1, $dontsave);
|
||||||
|
} else {
|
||||||
|
$self->{caps}->add($cap, { $subcap => 1 }, $dontsave);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub remove {
|
||||||
|
}
|
||||||
|
|
||||||
|
sub rebuild_botowner_capabilities {
|
||||||
|
my ($self) = @_;
|
||||||
|
$self->{caps}->remove('botowner');
|
||||||
|
foreach my $cap (keys %{$self->{caps}->{hash}}) {
|
||||||
|
next if $cap eq '_name';
|
||||||
|
$self->add('botowner', $cap, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub capcmd {
|
||||||
|
my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_;
|
||||||
|
|
||||||
|
my $command = $self->{pbot}->{interpreter}->shift_arg($stuff->{arglist});
|
||||||
|
my $result;
|
||||||
|
given ($command) {
|
||||||
|
when ('list') {
|
||||||
|
my $cap = $self->{pbot}->{interpreter}->shift_arg($stuff->{arglist});
|
||||||
|
if (defined $cap) {
|
||||||
|
$cap = lc $cap;
|
||||||
|
return "No such capability $cap." if not exists $self->{caps}->{hash}->{$cap};
|
||||||
|
return "Capability $cap has no sub-capabilities." if keys %{$self->{caps}->{hash}->{$cap}} == 1;
|
||||||
|
|
||||||
|
$result = "Sub-capabilities for $cap: ";
|
||||||
|
$result .= join(', ', grep { $_ ne '_name' } sort keys %{$self->{caps}->{hash}->{$cap}});
|
||||||
|
} else {
|
||||||
|
return "No capabilities defined." if keys(%{$self->{caps}->{hash}}) == 0;
|
||||||
|
$result = "Capabilities: ";
|
||||||
|
my @caps;
|
||||||
|
|
||||||
|
# first list all capabilities that have sub-capabilities (i.e. grouped capabilities)
|
||||||
|
foreach my $cap (sort keys %{$self->{caps}->{hash}}) {
|
||||||
|
my $count = keys(%{$self->{caps}->{hash}->{$cap}}) - 1;
|
||||||
|
push @caps, "$cap [$count]" if $count;
|
||||||
|
}
|
||||||
|
|
||||||
|
# then list stand-alone capabilities
|
||||||
|
foreach my $cap (sort keys %{$self->{caps}->{hash}}) {
|
||||||
|
next if keys(%{$self->{caps}->{hash}->{$cap}}) > 1;
|
||||||
|
push @caps, $cap;
|
||||||
|
}
|
||||||
|
|
||||||
|
$result .= join ', ', @caps;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
when ('userhas') {
|
||||||
|
}
|
||||||
|
|
||||||
|
when ('add') {
|
||||||
|
}
|
||||||
|
|
||||||
|
when ('remove') {
|
||||||
|
}
|
||||||
|
|
||||||
|
default {
|
||||||
|
$result = "Usage: cap list [capability] | cap add <capability> [sub-capability] | cap remove <capability> [sub-capability] | cap userhas <user> <capability>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
1;
|
@ -28,22 +28,50 @@ sub new {
|
|||||||
|
|
||||||
sub initialize {
|
sub initialize {
|
||||||
my ($self, %conf) = @_;
|
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->{pbot}->{commands}->register(sub { return $self->ban_user(@_) }, "ban", 10);
|
# register commands
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->unban_user(@_) }, "unban", 10);
|
$self->{pbot}->{commands}->register(sub { return $self->ban_user(@_) }, "ban", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->mute_user(@_) }, "mute", 10);
|
$self->{pbot}->{commands}->register(sub { return $self->unban_user(@_) }, "unban", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->unmute_user(@_) }, "unmute", 10);
|
$self->{pbot}->{commands}->register(sub { return $self->mute_user(@_) }, "mute", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->kick_user(@_) }, "kick", 10);
|
$self->{pbot}->{commands}->register(sub { return $self->unmute_user(@_) }, "unmute", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->checkban(@_) }, "checkban", 0);
|
$self->{pbot}->{commands}->register(sub { return $self->kick_user(@_) }, "kick", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->checkmute(@_) }, "checkmute", 0);
|
$self->{pbot}->{commands}->register(sub { return $self->checkban(@_) }, "checkban", 0);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->op_user(@_) }, "op", 10);
|
$self->{pbot}->{commands}->register(sub { return $self->checkmute(@_) }, "checkmute", 0);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->deop_user(@_) }, "deop", 10);
|
$self->{pbot}->{commands}->register(sub { return $self->op_user(@_) }, "op", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->voice_user(@_) }, "voice", 10);
|
$self->{pbot}->{commands}->register(sub { return $self->deop_user(@_) }, "deop", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->devoice_user(@_) }, "devoice", 10);
|
$self->{pbot}->{commands}->register(sub { return $self->voice_user(@_) }, "voice", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->mode(@_) }, "mode", 40);
|
$self->{pbot}->{commands}->register(sub { return $self->devoice_user(@_) }, "devoice", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->invite(@_) }, "invite", 10);
|
$self->{pbot}->{commands}->register(sub { return $self->mode(@_) }, "mode", 1);
|
||||||
|
$self->{pbot}->{commands}->register(sub { return $self->invite(@_) }, "invite", 1);
|
||||||
|
|
||||||
|
# allow commands to set modes
|
||||||
|
$self->{pbot}->{capabilities}->add('can-ban', 'can-mode-b', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('can-unban', 'can-mode-b', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('can-mute', 'can-mode-q', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('can-unmute', 'can-mode-q', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('can-op', 'can-mode-o', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('can-deop', 'can-mode-o', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('can-voice', 'can-mode-v', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('can-devoice', 'can-mode-v', 1);
|
||||||
|
|
||||||
|
# create can-mode-any capabilities group
|
||||||
|
foreach my $mode ("a" .. "z", "A" .. "Z") {
|
||||||
|
$self->{pbot}->{capabilities}->add('can-mode-any', "can-mode-$mode", 1);
|
||||||
|
}
|
||||||
|
$self->{pbot}->{capabilities}->add('can-mode-any', 'can-mode', 1);
|
||||||
|
|
||||||
|
# create chanop capabilities group
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-ban', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-unban', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-mute', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-unmute', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-kick', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-op', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-deop', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-voice', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-devoice', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanop', 'can-invite', 1);
|
||||||
|
|
||||||
$self->{invites} = {}; # track who invited who in order to direct invite responses to them
|
$self->{invites} = {}; # track who invited who in order to direct invite responses to them
|
||||||
|
|
||||||
@ -127,7 +155,6 @@ sub generic_mode_user {
|
|||||||
if ($channel !~ m/^#/) {
|
if ($channel !~ m/^#/) {
|
||||||
# from message
|
# from message
|
||||||
$channel = $self->{pbot}->{interpreter}->shift_arg($stuff->{arglist});
|
$channel = $self->{pbot}->{interpreter}->shift_arg($stuff->{arglist});
|
||||||
$result = 'Done.';
|
|
||||||
if (not defined $channel) {
|
if (not defined $channel) {
|
||||||
return "Usage from message: $mode_name <channel> [nick]";
|
return "Usage from message: $mode_name <channel> [nick]";
|
||||||
} elsif ($channel !~ m/^#/) {
|
} elsif ($channel !~ m/^#/) {
|
||||||
@ -159,10 +186,11 @@ sub generic_mode_user {
|
|||||||
if ($i >= $max_modes) {
|
if ($i >= $max_modes) {
|
||||||
my $args = "$channel $mode $list";
|
my $args = "$channel $mode $list";
|
||||||
$stuff->{arglist} = $self->{pbot}->{interpreter}->make_args($args);
|
$stuff->{arglist} = $self->{pbot}->{interpreter}->make_args($args);
|
||||||
$self->mode($channel, $nick, $stuff->{user}, $stuff->{host}, $args, $stuff);
|
$result = $self->mode($channel, $nick, $stuff->{user}, $stuff->{host}, $args, $stuff);
|
||||||
$mode = $flag;
|
$mode = $flag;
|
||||||
$list = '';
|
$list = '';
|
||||||
$i = 0;
|
$i = 0;
|
||||||
|
last if $result ne '' and $result ne 'Done.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -170,13 +198,12 @@ sub generic_mode_user {
|
|||||||
if ($i) {
|
if ($i) {
|
||||||
my $args = "$channel $mode $list";
|
my $args = "$channel $mode $list";
|
||||||
$stuff->{arglist} = $self->{pbot}->{interpreter}->make_args($args);
|
$stuff->{arglist} = $self->{pbot}->{interpreter}->make_args($args);
|
||||||
$self->mode($channel, $nick, $stuff->{user}, $stuff->{host}, $args, $stuff);
|
$result = $self->mode($channel, $nick, $stuff->{user}, $stuff->{host}, $args, $stuff);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
sub op_user {
|
sub op_user {
|
||||||
my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_;
|
my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_;
|
||||||
return $self->generic_mode_user('+o', 'op', $from, $nick, $stuff);
|
return $self->generic_mode_user('+o', 'op', $from, $nick, $stuff);
|
||||||
@ -222,6 +249,8 @@ sub mode {
|
|||||||
my ($new_modes, $new_targets) = ("", "");
|
my ($new_modes, $new_targets) = ("", "");
|
||||||
my $max_modes = $self->{pbot}->{ircd}->{MODES} // 1;
|
my $max_modes = $self->{pbot}->{ircd}->{MODES} // 1;
|
||||||
|
|
||||||
|
my $u = $self->{pbot}->{users}->loggedin($channel, "$nick!$user\@$host");
|
||||||
|
|
||||||
while ($modes =~ m/(.)/g) {
|
while ($modes =~ m/(.)/g) {
|
||||||
my $mode = $1;
|
my $mode = $1;
|
||||||
|
|
||||||
@ -231,6 +260,10 @@ sub mode {
|
|||||||
next;
|
next;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (not $self->{pbot}->{capabilities}->userhas($u, "can-mode-$mode")) {
|
||||||
|
return "/msg $nick Your user account does not have the can-mode-$mode capability required to set this mode.";
|
||||||
|
}
|
||||||
|
|
||||||
my $target = $targets[$arg++] // "";
|
my $target = $targets[$arg++] // "";
|
||||||
|
|
||||||
if (($mode eq 'v' or $mode eq 'o') and $target =~ m/\*/) {
|
if (($mode eq 'v' or $mode eq 'o') and $target =~ m/\*/) {
|
||||||
@ -297,6 +330,8 @@ sub mode {
|
|||||||
|
|
||||||
if ($from !~ m/^#/) {
|
if ($from !~ m/^#/) {
|
||||||
return "Done.";
|
return "Done.";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -363,10 +398,6 @@ sub ban_user {
|
|||||||
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
||||||
return "I don't think so." if $target =~ /^\Q$botnick\E!/i;
|
return "I don't think so." if $target =~ /^\Q$botnick\E!/i;
|
||||||
|
|
||||||
if ($self->{pbot}->{commands}->get_meta($stuff->{keyword}, 'level') and not $stuff->{'effective-level'} and not $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host")) {
|
|
||||||
return "You are not an admin for $channel.";
|
|
||||||
}
|
|
||||||
|
|
||||||
my $result = '';
|
my $result = '';
|
||||||
my $sep = '';
|
my $sep = '';
|
||||||
my @targets = split /,/, $target;
|
my @targets = split /,/, $target;
|
||||||
@ -430,10 +461,6 @@ sub unban_user {
|
|||||||
|
|
||||||
return "Usage for /msg: unban <nick/mask> <channel> [false value to use unban queue]" if $channel !~ /^#/;
|
return "Usage for /msg: unban <nick/mask> <channel> [false value to use unban queue]" if $channel !~ /^#/;
|
||||||
|
|
||||||
if ($self->{pbot}->{commands}->get_meta($stuff->{keyword}, 'level') and not $stuff->{'effective-level'} and not $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host")) {
|
|
||||||
return "You are not an admin for $channel.";
|
|
||||||
}
|
|
||||||
|
|
||||||
my @targets = split /,/, $target;
|
my @targets = split /,/, $target;
|
||||||
$immediately = 0 if @targets > 1;
|
$immediately = 0 if @targets > 1;
|
||||||
|
|
||||||
@ -494,10 +521,6 @@ sub mute_user {
|
|||||||
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
my $botnick = $self->{pbot}->{registry}->get_value('irc', 'botnick');
|
||||||
return "I don't think so." if $target =~ /^\Q$botnick\E!/i;
|
return "I don't think so." if $target =~ /^\Q$botnick\E!/i;
|
||||||
|
|
||||||
if ($self->{pbot}->{commands}->get_meta($stuff->{keyword}, 'level') and not $stuff->{'effective-level'} and not $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host")) {
|
|
||||||
return "You are not an admin for $channel.";
|
|
||||||
}
|
|
||||||
|
|
||||||
my $result = '';
|
my $result = '';
|
||||||
my $sep = '';
|
my $sep = '';
|
||||||
my @targets = split /,/, $target;
|
my @targets = split /,/, $target;
|
||||||
@ -561,10 +584,6 @@ sub unmute_user {
|
|||||||
|
|
||||||
return "Usage for /msg: unmute <nick/mask> <channel> [false value to use unban queue]" if $channel !~ /^#/;
|
return "Usage for /msg: unmute <nick/mask> <channel> [false value to use unban queue]" if $channel !~ /^#/;
|
||||||
|
|
||||||
if ($self->{pbot}->{commands}->get_meta($stuff->{keyword}, 'level') and not $stuff->{'effective-level'} and not $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host")) {
|
|
||||||
return "You are not an admin for $channel.";
|
|
||||||
}
|
|
||||||
|
|
||||||
my @targets = split /,/, $target;
|
my @targets = split /,/, $target;
|
||||||
$immediately = 0 if @targets > 1;
|
$immediately = 0 if @targets > 1;
|
||||||
|
|
||||||
@ -615,10 +634,6 @@ sub kick_user {
|
|||||||
$channel = $1;
|
$channel = $1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($self->{pbot}->{commands}->get_meta($stuff->{keyword}, 'level') and not $stuff->{'effective-level'} and not $self->{pbot}->{users}->loggedin_admin($channel, "$nick!$user\@$host")) {
|
|
||||||
return "You are not an admin for $channel.";
|
|
||||||
}
|
|
||||||
|
|
||||||
my @insults;
|
my @insults;
|
||||||
if (not length $reason) {
|
if (not length $reason) {
|
||||||
if (open my $fh, '<', $self->{pbot}->{registry}->get_value('general', 'module_dir') . '/insults.txt') {
|
if (open my $fh, '<', $self->{pbot}->{registry}->get_value('general', 'module_dir') . '/insults.txt') {
|
||||||
|
@ -2,10 +2,8 @@
|
|||||||
#
|
#
|
||||||
# Author: pragma_
|
# Author: pragma_
|
||||||
#
|
#
|
||||||
# Purpose: Derives from Registerable class to provide functionality to
|
# Purpose: Registers commands. Invokes commands with user capability
|
||||||
# register subroutines, along with a command name and admin level.
|
# validation.
|
||||||
# Registered items will then be executed if their command name matches
|
|
||||||
# a name provided via input.
|
|
||||||
|
|
||||||
# This Source Code Form is subject to the terms of the Mozilla Public
|
# This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
# License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
@ -40,32 +38,35 @@ sub initialize {
|
|||||||
$self->{metadata} = PBot::HashObject->new(pbot => $self->{pbot}, name => 'Commands', filename => $conf{filename});
|
$self->{metadata} = PBot::HashObject->new(pbot => $self->{pbot}, name => 'Commands', filename => $conf{filename});
|
||||||
$self->load_metadata;
|
$self->load_metadata;
|
||||||
|
|
||||||
$self->register(sub { $self->cmdset(@_) }, "cmdset", 90);
|
$self->register(sub { $self->cmdset(@_) }, "cmdset", 1);
|
||||||
$self->register(sub { $self->cmdunset(@_) }, "cmdunset", 90);
|
$self->register(sub { $self->cmdunset(@_) }, "cmdunset", 1);
|
||||||
$self->register(sub { $self->help(@_) }, "help", 0);
|
$self->register(sub { $self->help(@_) }, "help", 0);
|
||||||
$self->register(sub { $self->uptime(@_) }, "uptime", 0);
|
$self->register(sub { $self->uptime(@_) }, "uptime", 0);
|
||||||
$self->register(sub { $self->in_channel(@_) }, "in", 10);
|
$self->register(sub { $self->in_channel(@_) }, "in", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
sub register {
|
sub register {
|
||||||
my ($self, $subref, $name, $level) = @_;
|
my ($self, $subref, $name, $requires_cap) = @_;
|
||||||
|
|
||||||
if (not defined $subref or not defined $name or not defined $level) {
|
if (not defined $subref or not defined $name) {
|
||||||
Carp::croak("Missing parameters to Commands::register");
|
Carp::croak("Missing parameters to Commands::register");
|
||||||
}
|
}
|
||||||
|
|
||||||
my $ref = $self->SUPER::register($subref);
|
my $ref = $self->SUPER::register($subref);
|
||||||
$ref->{name} = lc $name;
|
$ref->{name} = lc $name;
|
||||||
$ref->{level} = $level;
|
$ref->{requires_cap} = $requires_cap // 0;
|
||||||
|
|
||||||
if (not $self->{metadata}->exists($name)) {
|
if (not $self->{metadata}->exists($name)) {
|
||||||
$self->{metadata}->add($name, { level => $level, help => '' }, 1);
|
$self->{metadata}->add($name, { requires_cap => $requires_cap, help => '' }, 1);
|
||||||
} else {
|
} else {
|
||||||
if (not defined $self->get_meta($name, 'level')) {
|
if (not defined $self->get_meta($name, 'requires_cap')) {
|
||||||
$self->{metadata}->set($name, 'level', $level, 1);
|
$self->{metadata}->set($name, 'requires_cap', $requires_cap, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# add can-cmd capability
|
||||||
|
$self->{pbot}->{capabilities}->add("can-$name", undef, 1);
|
||||||
|
|
||||||
return $ref;
|
return $ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -96,39 +97,44 @@ sub interpreter {
|
|||||||
$self->{pbot}->{logger}->log(Dumper $stuff);
|
$self->{pbot}->{logger}->log(Dumper $stuff);
|
||||||
}
|
}
|
||||||
|
|
||||||
my $from = exists $stuff->{admin_channel_override} ? $stuff->{admin_channel_override} : $stuff->{from};
|
|
||||||
my ($admin_channel) = $stuff->{arguments} =~ m/\B(#[^ ]+)/; # assume first channel-like argument
|
|
||||||
$admin_channel = $from if not defined $admin_channel;
|
|
||||||
my $admin = $self->{pbot}->{users}->loggedin_admin($admin_channel, "$stuff->{nick}!$stuff->{user}\@$stuff->{host}");
|
|
||||||
my $admin_level = defined $admin ? $admin->{level} : 0;
|
|
||||||
my $keyword = lc $stuff->{keyword};
|
my $keyword = lc $stuff->{keyword};
|
||||||
|
my $from = $stuff->{from};
|
||||||
|
|
||||||
if (exists $stuff->{'effective-level'}) {
|
my ($cmd_channel) = $stuff->{arguments} =~ m/\B(#[^ ]+)/; # assume command is invoked in regards to first channel-like argument
|
||||||
$self->{pbot}->{logger}->log("override level to $stuff->{'effective-level'}\n");
|
$cmd_channel = $from if not defined $cmd_channel; # otherwise command is invoked in regards to the channel the user is in
|
||||||
$admin_level = $stuff->{'effective-level'};
|
my $user = $self->{pbot}->{users}->loggedin($cmd_channel, "$stuff->{nick}!$stuff->{user}\@$stuff->{host}");
|
||||||
|
|
||||||
|
my $cap_override;
|
||||||
|
if (exists $stuff->{'cap-override'}) {
|
||||||
|
$self->{pbot}->{logger}->log("Override cap to $stuff->{'cap-override'}\n");
|
||||||
|
$cap_override = $stuff->{'cap-override'};
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach my $ref (@{ $self->{handlers} }) {
|
foreach my $ref (@{ $self->{handlers} }) {
|
||||||
if ($ref->{name} eq $keyword) {
|
if ($ref->{name} eq $keyword) {
|
||||||
my $cmd_level = $self->get_meta($keyword, 'level') // $ref->{level};
|
my $requires_cap = $self->get_meta($keyword, 'requires_cap') // $ref->{requires_cap};
|
||||||
if ($admin_level >= $cmd_level) {
|
if ($requires_cap) {
|
||||||
$stuff->{no_nickoverride} = 1;
|
if (defined $cap_override) {
|
||||||
my $result = &{ $ref->{subref} }($stuff->{from}, $stuff->{nick}, $stuff->{user}, $stuff->{host}, $stuff->{arguments}, $stuff);
|
if (not $self->{pbot}->{capabilities}->has($cap_override, "can-$keyword")) {
|
||||||
if ($stuff->{referenced}) {
|
return "/msg $stuff->{nick} The $keyword command requires the can-$keyword capability, which cap-override $cap_override does not have.";
|
||||||
return undef if $result =~ m/(?:usage:|no results)/i;
|
}
|
||||||
}
|
|
||||||
return $result;
|
|
||||||
} else {
|
|
||||||
return undef if $stuff->{referenced};
|
|
||||||
if ($admin_level == 0) {
|
|
||||||
return "/msg $stuff->{nick} You must be an admin to use this command.";
|
|
||||||
} else {
|
} else {
|
||||||
return "/msg $stuff->{nick} Your level is too low to use this command.";
|
if (not defined $user) {
|
||||||
|
return "/msg $stuff->{nick} You must be logged into your user account to use $keyword.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (not $self->{pbot}->{capabilities}->userhas($user, "can-$keyword")) {
|
||||||
|
return "/msg $stuff->{nick} The $keyword command requires the can-$keyword capability, which your user account does not have.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$stuff->{no_nickoverride} = 1;
|
||||||
|
my $result = &{ $ref->{subref} }($stuff->{from}, $stuff->{nick}, $stuff->{user}, $stuff->{host}, $stuff->{arguments}, $stuff);
|
||||||
|
return undef if $stuff->{referenced} and $result =~ m/(?:usage:|no results)/i;
|
||||||
|
return $result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return undef;
|
return undef;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -185,12 +191,12 @@ sub help {
|
|||||||
if ($self->exists($keyword)) {
|
if ($self->exists($keyword)) {
|
||||||
if (exists $self->{metadata}->{hash}->{$keyword}) {
|
if (exists $self->{metadata}->{hash}->{$keyword}) {
|
||||||
my $name = $self->{metadata}->{hash}->{$keyword}->{_name};
|
my $name = $self->{metadata}->{hash}->{$keyword}->{_name};
|
||||||
my $level = $self->{metadata}->{hash}->{$keyword}->{level};
|
my $requires_cap = $self->{metadata}->{hash}->{$keyword}->{requires_cap};
|
||||||
my $help = $self->{metadata}->{hash}->{$keyword}->{help};
|
my $help = $self->{metadata}->{hash}->{$keyword}->{help};
|
||||||
my $result = "/say $name: ";
|
my $result = "/say $name: ";
|
||||||
|
|
||||||
if (defined $level and $level > 0) {
|
if ($requires_cap) {
|
||||||
$result .= "[Level $level admin command] ";
|
$result .= "[Requires can-$keyword] ";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (not defined $help or not length $help) {
|
if (not defined $help or not length $help) {
|
||||||
|
@ -345,7 +345,7 @@ sub on_join {
|
|||||||
|
|
||||||
my $msg = 'JOIN';
|
my $msg = 'JOIN';
|
||||||
|
|
||||||
if (exists $self->{pbot}->{capabilities}->{'extended-join'}) {
|
if (exists $self->{pbot}->{irc_capabilities}->{'extended-join'}) {
|
||||||
$msg .= " $event->{event}->{args}[0] :$event->{event}->{args}[1]";
|
$msg .= " $event->{event}->{args}[0] :$event->{event}->{args}[1]";
|
||||||
|
|
||||||
$self->{pbot}->{messagehistory}->{database}->update_gecos($message_account, $event->{event}->{args}[1], scalar gettimeofday);
|
$self->{pbot}->{messagehistory}->{database}->update_gecos($message_account, $event->{event}->{args}[1], scalar gettimeofday);
|
||||||
@ -480,7 +480,7 @@ sub on_cap {
|
|||||||
|
|
||||||
my @caps = split /\s+/, $event->{event}->{args}->[1];
|
my @caps = split /\s+/, $event->{event}->{args}->[1];
|
||||||
foreach my $cap (@caps) {
|
foreach my $cap (@caps) {
|
||||||
$self->{pbot}->{capabilities}->{$cap} = 1;
|
$self->{pbot}->{irc_capabilities}->{$cap} = 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$self->{pbot}->{logger}->log(Dumper $event->{event});
|
$self->{pbot}->{logger}->log(Dumper $event->{event});
|
||||||
|
22
PBot/PBot.pm
22
PBot/PBot.pm
@ -21,6 +21,7 @@ use Carp ();
|
|||||||
use PBot::Logger;
|
use PBot::Logger;
|
||||||
use PBot::VERSION;
|
use PBot::VERSION;
|
||||||
use PBot::Registry;
|
use PBot::Registry;
|
||||||
|
use PBot::Capabilities;
|
||||||
use PBot::SelectHandler;
|
use PBot::SelectHandler;
|
||||||
use PBot::StdinReader;
|
use PBot::StdinReader;
|
||||||
use PBot::IRC;
|
use PBot::IRC;
|
||||||
@ -99,16 +100,22 @@ sub initialize {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# then capabilities so commands can add new capabilities
|
||||||
|
$self->{capabilities} = PBot::Capabilities->new(pbot => $self, filename => "$data_dir/capabilities", %conf);
|
||||||
|
|
||||||
# then commands so the modules can register new commands
|
# then commands so the modules can register new commands
|
||||||
$self->{commands} = PBot::Commands->new(pbot => $self, filename => "$data_dir/commands", %conf);
|
$self->{commands} = PBot::Commands->new(pbot => $self, filename => "$data_dir/commands", %conf);
|
||||||
|
|
||||||
# add some commands
|
# add some commands
|
||||||
$self->{commands}->register(sub { $self->listcmd(@_) }, "list", 0);
|
$self->{commands}->register(sub { $self->listcmd(@_) }, "list");
|
||||||
$self->{commands}->register(sub { $self->ack_die(@_) }, "die", 90);
|
$self->{commands}->register(sub { $self->ack_die(@_) }, "die", 1);
|
||||||
$self->{commands}->register(sub { $self->export(@_) }, "export", 90);
|
$self->{commands}->register(sub { $self->export(@_) }, "export", 1);
|
||||||
$self->{commands}->register(sub { $self->reload(@_) }, "reload", 90);
|
$self->{commands}->register(sub { $self->reload(@_) }, "reload", 1);
|
||||||
$self->{commands}->register(sub { $self->evalcmd(@_) }, "eval", 99);
|
$self->{commands}->register(sub { $self->evalcmd(@_) }, "eval", 1);
|
||||||
$self->{commands}->register(sub { $self->sl(@_) }, "sl", 90);
|
$self->{commands}->register(sub { $self->sl(@_) }, "sl", 1);
|
||||||
|
|
||||||
|
# add 'cap' capability command
|
||||||
|
$self->{commands}->register(sub { $self->{capabilities}->capcmd(@_) }, "cap");
|
||||||
|
|
||||||
# prepare the version
|
# prepare the version
|
||||||
$self->{version} = PBot::VERSION->new(pbot => $self, %conf);
|
$self->{version} = PBot::VERSION->new(pbot => $self, %conf);
|
||||||
@ -225,6 +232,9 @@ sub initialize {
|
|||||||
|
|
||||||
# start timer
|
# start timer
|
||||||
$self->{timer}->start();
|
$self->{timer}->start();
|
||||||
|
|
||||||
|
# give botowner all capabilities
|
||||||
|
$self->{capabilities}->rebuild_botowner_capabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
sub random_nick {
|
sub random_nick {
|
||||||
|
@ -32,13 +32,13 @@ sub initialize {
|
|||||||
$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->load;
|
||||||
|
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->logincmd(@_) }, "login", 0);
|
$self->{pbot}->{commands}->register(sub { return $self->logincmd(@_) }, "login", 0);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->logoutcmd(@_) }, "logout", 0);
|
$self->{pbot}->{commands}->register(sub { return $self->logoutcmd(@_) }, "logout", 0);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->useradd(@_) }, "useradd", 60);
|
$self->{pbot}->{commands}->register(sub { return $self->useradd(@_) }, "useradd", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->userdel(@_) }, "userdel", 60);
|
$self->{pbot}->{commands}->register(sub { return $self->userdel(@_) }, "userdel", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->userset(@_) }, "userset", 60);
|
$self->{pbot}->{commands}->register(sub { return $self->userset(@_) }, "userset", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->userunset(@_) }, "userunset", 60);
|
$self->{pbot}->{commands}->register(sub { return $self->userunset(@_) }, "userunset", 1);
|
||||||
$self->{pbot}->{commands}->register(sub { return $self->mycmd(@_) }, "my", 0);
|
$self->{pbot}->{commands}->register(sub { return $self->mycmd(@_) }, "my", 0);
|
||||||
|
|
||||||
$self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join(@_) });
|
$self->{pbot}->{event_dispatcher}->register_handler('irc.join', sub { $self->on_join(@_) });
|
||||||
}
|
}
|
||||||
@ -502,21 +502,22 @@ sub mycmd {
|
|||||||
if (defined $key) {
|
if (defined $key) {
|
||||||
$key = lc $key;
|
$key = lc $key;
|
||||||
|
|
||||||
if (defined $value and $u->{level} == 0) {
|
if (defined $value and not $self->{pbot}->{capabilities}->userhas($u, 'admin')) {
|
||||||
my @disallowed = qw/name level autoop autovoice/;
|
my @disallowed = qw/name autoop autovoice/;
|
||||||
if (grep { $_ eq $key } @disallowed) {
|
if (grep { $_ eq $key } @disallowed) {
|
||||||
return "You must be an admin to set $key.";
|
return "The $key metadata requires the admin capability to set, which your user account does not have.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($key eq 'level' and defined $value and $u->{level} < 90 and $value > $u->{level}) {
|
if (defined $value and not $self->{pbot}->{capabilities}->userhas($u, 'can-modify-capabilities')) {
|
||||||
return "You may not increase your level!";
|
if ($key =~ m/^can-/ or $self->{pbot}->{capabilities}->exists($key)) {
|
||||||
|
return "The $key metadata requires the can-modify-capabilities capability, which your user account does not have.";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
$result = "Usage: my <key> [value]; ";
|
$result = "Usage: my <key> [value]; ";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
my ($found_channel, $found_hostmask) = $self->find_user_account($channel, $hostmask);
|
my ($found_channel, $found_hostmask) = $self->find_user_account($channel, $hostmask);
|
||||||
$found_channel = $channel if not defined $found_channel; # let DualIndexHashObject disambiguate
|
$found_channel = $channel if not defined $found_channel; # let DualIndexHashObject disambiguate
|
||||||
$result .= $self->{users}->set($found_channel, $found_hostmask, $key, $value);
|
$result .= $self->{users}->set($found_channel, $found_hostmask, $key, $value);
|
||||||
|
@ -32,6 +32,10 @@ sub initialize {
|
|||||||
$self->{pbot}->{commands}->register(sub { $self->modcmd(@_) }, 'mod', 0);
|
$self->{pbot}->{commands}->register(sub { $self->modcmd(@_) }, 'mod', 0);
|
||||||
$self->{pbot}->{commands}->set_meta('mod', 'help', 'Provides restricted moderation abilities to voiced users. They can kick/ban/etc only users that are not admins, whitelisted, voiced or opped.');
|
$self->{pbot}->{commands}->set_meta('mod', 'help', 'Provides restricted moderation abilities to voiced users. They can kick/ban/etc only users that are not admins, whitelisted, voiced or opped.');
|
||||||
|
|
||||||
|
$self->{pbot}->{capabilities}->add('chanmod', 'can-mod', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanmod', 'can-voice', 1);
|
||||||
|
$self->{pbot}->{capabilities}->add('chanmod', 'can-devoice', 1);
|
||||||
|
|
||||||
$self->{commands} = {
|
$self->{commands} = {
|
||||||
'help' => { subref => sub { $self->help(@_) }, help => "Provides help about this command. Usage: mod help <mod command>; see also: mod help list" },
|
'help' => { subref => sub { $self->help(@_) }, help => "Provides help about this command. Usage: mod help <mod command>; see also: mod help list" },
|
||||||
'list' => { subref => sub { $self->list(@_) }, help => "Lists available mod commands. Usage: mod list" },
|
'list' => { subref => sub { $self->list(@_) }, help => "Lists available mod commands. Usage: mod list" },
|
||||||
@ -47,6 +51,7 @@ sub initialize {
|
|||||||
sub unload {
|
sub unload {
|
||||||
my ($self) = @_;
|
my ($self) = @_;
|
||||||
$self->{pbot}->{commands}->unregister('mod');
|
$self->{pbot}->{commands}->unregister('mod');
|
||||||
|
$self->{pbot}->{capabilities}->remove('chanmod');
|
||||||
}
|
}
|
||||||
|
|
||||||
sub help {
|
sub help {
|
||||||
|
Loading…
Reference in New Issue
Block a user