3
0
mirror of https://github.com/pragma-/pbot.git synced 2024-12-24 11:42:35 +01:00
pbot/lib/PBot/Plugin/RestrictedMod.pm

201 lines
7.9 KiB
Perl
Raw Normal View History

2021-06-19 06:23:34 +02:00
# File: RestrictedMod.pm
#
# Purpose: Provides restricted moderation abilities to voiced users.
# They are allowed to ban/mute/kick only users that are not admins,
# whitelisted, or autoop/autovoice. This is useful for, e.g., IRCnet
# configurations where +v users are recognized as "semi-trusted" in
# order to provide assistance in combating heavy spam and drone traffic.
2021-07-11 00:00:22 +02:00
# SPDX-FileCopyrightText: 2021 Pragmatic Software <pragma78@gmail.com>
# SPDX-License-Identifier: MIT
2020-02-01 03:29:52 +01:00
2021-07-14 04:45:56 +02:00
package PBot::Plugin::RestrictedMod;
use parent 'PBot::Plugin::Base';
2020-02-01 03:29:52 +01:00
2021-06-19 06:23:34 +02:00
use PBot::Imports;
2020-02-01 03:29:52 +01:00
use Storable qw/dclone/;
2020-02-01 03:29:52 +01:00
sub initialize {
2020-02-15 23:38:32 +01:00
my ($self, %conf) = @_;
$self->{pbot}->{commands}->add(
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.',
subref => sub { $self->cmd_mod(@_) },
2020-02-15 23:38:32 +01:00
);
$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} = {
'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"},
'kick' => {subref => sub { $self->kick(@_) }, help => "Kicks a nick from the channel. Usage: mod kick <nick>"},
'ban' => {subref => sub { $self->ban(@_) }, help => "Bans a nick from the channel. Cannot be used to set a custom banmask. Usage: mod ban <nick>"},
'mute' => {subref => sub { $self->mute(@_) }, help => "Mutes a nick in the channel. Usage: mod mute <nick>"},
'unban' => {subref => sub { $self->unban(@_) }, help => "Removes bans set by moderators. Cannot remove any other types of bans. Usage: mod unban <nick or mask>"},
'unmute' => {subref => sub { $self->unmute(@_) }, help => "Removes mutes set by moderators. Cannot remove any other types of mutes. Usage: mod unmute <nick or mask>"},
'kb' => {subref => sub { $self->kb(@_) }, help => "Kickbans a nick from the channel. Cannot be used to set a custom banmask. Usage: mod kb <nick>"},
};
2020-02-01 03:29:52 +01:00
}
sub unload {
2020-02-15 23:38:32 +01:00
my ($self) = @_;
$self->{pbot}->{commands}->remove('mod');
2020-02-15 23:38:32 +01:00
$self->{pbot}->{capabilities}->remove('chanmod');
2020-02-01 03:29:52 +01:00
}
sub help {
2020-05-02 05:59:51 +02:00
my ($self, $context) = @_;
my $command = $self->{pbot}->{interpreter}->shift_arg($context->{arglist}) // 'help';
2020-02-15 23:38:32 +01:00
if (exists $self->{commands}->{$command}) {
return $self->{commands}->{$command}->{help};
} else {
return "No such mod command '$command'. I can't help you with that.";
}
2020-02-01 03:29:52 +01:00
}
sub list {
2020-05-02 05:59:51 +02:00
my ($self, $context) = @_;
2020-02-15 23:38:32 +01:00
return "Available mod commands: " . join ', ', sort keys %{$self->{commands}};
2020-02-01 03:29:52 +01:00
}
sub generic_command {
2020-05-02 05:59:51 +02:00
my ($self, $context, $command) = @_;
2020-02-01 03:29:52 +01:00
2020-05-02 05:59:51 +02:00
my $channel = $context->{from};
2020-02-15 23:38:32 +01:00
if ($channel !~ m/^#/) {
2020-05-02 05:59:51 +02:00
$channel = $self->{pbot}->{interpreter}->shift_arg($context->{arglist});
2020-02-01 03:29:52 +01:00
2020-02-15 23:38:32 +01:00
if (not defined $channel or $channel !~ /^#/) { return "Must specify channel from private message. Usage: mod $command <channel> <nick>"; }
2020-02-01 03:29:52 +01:00
}
2020-02-15 23:38:32 +01:00
return "I do not have OPs for this channel. I cannot do any moderation here." if not $self->{pbot}->{chanops}->can_gain_ops($channel);
return "Voiced moderation is not enabled for this channel. Use `regset $channel.restrictedmod 1` to enable."
if not $self->{pbot}->{registry}->get_value($channel, 'restrictedmod');
my $hostmask = $context->{hostmask};
2020-02-15 23:38:32 +01:00
my $user = $self->{pbot}->{users}->loggedin($channel, $hostmask) // {admin => 0, chanmod => 0};
2020-05-02 05:59:51 +02:00
my $voiced = $self->{pbot}->{nicklist}->get_meta($channel, $context->{nick}, '+v');
2020-02-15 23:38:32 +01:00
if (not $voiced and not $self->{pbot}->{capabilities}->userhas($user, 'admin') and not $self->{pbot}->{capabilities}->userhas($user, 'chanmod')) {
return "You must be voiced (usermode +v) or have the admin or chanmod capability to use this command.";
}
2020-02-15 23:38:32 +01:00
2020-05-02 05:59:51 +02:00
my $target = $self->{pbot}->{interpreter}->shift_arg($context->{arglist});
2020-02-15 23:38:32 +01:00
return "Missing target. Usage: mod $command <nick>" if not defined $target;
if ($command eq 'unban') {
my $reason = $self->{pbot}->{banlist}->checkban($channel, 'b', $target);
2020-02-15 23:38:32 +01:00
if ($reason =~ m/moderator ban/) {
2020-04-29 07:36:13 +02:00
$self->{pbot}->{banlist}->unban_user($channel, 'b', $target, 1);
2020-02-15 23:38:32 +01:00
return "";
} else {
return "I don't think so. That ban was not set by a moderator.";
}
} elsif ($command eq 'unmute') {
my $reason = $self->{pbot}->{banlist}->checkban($channel, 'q', $target);
2020-02-15 23:38:32 +01:00
if ($reason =~ m/moderator mute/) {
$self->{pbot}->{banlist}->unban_user($channel, 'q', $target, 1);
2020-02-15 23:38:32 +01:00
return "";
} else {
return "I don't think so. That mute was not set by a moderator.";
}
}
my $target_nicklist;
2020-05-02 05:59:51 +02:00
if (not $self->{pbot}->{nicklist}->is_present($channel, $target)) { return "$context->{nick}: I do not see anybody named $target in this channel."; }
2020-02-15 23:38:32 +01:00
else { $target_nicklist = $self->{pbot}->{nicklist}->{nicklist}->{lc $channel}->{lc $target}; }
my $target_user = $self->{pbot}->{users}->loggedin($channel, $target_nicklist->{hostmask});
if ( (defined $target_user and $target_user->{autoop} or $target_user->{autovoice})
or $target_nicklist->{'+v'}
or $target_nicklist->{'+o'}
or $self->{pbot}->{capabilities}->userhas($target_user, 'is-whitelisted'))
{
return "I don't think so.";
}
if ($command eq 'kick') {
$self->{pbot}->{chanops}->add_op_command($channel, "kick $channel $target Have a nice day!");
$self->{pbot}->{chanops}->gain_ops($channel);
} elsif ($command eq 'ban') {
$self->{pbot}->{banlist}->ban_user_timed(
$channel,
'b',
$target,
3600 * 24,
2020-05-02 05:59:51 +02:00
"$context->{nick}!$context->{user}\@$context->{host}",
"doing something naughty (moderator ban)",
1
2020-02-15 23:38:32 +01:00
);
} elsif ($command eq 'mute') {
$self->{pbot}->{banlist}->ban_user_timed(
$channel,
'q',
$target,
3600 * 24,
2020-05-02 05:59:51 +02:00
"$context->{nick}!$context->{user}\@$context->{host}",
"doing something naughty (moderator mute)",
1
2020-02-15 23:38:32 +01:00
);
}
2020-02-15 23:38:32 +01:00
return "";
2020-02-01 03:29:52 +01:00
}
sub kick {
2020-05-02 05:59:51 +02:00
my ($self, $context) = @_;
return $self->generic_command($context, 'kick');
2020-02-01 03:29:52 +01:00
}
sub ban {
2020-05-02 05:59:51 +02:00
my ($self, $context) = @_;
return $self->generic_command($context, 'ban');
2020-02-01 03:29:52 +01:00
}
sub mute {
2020-05-02 05:59:51 +02:00
my ($self, $context) = @_;
return $self->generic_command($context, 'mute');
}
sub unban {
2020-05-02 05:59:51 +02:00
my ($self, $context) = @_;
return $self->generic_command($context, 'unban');
}
sub unmute {
2020-05-02 05:59:51 +02:00
my ($self, $context) = @_;
return $self->generic_command($context, 'unmute');
}
sub kb {
2020-05-02 05:59:51 +02:00
my ($self, $context) = @_;
my $result = $self->ban(dclone $context); # note: using copy of $context to preserve $context->{arglist} for $self->kick($context)
2020-02-15 23:38:32 +01:00
return $result if length $result;
2020-05-02 05:59:51 +02:00
return $self->kick($context);
2020-02-01 03:29:52 +01:00
}
sub cmd_mod {
my ($self, $context) = @_;
2020-02-01 03:29:52 +01:00
2020-05-02 05:59:51 +02:00
my $command = $self->{pbot}->{interpreter}->shift_arg($context->{arglist}) // '';
2020-02-15 23:38:32 +01:00
$command = lc $command;
2020-02-01 03:29:52 +01:00
2020-02-15 23:38:32 +01:00
if (grep { $_ eq $command } keys %{$self->{commands}}) {
2020-05-02 05:59:51 +02:00
return $self->{commands}->{$command}->{subref}->($context);
2020-02-01 03:29:52 +01:00
} else {
2020-02-15 23:38:32 +01:00
my $commands = join ', ', sort keys %{$self->{commands}};
if ($context->{from} !~ m/^#/) {
return "Usage: mod <channel> <command> [arguments]; commands are: $commands; see `mod help <command>` for more information.";
} else {
return "Usage: mod <command> [arguments]; commands are: $commands; see `mod help <command>` for more information.";
}
2020-02-01 03:29:52 +01:00
}
}
1;