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.
2023-02-21 06:31:52 +01:00
# SPDX-FileCopyrightText: 2020-2023 Pragmatic Software <pragma78@gmail.com>
2021-07-11 00:00:22 +02:00
# 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
2020-02-01 03:42:16 +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 ) = @ _ ;
2021-07-31 04:01:24 +02:00
$ 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 ) = @ _ ;
2021-07-31 04:01:24 +02:00
$ 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
2020-04-29 06:33:49 +02: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' ) ;
2020-05-04 22:21:35 +02:00
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-01 07:03:50 +01:00
}
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' ) {
2020-04-29 06:33:49 +02:00
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' ) {
2020-04-29 06:33:49 +02:00
my $ reason = $ self - > { pbot } - > { banlist } - > checkban ( $ channel , 'q' , $ target ) ;
2020-02-15 23:38:32 +01:00
if ( $ reason =~ m/moderator mute/ ) {
2020-04-29 06:33:49 +02:00
$ 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' ) {
2020-04-29 06:33:49 +02:00
$ 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}" ,
2020-04-29 06:33:49 +02:00
"doing something naughty (moderator ban)" ,
1
2020-02-15 23:38:32 +01:00
) ;
} elsif ( $ command eq 'mute' ) {
2020-04-29 06:33:49 +02:00
$ 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}" ,
2020-04-29 06:33:49 +02:00
"doing something naughty (moderator mute)" ,
1
2020-02-15 23:38:32 +01:00
) ;
2020-02-01 07:03:50 +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' ) ;
2020-02-01 07:03:50 +01:00
}
sub unban {
2020-05-02 05:59:51 +02:00
my ( $ self , $ context ) = @ _ ;
return $ self - > generic_command ( $ context , 'unban' ) ;
2020-02-01 07:03:50 +01:00
}
sub unmute {
2020-05-02 05:59:51 +02:00
my ( $ self , $ context ) = @ _ ;
return $ self - > generic_command ( $ context , 'unmute' ) ;
2020-02-01 07:03:50 +01:00
}
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
}
2020-05-04 22:21:35 +02: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 } } ;
2020-05-04 22:21:35 +02:00
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 ;