2021-07-21 06:38:07 +02:00
# File: ChanOp.pm
2010-03-23 19:24:02 +01:00
#
# Purpose: Channel operator command subroutines.
2023-02-21 06:31:52 +01:00
# SPDX-FileCopyrightText: 2010-2023 Pragmatic Software <pragma78@gmail.com>
2021-07-11 00:00:22 +02:00
# SPDX-License-Identifier: MIT
2017-03-05 22:33:31 +01:00
2021-07-21 08:06:03 +02:00
package PBot::Core::Commands::ChanOp ;
2010-03-23 19:24:02 +01:00
2021-06-19 06:23:34 +02:00
use PBot::Imports ;
2021-07-23 16:24:30 +02:00
use parent 'PBot::Core::Class' ;
2019-07-11 03:40:53 +02:00
2015-04-12 01:00:20 +02:00
use Time::Duration ;
2019-07-23 17:55:24 +02:00
use Time::HiRes qw/gettimeofday/ ;
2015-04-14 00:43:19 +02:00
2023-04-14 06:04:12 +02:00
sub initialize ($self, %conf) {
2020-02-15 23:38:32 +01:00
# register commands
2020-05-04 22:21:35 +02:00
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_op ( @ _ ) } , "op" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_deop ( @ _ ) } , "deop" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_voice ( @ _ ) } , "voice" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_devoice ( @ _ ) } , "devoice" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_ban ( @ _ ) } , "ban" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_unban ( @ _ ) } , "unban" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_mute ( @ _ ) } , "mute" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_unmute ( @ _ ) } , "unmute" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_kick ( @ _ ) } , "kick" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_mode ( @ _ ) } , "mode" , 1 ) ;
$ self - > { pbot } - > { commands } - > register ( sub { $ self - > cmd_invite ( @ _ ) } , "invite" , 1 ) ;
2020-02-15 23:38:32 +01:00
# 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 ) ;
# add to 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 - > { pbot } - > { capabilities } - > add ( 'chanop' , 'is-whitelisted' , 1 ) ;
# add to admin capability group
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'chanop' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-mode' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-mode-any' , 1 ) ;
# allow users to use !unban * or !unmute *
$ self - > { pbot } - > { capabilities } - > add ( 'can-clear-bans' , undef , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'can-clear-mutes' , undef , 1 ) ;
# allow admins to use !unban * or !unmute *
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-clear-bans' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-clear-mutes' , 1 ) ;
# allows users to use wildcards in command
$ self - > { pbot } - > { capabilities } - > add ( 'can-op-wildcard' , undef , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'can-voice-wildcard' , undef , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'can-kick-wildcard' , undef , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-kick-wildcard' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-op-wildcard' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'admin' , 'can-voice-wildcard' , 1 ) ;
$ self - > { pbot } - > { capabilities } - > add ( 'chanmod' , 'can-voice-wildcard' , 1 ) ;
$ self - > { invites } = { } ; # track who invited who in order to direct invite responses to them
# handle invite responses
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.inviting' , sub { $ self - > on_inviting ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.useronchannel' , sub { $ self - > on_useronchannel ( @ _ ) } ) ;
$ self - > { pbot } - > { event_dispatcher } - > register_handler ( 'irc.nosuchnick' , sub { $ self - > on_nosuchnick ( @ _ ) } ) ;
2020-01-15 05:01:19 +01:00
}
2023-04-14 06:04:12 +02:00
sub on_inviting ($self, $event_type, $event) {
2023-01-31 14:44:34 +01:00
my ( $ botnick , $ target , $ channel ) = $ event - > args ;
2021-07-21 06:38:07 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "User $target invited to channel $channel.\n" ) ;
2021-07-21 06:38:07 +02:00
if ( not exists $ self - > { invites } - > { lc $ channel } or not exists $ self - > { invites } - > { lc $ channel } - > { lc $ target } ) {
return 0 ;
}
2020-02-15 23:38:32 +01:00
$ event - > { conn } - > privmsg ( $ self - > { invites } - > { lc $ channel } - > { lc $ target } , "$target invited to $channel." ) ;
2021-07-21 06:38:07 +02:00
2020-02-15 23:38:32 +01:00
delete $ self - > { invites } - > { lc $ channel } - > { lc $ target } ;
return 1 ;
2020-01-15 05:01:19 +01:00
}
2023-04-14 06:04:12 +02:00
sub on_useronchannel ($self, $event_type, $event) {
2023-01-31 14:44:34 +01:00
my ( $ botnick , $ target , $ channel ) = $ event - > args ;
2021-07-21 06:38:07 +02:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "User $target is already on channel $channel.\n" ) ;
2021-07-21 06:38:07 +02:00
if ( not exists $ self - > { invites } - > { lc $ channel } or not exists $ self - > { invites } - > { lc $ channel } - > { lc $ target } ) {
return 0 ;
}
2020-02-15 23:38:32 +01:00
$ event - > { conn } - > privmsg ( $ self - > { invites } - > { lc $ channel } - > { lc $ target } , "$target is already on $channel." ) ;
2021-07-21 06:38:07 +02:00
2020-02-15 23:38:32 +01:00
delete $ self - > { invites } - > { lc $ channel } - > { lc $ target } ;
return 1 ;
2020-01-15 05:01:19 +01:00
}
2023-04-14 06:04:12 +02:00
sub on_nosuchnick ($self, $event_type, $event) {
2023-01-31 14:44:34 +01:00
my ( $ botnick , $ target , $ msg ) = $ event - > args ;
2020-01-15 05:01:19 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "$target: $msg\n" ) ;
2020-01-15 05:01:19 +01:00
2020-02-15 23:38:32 +01:00
my $ nick ;
foreach my $ channel ( keys % { $ self - > { invites } } ) {
if ( exists $ self - > { invites } - > { $ channel } - > { lc $ target } ) {
$ nick = $ self - > { invites } - > { $ channel } - > { lc $ target } ;
delete $ self - > { invites } - > { $ channel } - > { lc $ target } ;
last ;
}
2020-01-15 05:01:19 +01:00
}
2010-03-23 19:24:02 +01:00
2020-02-15 23:38:32 +01:00
return 0 if not defined $ nick ;
$ event - > { conn } - > privmsg ( $ nick , "$target: $msg" ) ;
return 1 ;
2019-12-29 08:17:15 +01:00
}
2023-04-14 06:04:12 +02:00
sub cmd_invite ($self, $context) {
2020-02-15 23:38:32 +01:00
my ( $ channel , $ target ) ;
2020-05-04 22:21:35 +02:00
if ( $ context - > { from } !~ m/^#/ ) {
2020-02-15 23:38:32 +01:00
# from /msg
my $ usage = "Usage from /msg: invite <channel> [nick]; if you omit [nick] then you will be invited" ;
2020-05-04 22:21:35 +02:00
return $ usage if not length $ context - > { arguments } ;
2020-05-02 05:59:51 +02:00
( $ channel , $ target ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ context - > { arglist } , 2 ) ;
2020-02-15 23:38:32 +01:00
return "$channel is not a channel; $usage" if $ channel !~ m/^#/ ;
2020-05-04 22:21:35 +02:00
$ target = $ context - > { nick } if not defined $ target ;
2020-02-15 23:38:32 +01:00
} else {
# in channel
2020-05-04 22:21:35 +02:00
return "Usage: invite [channel] <nick>" if not length $ context - > { arguments } ;
2020-02-15 23:38:32 +01:00
# add current channel as default channel
2020-05-04 22:21:35 +02:00
$ self - > { pbot } - > { interpreter } - > unshift_arg ( $ context - > { arglist } , $ context - > { from } ) if $ context - > { arglist } [ 0 ] !~ m/^#/ ;
2020-05-02 05:59:51 +02:00
( $ channel , $ target ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ context - > { arglist } , 2 ) ;
2020-02-15 23:38:32 +01:00
}
2020-05-04 22:21:35 +02:00
$ self - > { invites } - > { lc $ channel } - > { lc $ target } = $ context - > { nick } ;
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { chanops } - > add_op_command ( $ channel , "sl invite $target $channel" ) ;
$ self - > { pbot } - > { chanops } - > gain_ops ( $ channel ) ;
return "" ; # responses handled by events
2019-12-26 15:08:39 +01:00
}
2023-04-14 06:04:12 +02:00
sub generic_mode ($self, $mode_flag, $mode_name, $context) {
2020-02-15 23:38:32 +01:00
my $ result = '' ;
2020-05-04 22:21:35 +02:00
my $ channel = $ context - > { from } ;
2020-02-15 23:38:32 +01:00
my ( $ flag , $ mode_char ) = $ mode_flag =~ m/(.)(.)/ ;
if ( $ channel !~ m/^#/ ) {
# from message
2020-05-02 05:59:51 +02:00
$ channel = $ self - > { pbot } - > { interpreter } - > shift_arg ( $ context - > { arglist } ) ;
2020-02-15 23:38:32 +01:00
if ( not defined $ channel ) { return "Usage from message: $mode_name <channel> [nick]" ; }
elsif ( $ channel !~ m/^#/ ) { return "$channel is not a channel. Usage from message: $mode_name <channel> [nick]" ; }
}
$ channel = lc $ channel ;
if ( not $ self - > { pbot } - > { chanops } - > can_gain_ops ( $ channel ) ) { return "I am not configured as an OP for $channel. See `chanset` command for more information." ; }
# add $nick to $args if no argument
2020-05-04 22:21:35 +02:00
if ( not $ self - > { pbot } - > { interpreter } - > arglist_size ( $ context - > { arglist } ) ) { $ self - > { pbot } - > { interpreter } - > unshift_arg ( $ context - > { arglist } , $ context - > { nick } ) ; }
2020-02-15 23:38:32 +01:00
2021-06-25 03:28:49 +02:00
my $ max_modes = $ self - > { pbot } - > { isupport } - > { MODES } // 1 ;
2020-02-15 23:38:32 +01:00
my $ mode = $ flag ;
my $ list = '' ;
my $ i = 0 ;
2020-05-02 05:59:51 +02:00
foreach my $ targets ( $ self - > { pbot } - > { interpreter } - > unquoted_args ( $ context - > { arglist } ) ) {
2020-02-15 23:38:32 +01:00
foreach my $ target ( split /,/ , $ targets ) {
$ mode . = $ mode_char ;
$ list . = "$target " ;
$ i + + ;
if ( $ i >= $ max_modes ) {
2020-05-31 08:18:49 +02:00
$ context - > { arguments } = "$channel $mode $list" ;
$ context - > { arglist } = $ self - > { pbot } - > { interpreter } - > make_args ( $ context - > { arguments } ) ;
$ result = $ self - > cmd_mode ( $ context ) ;
$ mode = $ flag ;
$ list = '' ;
$ i = 0 ;
2020-02-15 23:38:32 +01:00
last if $ result ne '' and $ result ne 'Done.' ;
}
}
2020-01-12 02:46:44 +01:00
}
2020-02-15 23:38:32 +01:00
if ( $ i ) {
2020-05-31 08:18:49 +02:00
$ context - > { arguments } = "$channel $mode $list" ;
$ context - > { arglist } = $ self - > { pbot } - > { interpreter } - > make_args ( $ context - > { arguments } ) ;
2020-05-04 22:21:35 +02:00
$ result = $ self - > cmd_mode ( $ context ) ;
2020-01-12 02:46:44 +01:00
}
2020-02-15 23:38:32 +01:00
return $ result ;
2020-01-12 02:46:44 +01:00
}
2023-04-14 06:04:12 +02:00
sub cmd_op ($self, $context) {
2020-05-04 22:21:35 +02:00
return $ self - > generic_mode ( '+o' , 'op' , $ context ) ;
2020-01-12 03:02:00 +01:00
}
2020-01-12 02:46:44 +01:00
2023-04-14 06:04:12 +02:00
sub cmd_deop ($self, $context) {
2020-05-04 22:21:35 +02:00
return $ self - > generic_mode ( '-o' , 'deop' , $ context ) ;
2020-01-12 03:02:00 +01:00
}
2020-01-12 02:46:44 +01:00
2023-04-14 06:04:12 +02:00
sub cmd_voice ($self, $context) {
2020-05-04 22:21:35 +02:00
return $ self - > generic_mode ( '+v' , 'voice' , $ context ) ;
2020-01-12 03:02:00 +01:00
}
2020-01-12 02:46:44 +01:00
2023-04-14 06:04:12 +02:00
sub cmd_devoice ($self, $context) {
2020-05-04 22:21:35 +02:00
return $ self - > generic_mode ( '-v' , 'devoice' , $ context ) ;
2020-01-12 02:46:44 +01:00
}
2023-04-14 06:04:12 +02:00
sub cmd_mode ($self, $context) {
2020-05-04 22:21:35 +02:00
if ( not length $ context - > { arguments } ) { return "Usage: mode [channel] <arguments>" ; }
2019-12-27 04:24:35 +01:00
2020-02-15 23:38:32 +01:00
# add current channel as default channel
2020-05-02 05:59:51 +02:00
if ( $ context - > { arglist } [ 0 ] !~ m/^#/ ) {
2020-05-04 22:21:35 +02:00
if ( $ context - > { from } =~ m/^#/ ) {
$ self - > { pbot } - > { interpreter } - > unshift_arg ( $ context - > { arglist } , $ context - > { from } ) ;
} else {
return "Usage from private message: mode <channel> <arguments>" ;
}
2019-12-27 04:24:35 +01:00
}
2020-05-02 05:59:51 +02:00
my ( $ channel , $ modes , $ args ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ context - > { arglist } , 3 ) ;
2020-02-15 23:38:32 +01:00
my @ targets = split /\s+/ , $ args if defined $ args ;
my $ modifier ;
my $ i = 0 ;
my $ arg = 0 ;
2020-02-03 18:50:38 +01:00
2020-02-15 23:38:32 +01:00
my ( $ new_modes , $ new_targets ) = ( "" , "" ) ;
2021-06-25 03:28:49 +02:00
my $ max_modes = $ self - > { pbot } - > { isupport } - > { MODES } // 1 ;
2019-12-27 04:24:35 +01:00
2020-05-04 22:21:35 +02:00
my $ u = $ self - > { pbot } - > { users } - > loggedin ( $ channel , $ context - > { hostmask } ) ;
2019-12-27 04:24:35 +01:00
2020-02-15 23:38:32 +01:00
while ( $ modes =~ m/(.)/g ) {
my $ mode = $ 1 ;
2019-12-27 04:24:35 +01:00
2020-02-15 23:38:32 +01:00
if ( $ mode eq '-' or $ mode eq '+' ) {
$ modifier = $ mode ;
$ new_modes . = $ mode ;
next ;
2020-02-06 02:38:56 +01:00
}
2020-02-15 23:38:32 +01:00
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , "can-mode-$mode" ) ) {
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} Your user account does not have the can-mode-$mode capability required to set this mode." ;
2020-02-06 02:38:56 +01:00
}
2020-02-15 23:38:32 +01:00
my $ target = $ targets [ $ arg + + ] // "" ;
if ( ( $ mode eq 'v' or $ mode eq 'o' ) and $ target =~ m/\*/ ) {
# wildcard used; find all matching nicks; test against whitelist, etc
my $ q_target = lc quotemeta $ target ;
$ q_target =~ s/\\\*/.*/g ;
$ channel = lc $ channel ;
2020-05-04 22:21:35 +02:00
if ( not exists $ self - > { pbot } - > { nicklist } - > { nicklist } - > { $ channel } ) {
return "I have no nicklist for channel $channel; cannot use wildcard." ;
}
2020-02-15 23:38:32 +01:00
2020-05-04 22:21:35 +02:00
my $ u = $ self - > { pbot } - > { users } - > loggedin ( $ channel , $ context - > { hostmask } ) ;
2020-02-15 23:38:32 +01:00
if ( $ mode eq 'v' ) {
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-voice-wildcard' ) ) {
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} Using wildcards with `mode v` requires the can-voice-wildcard capability, which your user account does not have." ;
2020-02-15 23:38:32 +01:00
}
} else {
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-op-wildcard' ) ) {
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} Using wildcards with `mode o` requires the can-op-wildcard capability, which your user account does not have." ;
2020-02-15 23:38:32 +01:00
}
}
foreach my $ n ( keys % { $ self - > { pbot } - > { nicklist } - > { nicklist } - > { $ channel } } ) {
if ( $ n =~ m/^$q_target$/ ) {
my $ nick_data = $ self - > { pbot } - > { nicklist } - > { nicklist } - > { $ channel } - > { $ n } ;
if ( $ modifier eq '-' ) {
# removing mode -- check against whitelist, etc
2024-04-08 05:55:04 +02:00
next if $ nick_data - > { nick } eq $ self - > { pbot } - > { conn } - > nick ;
2020-02-15 23:38:32 +01:00
my $ u = $ self - > { pbot } - > { users } - > loggedin ( $ channel , $ nick_data - > { hostmask } ) ;
next if $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'is-whitelisted' ) ;
}
# skip nick if already has mode set/unset
if ( $ modifier eq '+' ) { next if exists $ nick_data - > { "+$mode" } ; }
else { next unless exists $ nick_data - > { "+$mode" } ; }
$ new_modes = $ modifier if not length $ new_modes ;
$ new_modes . = $ mode ;
$ new_targets . = "$self->{pbot}->{nicklist}->{nicklist}->{$channel}->{$n}->{nick} " ;
$ i + + ;
if ( $ i == $ max_modes ) {
$ self - > { pbot } - > { chanops } - > add_op_command ( $ channel , "mode $channel $new_modes $new_targets" ) ;
$ new_modes = "" ;
$ new_targets = "" ;
$ i = 0 ;
}
}
}
} else {
# no wildcard used; explicit mode requested - no whitelist checking
$ new_modes . = $ mode ;
$ new_targets . = "$target " if length $ target ;
$ i + + ;
if ( $ i == $ max_modes ) {
$ self - > { pbot } - > { chanops } - > add_op_command ( $ channel , "mode $channel $new_modes $new_targets" ) ;
$ new_modes = "" ;
$ new_targets = "" ;
$ i = 0 ;
}
2019-12-27 04:24:35 +01:00
}
}
2020-02-15 23:38:32 +01:00
if ( $ i ) { $ self - > { pbot } - > { chanops } - > add_op_command ( $ channel , "mode $channel $new_modes $new_targets" ) ; }
2019-12-27 04:24:35 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { chanops } - > gain_ops ( $ channel ) ;
2019-12-26 15:08:39 +01:00
2020-05-04 22:21:35 +02:00
if ( $ context - > { from } !~ m/^#/ ) { return "Done." ; }
else { return "" ; }
2019-07-23 17:55:24 +02:00
}
2023-04-14 06:04:12 +02:00
sub cmd_ban ($self, $context) {
2023-05-05 01:03:24 +02:00
return do_ban_or_mute ( $ self , $ context , 'ban' ) ;
2010-03-23 19:24:02 +01:00
}
2023-04-14 06:04:12 +02:00
sub cmd_unban ($self, $context) {
2020-05-04 22:21:35 +02:00
if ( not defined $ context - > { from } ) {
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Command missing ~from parameter!\n" ) ;
return "" ;
}
2020-05-02 05:59:51 +02:00
my ( $ target , $ channel , $ immediately ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ context - > { arglist } , 3 ) ;
2020-02-15 23:38:32 +01:00
if ( defined $ target and defined $ channel and $ channel !~ /^#/ ) {
my $ temp = $ target ;
$ target = $ channel ;
$ channel = $ temp ;
}
2023-05-05 01:03:24 +02:00
if ( not defined $ target ) { return "Usage: unban <nick/mask,...> [channel [false value to use unban queue]]" ; }
2020-02-15 23:38:32 +01:00
2020-05-04 22:21:35 +02:00
if ( not defined $ channel ) {
$ channel = exists $ context - > { admin_channel_override } ? $ context - > { admin_channel_override } : $ context - > { from } ;
}
2023-05-05 01:03:24 +02:00
return "Usage: unban <nick/mask,...> <channel> [false value to use unban queue]" if $ channel !~ /^#/ ;
2020-02-15 23:38:32 +01:00
my @ targets = split /,/ , $ target ;
2023-11-14 19:42:16 +01:00
$ immediately = 0 if @ targets > 1 && not defined $ immediately ;
$ immediately // = 1 ;
2020-02-15 23:38:32 +01:00
foreach my $ t ( @ targets ) {
if ( $ t eq '*' ) {
2020-05-04 22:21:35 +02:00
my $ u = $ self - > { pbot } - > { users } - > loggedin ( $ channel , $ context - > { hostmask } ) ;
2020-02-15 23:38:32 +01:00
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-clear-bans' ) ) {
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} Clearing the channel bans requires the can-clear-bans capability, which your user account does not have." ;
2020-02-15 23:38:32 +01:00
}
$ channel = lc $ channel ;
2020-04-29 06:33:49 +02:00
if ( $ self - > { pbot } - > { banlist } - > { banlist } - > exists ( $ channel ) ) {
2020-02-15 23:38:32 +01:00
$ immediately = 0 ;
2020-04-29 06:33:49 +02:00
foreach my $ banmask ( $ self - > { pbot } - > { banlist } - > { banlist } - > get_keys ( $ channel ) ) {
2020-05-15 01:53:10 +02:00
$ self - > { pbot } - > { banlist } - > unban_user ( $ channel , 'b' , $ banmask , $ immediately ) ;
2020-04-29 06:33:49 +02:00
}
2020-02-15 23:38:32 +01:00
last ;
}
} else {
2020-04-30 23:27:10 +02:00
$ self - > { pbot } - > { banlist } - > unban_user ( $ channel , 'b' , $ t , $ immediately ) ;
2020-02-06 02:03:39 +01:00
}
}
2018-06-06 07:59:33 +02:00
2023-11-14 19:42:16 +01:00
$ self - > { pbot } - > { banlist } - > flush_unban_queue if $ immediately ;
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} $target has been unbanned from $channel." ;
2010-03-23 19:24:02 +01:00
}
2023-04-14 06:04:12 +02:00
sub cmd_mute ($self, $context) {
2023-05-05 01:03:24 +02:00
return do_ban_or_mute ( $ self , $ context , 'mute' ) ;
2015-05-27 19:45:43 +02:00
}
2023-04-14 06:04:12 +02:00
sub cmd_unmute ($self, $context) {
2020-05-04 22:21:35 +02:00
if ( not defined $ context - > { from } ) {
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { logger } - > log ( "Command missing ~from parameter!\n" ) ;
return "" ;
}
2020-05-02 05:59:51 +02:00
my ( $ target , $ channel , $ immediately ) = $ self - > { pbot } - > { interpreter } - > split_args ( $ context - > { arglist } , 3 ) ;
2020-02-15 23:38:32 +01:00
if ( defined $ target and defined $ channel and $ channel !~ /^#/ ) {
my $ temp = $ target ;
$ target = $ channel ;
$ channel = $ temp ;
}
2023-05-05 01:03:24 +02:00
if ( not defined $ target ) { return "Usage: unmute <nick/mask,...> [channel [false value to use unban queue]]" ; }
2020-02-15 23:38:32 +01:00
2020-05-04 22:21:35 +02:00
$ channel = exists $ context - > { admin_channel_override } ? $ context - > { admin_channel_override } : $ context - > { from } if not defined $ channel ;
2020-02-15 23:38:32 +01:00
2023-05-05 01:03:24 +02:00
return "Usage for /msg: unmute <nick/mask,...> <channel> [false value to use unban queue]" if $ channel !~ /^#/ ;
2020-02-15 23:38:32 +01:00
my @ targets = split /,/ , $ target ;
2023-11-14 19:42:16 +01:00
$ immediately = 0 if @ targets > 1 && not defined $ immediately ;
$ immediately // = 1 ;
2020-02-15 23:38:32 +01:00
foreach my $ t ( @ targets ) {
if ( $ t eq '*' ) {
2020-05-04 22:21:35 +02:00
my $ u = $ self - > { pbot } - > { users } - > loggedin ( $ channel , $ context - > { hostmask } ) ;
2020-02-15 23:38:32 +01:00
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-clear-mutes' ) ) {
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} Clearing the channel mutes requires the can-clear-mutes capability, which your user account does not have." ;
2020-02-15 23:38:32 +01:00
}
$ channel = lc $ channel ;
2020-04-29 06:33:49 +02:00
if ( $ self - > { pbot } - > { banlist } - > { quietlist } - > exists ( $ channel ) ) {
2020-02-15 23:38:32 +01:00
$ immediately = 0 ;
2020-04-29 06:33:49 +02:00
foreach my $ banmask ( $ self - > { pbot } - > { banlist } - > { quietlist } - > get_keys ( $ channel ) ) {
$ self - > { pbot } - > { banlist } - > unban_user ( $ channel , 'q' , $ banmask , $ immediately ) ;
}
2020-02-15 23:38:32 +01:00
last ;
}
} else {
2020-04-29 06:33:49 +02:00
$ self - > { pbot } - > { banlist } - > unban_user ( $ channel , 'q' , $ t , $ immediately ) ;
2020-02-06 02:03:39 +01:00
}
}
2018-06-06 07:59:33 +02:00
2023-11-14 19:42:16 +01:00
$ self - > { pbot } - > { banlist } - > flush_unban_queue if $ immediately ;
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} $target has been unmuted in $channel." ;
2015-05-27 19:45:43 +02:00
}
2023-04-14 06:04:12 +02:00
sub cmd_kick ($self, $context) {
2020-02-15 23:38:32 +01:00
my ( $ channel , $ victim , $ reason ) ;
2020-05-04 22:21:35 +02:00
my $ arguments = $ context - > { arguments } ;
2016-08-29 07:36:46 +02:00
2020-05-04 22:21:35 +02:00
if ( not $ context - > { from } =~ /^#/ ) {
2020-02-15 23:38:32 +01:00
# used in private message
2023-05-05 01:03:24 +02:00
if ( not $ arguments =~ s/^(^#\S+) (\S+)\s*// ) { return "Usage from private message: kick <channel> <nick,...> [reason]; <nick> may include wildcards" ; }
2020-02-15 23:38:32 +01:00
( $ channel , $ victim ) = ( $ 1 , $ 2 ) ;
2016-08-29 07:36:46 +02:00
} else {
2020-02-15 23:38:32 +01:00
# used in channel
if ( $ arguments =~ s/^(#\S+)\s+(\S+)\s*// ) { ( $ channel , $ victim ) = ( $ 1 , $ 2 ) ; }
2020-05-04 22:21:35 +02:00
elsif ( $ arguments =~ s/^(\S+)\s*// ) { ( $ victim , $ channel ) = ( $ 1 , exists $ context - > { admin_channel_override } ? $ context - > { admin_channel_override } : $ context - > { from } ) ; }
2023-05-05 01:03:24 +02:00
else { return "Usage: kick [channel] <nick,...> [reason]; <nick> may include wildcards" ; }
2016-08-29 07:36:46 +02:00
}
2020-02-15 23:38:32 +01:00
$ reason = $ arguments ;
# If the user is too stupid to remember the order of the arguments,
# we can help them out by seeing if they put the channel in the reason.
if ( $ reason =~ s/^(#\S+)\s*// ) { $ channel = $ 1 ; }
my @ insults ;
if ( not length $ reason ) {
2021-11-20 03:05:50 +01:00
if ( open my $ fh , '<' , $ self - > { pbot } - > { registry } - > get_value ( 'general' , 'applet_dir' ) . '/insults.txt' ) {
2020-02-15 23:38:32 +01:00
@ insults = <$fh> ;
close $ fh ;
$ reason = $ insults [ rand @ insults ] ;
$ reason =~ s/\s+$// ;
} else {
$ reason = 'Bye!' ;
2019-12-31 03:17:35 +01:00
}
}
2020-06-25 18:53:09 +02:00
if ( $ context - > { keyword } =~ /^[A-Z]+$/ ) {
$ reason = uc $ reason ;
2020-06-27 02:22:38 +02:00
} elsif ( $ context - > { keyword } eq 'KiCk' or $ context - > { keyword } eq 'kIcK' ) {
$ reason =~ s/(.)(.)/lc($1) . uc($2)/ge ;
2020-06-25 18:53:09 +02:00
}
2020-02-15 23:38:32 +01:00
my @ nicks = split /,/ , $ victim ;
foreach my $ n ( @ nicks ) {
if ( $ n =~ m/\*/ ) {
# wildcard used; find all matching nicks; test against whitelist, etc
my $ q_target = lc quotemeta $ n ;
$ q_target =~ s/\\\*/.*/g ;
$ channel = lc $ channel ;
if ( not exists $ self - > { pbot } - > { nicklist } - > { nicklist } - > { $ channel } ) { return "I have no nicklist for channel $channel; cannot use wildcard." ; }
2020-05-04 22:21:35 +02:00
my $ u = $ self - > { pbot } - > { users } - > loggedin ( $ channel , $ context - > { hostmask } ) ;
2020-02-15 23:38:32 +01:00
if ( not $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'can-kick-wildcard' ) ) {
2020-05-04 22:21:35 +02:00
return "/msg $context->{nick} Using wildcards with `kick` requires the can-kick-wildcard capability, which your user account does not have." ;
2020-02-15 23:38:32 +01:00
}
foreach my $ nl ( keys % { $ self - > { pbot } - > { nicklist } - > { nicklist } - > { $ channel } } ) {
if ( $ nl =~ m/^$q_target$/ ) {
my $ nick_data = $ self - > { pbot } - > { nicklist } - > { nicklist } - > { $ channel } - > { $ nl } ;
2024-04-08 05:55:04 +02:00
next if $ nick_data - > { nick } eq $ self - > { pbot } - > { conn } - > nick ;
2020-02-15 23:38:32 +01:00
my $ u = $ self - > { pbot } - > { users } - > loggedin ( $ channel , $ nick_data - > { hostmask } ) ;
next if $ self - > { pbot } - > { capabilities } - > userhas ( $ u , 'is-whitelisted' ) ;
$ self - > { pbot } - > { chanops } - > add_op_command ( $ channel , "kick $channel $nl $reason" ) ;
}
}
} else {
# no wildcard used, explicit kick
$ self - > { pbot } - > { chanops } - > add_op_command ( $ channel , "kick $channel $n $reason" ) ;
}
# randomize next kick reason
if ( @ insults ) {
$ reason = $ insults [ rand @ insults ] ;
$ reason =~ s/\s+$// ;
}
2017-11-11 21:59:27 +01:00
}
2017-11-03 20:28:41 +01:00
2020-02-15 23:38:32 +01:00
$ self - > { pbot } - > { chanops } - > gain_ops ( $ channel ) ;
return "" ;
2010-03-23 19:24:02 +01:00
}
2023-05-05 01:03:24 +02:00
sub do_ban_or_mute ($self, $context, $mode) {
my ( $ target , $ channel , $ length , $ reason ) ;
my $ usage = "usage: $mode <mask,...> [timeout (default: 24h) [reason]] [-c <channel>] [-t <timeout>] [-r <reason>]" ;
my % opts = (
channel = > \ $ channel ,
timeout = > \ $ length ,
reason = > \ $ reason ,
) ;
my ( $ opt_args , $ opt_error ) = $ self - > { pbot } - > { interpreter } - > getopt (
$ context - > { arguments } ,
\ % opts ,
[ 'bundling' ] ,
'channel|c=s' ,
'timeout|t=s' ,
'reason|r=s' ,
) ;
return "$opt_error -- $usage" if defined $ opt_error ;
$ target = shift @$ opt_args ;
$ length = shift @$ opt_args if not defined $ length ;
$ reason = "@$opt_args" if not defined $ reason ;
$ channel = '' if not defined $ channel ;
$ length = '' if not defined $ length ;
$ reason = undef if not length $ reason ;
if ( not defined $ target ) {
return $ usage ;
}
if ( not length $ channel ) {
$ channel = exists $ context - > { admin_channel_override } ? $ context - > { admin_channel_override } : $ context - > { from } ;
}
if ( $ channel eq $ context - > { nick } ) {
return "Channel argument is required in private-message; $usage" ;
}
if ( $ channel !~ m/^#/ ) {
return "Bad channel '$channel'; $usage" ;
}
my $ error ;
( $ length , $ error ) = $ self - > { pbot } - > { parsedate } - > parsedate ( $ length ) ;
return $ error if defined $ error ;
$ channel = lc $ channel ;
$ target = lc $ target ;
my $ result = '' ;
my $ sep = '' ;
my @ targets = split /,/ , $ target ;
my $ immediately = @ targets > 1 ? 0 : 1 ;
my $ duration ;
2023-05-05 01:30:35 +02:00
my $ list = $ mode eq 'ban' ? 'banlist' : 'quietlist' ;
2023-05-05 01:03:24 +02:00
foreach my $ t ( @ targets ) {
my $ mask = lc $ self - > { pbot } - > { banlist } - > nick_to_banmask ( $ t ) ;
2023-05-05 01:30:35 +02:00
my $ ban = $ self - > { pbot } - > { banlist } - > { $ list } - > get_data ( $ channel , $ mask ) ;
2023-05-05 01:03:24 +02:00
if ( defined $ ban ) {
my $ save = 0 ;
if ( defined $ reason ) {
$ ban - > { reason } = $ reason ;
$ save = 1 ;
}
if ( $ length ) {
$ ban - > { timeout } = gettimeofday + $ length ;
$ save = 1 ;
}
2023-05-05 01:30:35 +02:00
$ self - > { pbot } - > { banlist } - > { $ list } - > save if $ save ;
2023-05-05 01:03:24 +02:00
$ result . = "$sep$mask " ;
if ( not $ save ) {
$ result . = 'is already ' ;
}
$ result . = ( $ mode eq 'ban' ? 'banned' : 'muted' ) . " in $channel" ;
if ( defined $ ban - > { reason } ) {
$ result . = " because $ban->{reason}" ;
}
if ( $ ban - > { timeout } > 0 ) {
my $ d = duration ( $ ban - > { timeout } - gettimeofday ) ;
$ result . = " ($d remaining)" ;
}
$ sep = '; ' ;
} else {
if ( not $ length ) {
# TODO: user account length override
$ length = $ self - > { pbot } - > { registry } - > get_value ( $ channel , "default_${mode}_timeout" , 0 , $ context )
2023-05-06 03:59:35 +02:00
// $ self - > { pbot } - > { registry } - > get_value ( 'general' , "default_${mode}_timeout" , 0 , $ context )
// 60 * 60 * 24 ; # 24 hours
2023-05-05 01:03:24 +02:00
}
$ self - > { pbot } - > { banlist } - > ban_user_timed ( $ channel , $ mode eq 'ban' ? 'b' : 'q' , $ mask , $ length , $ context - > { hostmask } , $ reason , $ immediately ) ;
2023-05-06 03:59:35 +02:00
2023-05-05 01:03:24 +02:00
$ duration = $ length > 0 ? duration $ length : 'all eternity' ;
2023-05-06 03:59:35 +02:00
2023-05-05 01:03:24 +02:00
if ( $ immediately ) {
$ result . = "$sep$mask " . ( $ mode eq 'ban' ? 'banned' : 'muted' ) . " in $channel ($duration)" ;
$ result . = " because $reason" if defined $ reason ;
$ sep = '; ' ;
} else {
$ result . = "$sep$mask" ;
$ sep = ', ' ;
}
}
}
if ( not $ immediately ) {
$ result . = ( $ mode eq 'ban' ? ' banned' : ' muted' ) . " in $channel for $duration" ;
$ self - > { pbot } - > { banlist } - > flush_ban_queue ;
}
$ result = "/msg $context->{nick} $result" if $ result !~ m/remaining/ ;
2023-05-06 03:59:35 +02:00
2023-05-05 01:03:24 +02:00
return $ result ;
}
2010-03-23 19:24:02 +01:00
1 ;