pbot/PBot/AdminCommands.pm

395 lines
12 KiB
Perl

# File: AdminCommands.pm
# Author: pragma_
#
# Purpose: Administrative command subroutines.
# 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::AdminCommands;
use warnings;
use strict;
use feature 'unicode_strings';
use feature 'switch';
no if $] >= 5.018, warnings => "experimental::smartmatch";
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__);
$self->{pbot}->{commands}->register(sub { return $self->login(@_) }, "login", 0);
$self->{pbot}->{commands}->register(sub { return $self->logout(@_) }, "logout", 0);
$self->{pbot}->{commands}->register(sub { return $self->in_channel(@_) }, "in", 0);
$self->{pbot}->{commands}->register(sub { return $self->join_channel(@_) }, "join", 40);
$self->{pbot}->{commands}->register(sub { return $self->part_channel(@_) }, "part", 40);
$self->{pbot}->{commands}->register(sub { return $self->ack_die(@_) }, "die", 90);
$self->{pbot}->{commands}->register(sub { return $self->adminadd(@_) }, "adminadd", 60);
$self->{pbot}->{commands}->register(sub { return $self->adminrem(@_) }, "adminrem", 60);
$self->{pbot}->{commands}->register(sub { return $self->adminset(@_) }, "adminset", 60);
$self->{pbot}->{commands}->register(sub { return $self->adminunset(@_) }, "adminunset", 60);
$self->{pbot}->{commands}->register(sub { return $self->sl(@_) }, "sl", 90);
$self->{pbot}->{commands}->register(sub { return $self->export(@_) }, "export", 90);
$self->{pbot}->{commands}->register(sub { return $self->reload(@_) }, "reload", 90);
$self->{pbot}->{commands}->register(sub { return $self->evalcmd(@_) }, "eval", 99);
}
sub sl {
my $self = shift;
my ($from, $nick, $user, $host, $arguments) = @_;
if (not length $arguments) {
return "Usage: sl <ircd command>";
}
$self->{pbot}->{conn}->sl($arguments);
return "";
}
sub in_channel {
my ($self, $from, $nick, $user, $host, $arguments, $stuff) = @_;
my $usage = "Usage: in <channel> <command>";
if (not $arguments) {
return $usage;
}
my ($channel, $command) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 2);
return $usage if not defined $channel or not defined $command;
$stuff->{admin_channel_override} = $channel;
$stuff->{command} = $command;
return $self->{pbot}->{interpreter}->interpret($stuff);
}
sub login {
my $self = shift;
my ($from, $nick, $user, $host, $arguments) = @_;
my $channel = $from;
if (not $arguments) {
return "Usage: login [channel] password";
}
if ($arguments =~ m/^([^ ]+)\s+(.+)/) {
$channel = $1;
$arguments = $2;
}
if ($self->{pbot}->{admins}->loggedin($channel, "$nick!$user\@$host")) {
return "/msg $nick You are already logged into channel $channel.";
}
my $result = $self->{pbot}->{admins}->login($channel, "$nick!$user\@$host", $arguments);
return "/msg $nick $result";
}
sub logout {
my $self = shift;
my ($from, $nick, $user, $host, $arguments) = @_;
return "/msg $nick Uh, you aren't logged into channel $from." if (not $self->{pbot}->{admins}->loggedin($from, "$nick!$user\@$host"));
$self->{pbot}->{admins}->logout($from, "$nick!$user\@$host");
return "/msg $nick Good-bye, $nick.";
}
sub adminadd {
my $self = shift;
my ($from, $nick, $user, $host, $arguments, $stuff) = @_;
my ($name, $channel, $hostmask, $level, $password) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 5);
if (not defined $name or not defined $channel or not defined $hostmask or not defined $level
or not defined $password) {
return "/msg $nick Usage: adminadd <name> <channel> <hostmask> <level> <password>";
}
$channel = '.*' if lc $channel eq 'global';
my $admin = $self->{pbot}->{admins}->find_admin($from, "$nick!$user\@$host");
if (not $admin) {
return "You are not an admin in $from.\n";
}
if ($admin->{level} < 90 and $level > 60) {
return "You may not set admin level higher than 60.\n";
}
$self->{pbot}->{admins}->add_admin($name, $channel, $hostmask, $level, $password);
return "Admin added.";
}
sub adminrem {
my $self = shift;
my ($from, $nick, $user, $host, $arguments, $stuff) = @_;
my ($channel, $hostmask) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 2);
if (not defined $channel or not defined $hostmask) {
return "/msg $nick Usage: adminrem <channel> <hostmask/name>";
}
$channel = lc $channel;
$hostmask = lc $hostmask;
$channel = '.*' if $channel eq 'global';
if (exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}) {
if (not exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$hostmask}) {
foreach my $mask (keys %{ $self->{pbot}->{admins}->{admins}->{hash}->{$channel} }) {
next if $mask eq '_name';
if ($self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$mask}->{name} eq $hostmask) {
$hostmask = $mask;
last;
}
}
}
}
return $self->{pbot}->{admins}->remove_admin($channel, $hostmask);
}
sub adminset {
my $self = shift;
my ($from, $nick, $user, $host, $arguments, $stuff) = @_;
my ($channel, $hostmask, $key, $value) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 4);
if (not defined $channel or not defined $hostmask) {
return "Usage: adminset <channel> <hostmask/name> [key] [value]";
}
$channel = lc $channel;
$hostmask = lc $hostmask;
$channel = '.*' if $channel eq 'global';
if (exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}) {
if (not exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$hostmask}) {
foreach my $mask (keys %{ $self->{pbot}->{admins}->{admins}->{hash}->{$channel} }) {
next if $mask eq '_name';
if ($self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$mask}->{name} eq $hostmask) {
$hostmask = $mask;
last;
}
}
}
}
my $admin = $self->{pbot}->{admins}->find_admin($from, "$nick!$user\@$host");
my $target = $self->{pbot}->{admins}->find_admin($channel, $hostmask);
if (not $admin) {
return "You are not an admin in $from.";
}
if (not $target) {
return "There is no admin $hostmask in channel $channel.";
}
if ($key eq 'level' && $admin->{level} < 90 and $value > 60) {
return "You may not set admin level higher than 60.\n";
}
if ($target->{level} > $admin->{level}) {
return "You may not modify admins higher in level than you.";
}
my $result = $self->{pbot}->{admins}->{admins}->set($channel, $hostmask, $key, $value);
$result =~ s/^password => .*;$/password => <private>;/m;
return $result;
}
sub adminunset {
my $self = shift;
my ($from, $nick, $user, $host, $arguments, $stuff) = @_;
my ($channel, $hostmask, $key) = $self->{pbot}->{interpreter}->split_args($stuff->{arglist}, 3);
if (not defined $channel or not defined $hostmask) {
return "Usage: adminunset <channel> <hostmask/name> <key>";
}
$channel = lc $channel;
$hostmask = lc $hostmask;
$channel = '.*' if $channel eq 'global';
if (exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}) {
if (not exists $self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$hostmask}) {
foreach my $mask (keys %{ $self->{pbot}->{admins}->{admins}->{hash}->{$channel} }) {
next if $mask eq '_name';
if ($self->{pbot}->{admins}->{admins}->{hash}->{$channel}->{$mask}->{name} eq $hostmask) {
$hostmask = $mask;
last;
}
}
}
}
return $self->{pbot}->{admins}->{admins}->unset($channel, $hostmask, $key);
}
sub join_channel {
my $self = shift;
my ($from, $nick, $user, $host, $arguments) = @_;
foreach my $channel (split /[\s+,]/, $arguments) {
$self->{pbot}->{logger}->log("$nick!$user\@$host made me join $channel\n");
$self->{pbot}->{chanops}->join_channel($channel);
}
return "/msg $nick Joining $arguments";
}
sub part_channel {
my $self = shift;
my ($from, $nick, $user, $host, $arguments) = @_;
$arguments = $from if not $arguments;
foreach my $channel (split /[\s+,]/, $arguments) {
$self->{pbot}->{logger}->log("$nick!$user\@$host made me part $channel\n");
$self->{pbot}->{chanops}->part_channel($channel);
}
return "/msg $nick Parting $arguments";
}
sub ack_die {
my $self = shift;
my ($from, $nick, $user, $host, $arguments) = @_;
$self->{pbot}->{logger}->log("$nick!$user\@$host made me exit.\n");
$self->{pbot}->atexit();
$self->{pbot}->{conn}->privmsg($from, "Good-bye.") if defined $from;
$self->{pbot}->{conn}->quit("Departure requested.");
exit 0;
}
sub export {
my $self = shift;
my ($from, $nick, $user, $host, $arguments) = @_;
if (not defined $arguments) {
return "/msg $nick Usage: export <factoids>";
}
if ($arguments =~ /^factoids$/i) {
return $self->{pbot}->{factoids}->export_factoids;
}
}
sub evalcmd {
my ($self, $from, $nick, $user, $host, $arguments) = @_;
$self->{pbot}->{logger}->log("[$from] $nick!$user\@$host Evaluating [$arguments]\n");
my $ret;
my $result = eval $arguments;
if ($@) {
if (length $result) {
$ret .= "[Error: $@] ";
} else {
$ret .= "Error: $@";
}
$ret =~ s/ at \(eval \d+\) line 1.//;
}
return "/say $ret $result";
}
sub reload {
my $self = shift;
my ($from, $nick, $user, $host, $arguments) = @_;
my %reloadables = (
'commands' => sub {
$self->{pbot}->{commands}->load_metadata;
return "Commands metadata reloaded.";
},
'blacklist' => sub {
$self->{pbot}->{blacklist}->clear_blacklist;
$self->{pbot}->{blacklist}->load_blacklist;
return "Blacklist reloaded.";
},
'whitelist' => sub {
$self->{pbot}->{antiflood}->{whitelist}->clear;
$self->{pbot}->{antiflood}->{whitelist}->load;
return "Whitelist reloaded.";
},
'ignores' => sub {
$self->{pbot}->{ignorelist}->clear_ignores;
$self->{pbot}->{ignorelist}->load_ignores;
return "Ignore list reloaded.";
},
'admins' => sub {
$self->{pbot}->{admins}->{admins}->clear;
$self->{pbot}->{admins}->load_admins;
return "Admins reloaded.";
},
'channels' => sub {
$self->{pbot}->{channels}->{channels}->clear;
$self->{pbot}->{channels}->load_channels;
return "Channels reloaded.";
},
'bantimeouts' => sub {
$self->{pbot}->{chanops}->{unban_timeout}->clear;
$self->{pbot}->{chanops}->{unban_timeout}->load;
return "Ban timeouts reloaded.";
},
'mutetimeouts' => sub {
$self->{pbot}->{chanops}->{unmute_timeout}->clear;
$self->{pbot}->{chanops}->{unmute_timeout}->load;
return "Mute timeouts reloaded.";
},
'registry' => sub {
$self->{pbot}->{registry}->{registry}->clear;
$self->{pbot}->{registry}->load;
return "Registry reloaded.";
},
'factoids' => sub {
$self->{pbot}->{factoids}->{factoids}->clear;
$self->{pbot}->{factoids}->load_factoids;
return "Factoids reloaded.";
},
'funcs' => sub {
$self->{pbot}->{func_cmd}->init_funcs;
return "Funcs reloaded.";
}
);
if (not length $arguments or not exists $reloadables{$arguments}) {
my $usage = 'Usage: reload <';
$usage .= join '|', sort keys %reloadables;
$usage .= '>';
return $usage;
}
return $reloadables{$arguments}();
}
1;