2021-09-06 12:46:16 +00:00
use std ::{ collections ::HashMap , iter } ;
2020-08-18 19:09:21 +00:00
2021-09-06 12:46:16 +00:00
use chrono ::offset ::Utc ;
use chrono_tz ::{ Tz , TZ_VARIANTS } ;
use levenshtein ::levenshtein ;
2021-09-10 23:14:23 +00:00
use regex ::Regex ;
2021-09-06 12:46:16 +00:00
use regex_command_attr ::command ;
2020-08-18 19:09:21 +00:00
use serenity ::{
client ::Context ,
2021-01-03 14:48:18 +00:00
model ::{
2021-06-27 15:55:59 +00:00
channel ::Message ,
2021-09-10 23:14:23 +00:00
guild ::ActionRole ::Create ,
2021-06-27 15:31:54 +00:00
id ::{ ChannelId , MessageId , RoleId } ,
2021-09-02 22:38:12 +00:00
interactions ::message_component ::ButtonStyle ,
2021-09-10 23:14:23 +00:00
misc ::Mentionable ,
2021-01-03 14:48:18 +00:00
} ,
2020-08-18 19:09:21 +00:00
} ;
2020-08-22 00:24:12 +00:00
use crate ::{
2021-09-10 23:14:23 +00:00
consts ::{ REGEX_ALIAS , REGEX_COMMANDS , THEME_COLOR } ,
framework ::{ CommandInvoke , CreateGenericResponse , PermissionLevel } ,
2021-09-02 22:38:12 +00:00
models ::{ channel_data ::ChannelData , guild_data ::GuildData , user_data ::UserData , CtxData } ,
2021-09-10 23:14:23 +00:00
PopularTimezones , RegexFramework , SQLPool ,
2020-08-22 00:24:12 +00:00
} ;
2020-09-28 15:11:27 +00:00
2021-09-10 23:14:23 +00:00
#[ command( " blacklist " ) ]
#[ description( " Block channels from using bot commands " ) ]
#[ arg(
name = " channel " ,
description = " The channel to blacklist " ,
kind = " Channel " ,
required = false
) ]
2020-08-18 19:09:21 +00:00
#[ supports_dm(false) ]
2021-09-10 23:14:23 +00:00
#[ required_permissions(Restricted) ]
2020-08-25 16:19:08 +00:00
#[ can_blacklist(false) ]
2021-09-10 23:14:23 +00:00
async fn blacklist (
ctx : & Context ,
invoke : & ( dyn CommandInvoke + Send + Sync ) ,
args : HashMap < String , String > ,
) {
let pool = ctx . data . read ( ) . await . get ::< SQLPool > ( ) . cloned ( ) . unwrap ( ) ;
2020-08-26 17:26:28 +00:00
2021-09-10 23:14:23 +00:00
let channel = match args . get ( " channel " ) {
Some ( channel_id ) = > ChannelId ( channel_id . parse ::< u64 > ( ) . unwrap ( ) ) ,
2020-08-26 17:26:28 +00:00
2021-09-10 23:14:23 +00:00
None = > invoke . channel_id ( ) ,
}
. to_channel_cached ( & ctx )
. unwrap ( ) ;
2020-08-22 00:24:12 +00:00
2021-09-10 23:14:23 +00:00
let mut channel_data = ChannelData ::from_channel ( & channel , & pool ) . await . unwrap ( ) ;
2020-11-19 21:31:31 +00:00
channel_data . blacklisted = ! channel_data . blacklisted ;
channel_data . commit_changes ( & pool ) . await ;
if channel_data . blacklisted {
2021-09-10 23:14:23 +00:00
let _ = invoke
. respond (
ctx . http . clone ( ) ,
CreateGenericResponse ::new ( )
. content ( format! ( " {} has been blacklisted " , channel . mention ( ) ) ) ,
)
2021-07-17 16:00:47 +00:00
. await ;
2020-10-12 20:01:27 +00:00
} else {
2021-09-10 23:14:23 +00:00
let _ = invoke
. respond (
ctx . http . clone ( ) ,
CreateGenericResponse ::new ( )
. content ( format! ( " {} has been removed from the blacklist " , channel . mention ( ) ) ) ,
)
2021-07-17 16:00:47 +00:00
. await ;
2020-08-22 00:24:12 +00:00
}
2020-08-18 19:09:21 +00:00
}
2020-08-27 11:15:20 +00:00
2021-09-10 23:14:23 +00:00
#[ command( " timezone " ) ]
#[ description( " Select your timezone " ) ]
#[ arg(
name = " timezone " ,
description = " Timezone to use from this list: https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee " ,
kind = " String " ,
required = false
) ]
async fn timezone (
ctx : & Context ,
invoke : & ( dyn CommandInvoke + Send + Sync ) ,
args : HashMap < String , String > ,
) {
let pool = ctx . data . read ( ) . await . get ::< SQLPool > ( ) . cloned ( ) . unwrap ( ) ;
let mut user_data = ctx . user_data ( invoke . author_id ( ) ) . await . unwrap ( ) ;
let footer_text = format! ( " Current timezone: {} " , user_data . timezone ) ;
if let Some ( timezone ) = args . get ( " timezone " ) {
match timezone . parse ::< Tz > ( ) {
Ok ( tz ) = > {
user_data . timezone = timezone . clone ( ) ;
2020-09-01 16:07:51 +00:00
user_data . commit_changes ( & pool ) . await ;
2020-08-27 20:37:44 +00:00
2021-09-10 23:14:23 +00:00
let now = Utc ::now ( ) . with_timezone ( & tz ) ;
let _ = invoke
. respond (
ctx . http . clone ( ) ,
CreateGenericResponse ::new ( ) . embed ( | e | {
e . title ( " Timezone Set " )
. description ( format! (
" Timezone has been set to **{}**. Your current time should be `{}` " ,
timezone ,
now . format ( " %H:%M " ) . to_string ( )
) )
. color ( * THEME_COLOR )
} ) ,
)
. await ;
2020-08-29 17:07:15 +00:00
}
2020-08-27 20:37:44 +00:00
2020-08-29 17:07:15 +00:00
Err ( _ ) = > {
2020-11-22 23:58:46 +00:00
let filtered_tz = TZ_VARIANTS
. iter ( )
2021-06-27 15:31:54 +00:00
. filter ( | tz | {
2021-09-10 23:14:23 +00:00
timezone . contains ( & tz . to_string ( ) )
| | tz . to_string ( ) . contains ( timezone )
| | levenshtein ( & tz . to_string ( ) , timezone ) < 4
2021-06-27 15:31:54 +00:00
} )
2020-11-22 23:58:46 +00:00
. take ( 25 )
2021-06-27 15:31:54 +00:00
. map ( | t | t . to_owned ( ) )
. collect ::< Vec < Tz > > ( ) ;
let fields = filtered_tz . iter ( ) . map ( | tz | {
(
tz . to_string ( ) ,
format! (
" 🕗 `{}` " ,
2021-07-16 20:28:51 +00:00
Utc ::now ( ) . with_timezone ( tz ) . format ( " %H:%M " ) . to_string ( )
2021-06-27 15:31:54 +00:00
) ,
true ,
)
} ) ;
2020-11-22 23:58:46 +00:00
2021-09-10 23:14:23 +00:00
let _ = invoke
. respond (
ctx . http . clone ( ) ,
CreateGenericResponse ::new ( ) . embed ( | e | {
e . title ( " Timezone Not Recognized " )
. description ( " Possibly you meant one of the following timezones, otherwise click [here](https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee): " )
2020-11-22 23:58:46 +00:00
. color ( * THEME_COLOR )
2021-06-27 15:31:54 +00:00
. fields ( fields )
2020-11-22 23:58:46 +00:00
. footer ( | f | f . text ( footer_text ) )
. url ( " https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee " )
2021-09-10 23:14:23 +00:00
} ) ,
)
2020-10-12 20:01:27 +00:00
. await ;
2020-08-29 17:07:15 +00:00
}
2020-08-27 20:37:44 +00:00
}
2020-10-12 20:01:27 +00:00
} else {
2021-09-10 23:14:23 +00:00
let popular_timezones = ctx . data . read ( ) . await . get ::< PopularTimezones > ( ) . cloned ( ) . unwrap ( ) ;
2020-10-11 00:42:19 +00:00
2020-11-22 23:58:46 +00:00
let popular_timezones_iter = popular_timezones . iter ( ) . map ( | t | {
(
2021-01-19 12:19:20 +00:00
t . to_string ( ) ,
2021-09-10 23:14:23 +00:00
format! ( " 🕗 ` {} ` " , Utc ::now ( ) . with_timezone ( t ) . format ( " %H:%M " ) . to_string ( ) ) ,
2020-11-23 14:11:57 +00:00
true ,
2020-11-22 23:58:46 +00:00
)
} ) ;
2021-09-10 23:14:23 +00:00
let _ = invoke
. respond (
ctx . http . clone ( ) ,
CreateGenericResponse ::new ( ) . embed ( | e | {
e . title ( " Timezone Usage " )
. description (
" **Usage:**
` / timezone Name `
* * Example :* *
` / timezone Europe / London `
You may want to use one of the popular timezones below , otherwise click [ here ] ( https ://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee):",
)
2020-11-22 23:58:46 +00:00
. color ( * THEME_COLOR )
. fields ( popular_timezones_iter )
. footer ( | f | f . text ( footer_text ) )
. url ( " https://gist.github.com/JellyWX/913dfc8b63d45192ad6cb54c829324ee " )
2021-09-10 23:14:23 +00:00
} ) ,
2020-11-23 14:11:57 +00:00
)
. await ;
2020-08-29 17:07:15 +00:00
}
2020-08-27 11:15:20 +00:00
}
2020-08-29 19:57:11 +00:00
2021-09-10 23:14:23 +00:00
#[ command( " prefix " ) ]
#[ description( " Configure a prefix for text-based commands (deprecated) " ) ]
2020-09-01 17:48:40 +00:00
#[ supports_dm(false) ]
2021-09-10 23:14:23 +00:00
#[ required_permissions(Restricted) ]
async fn prefix ( ctx : & Context , invoke : & ( dyn CommandInvoke + Send + Sync ) , args : String ) {
let pool = ctx . data . read ( ) . await . get ::< SQLPool > ( ) . cloned ( ) . unwrap ( ) ;
2020-10-12 20:01:27 +00:00
2021-09-10 23:14:23 +00:00
let guild_data = ctx . guild_data ( invoke . guild_id ( ) . unwrap ( ) ) . await . unwrap ( ) ;
2020-08-29 19:57:11 +00:00
if args . len ( ) > 5 {
2021-09-10 23:14:23 +00:00
let _ = invoke
. respond (
ctx . http . clone ( ) ,
CreateGenericResponse ::new ( ) . content ( " Please select a prefix under 5 characters " ) ,
)
2020-10-12 20:01:27 +00:00
. await ;
} else if args . is_empty ( ) {
2021-09-10 23:14:23 +00:00
let _ = invoke
. respond (
ctx . http . clone ( ) ,
CreateGenericResponse ::new ( )
. content ( " Please use this command as `@reminder-bot prefix <prefix>` " ) ,
)
2020-10-12 20:01:27 +00:00
. await ;
} else {
2021-05-23 09:59:06 +00:00
guild_data . write ( ) . await . prefix = args ;
guild_data . read ( ) . await . commit_changes ( & pool ) . await ;
2020-08-29 19:57:11 +00:00
2021-09-10 23:14:23 +00:00
let _ = invoke
. respond (
ctx . http . clone ( ) ,
CreateGenericResponse ::new ( )
. content ( format! ( " Prefix changed to {} " , guild_data . read ( ) . await . prefix ) ) ,
)
. await ;
2020-08-29 19:57:11 +00:00
}
}
2020-09-01 18:00:56 +00:00
2021-09-10 23:14:23 +00:00
#[ command( " restrict " ) ]
#[ description( " Configure which roles can use commands on the bot " ) ]
#[ arg(
name = " role " ,
description = " The role to configure command permissions for " ,
kind = " Role " ,
required = true
) ]
2020-09-01 18:00:56 +00:00
#[ supports_dm(false) ]
2021-09-10 23:14:23 +00:00
#[ required_permissions(Restricted) ]
async fn restrict (
ctx : & Context ,
invoke : & ( dyn CommandInvoke + Send + Sync ) ,
args : HashMap < String , String > ,
) {
let pool = ctx . data . read ( ) . await . get ::< SQLPool > ( ) . cloned ( ) . unwrap ( ) ;
let framework = ctx . data . read ( ) . await . get ::< RegexFramework > ( ) . cloned ( ) . unwrap ( ) ;
let role = RoleId ( args . get ( " role " ) . unwrap ( ) . parse ::< u64 > ( ) . unwrap ( ) ) ;
let restricted_commands =
sqlx ::query! ( " SELECT command FROM command_restrictions WHERE role_id = ? " , role . 0 )
. fetch_all ( & pool )
. await
. unwrap ( )
. iter ( )
. map ( | row | row . command . clone ( ) )
2020-10-12 20:01:27 +00:00
. collect ::< Vec < String > > ( ) ;
2020-09-02 16:13:17 +00:00
2021-09-10 23:14:23 +00:00
let restrictable_commands = framework
. commands
. iter ( )
. filter ( | c | c . required_permissions = = PermissionLevel ::Managed )
. map ( | c | c . names [ 0 ] . to_string ( ) )
. collect ::< Vec < String > > ( ) ;
2020-09-02 16:13:17 +00:00
2021-09-10 23:14:23 +00:00
let len = restrictable_commands . len ( ) ;
2020-10-11 17:56:27 +00:00
2021-09-10 23:14:23 +00:00
invoke
. respond (
ctx . http . clone ( ) ,
CreateGenericResponse ::new ( )
. content ( format! ( " Select the commands to allow to {} from below: " , role . mention ( ) ) )
. components ( | c | {
c . create_action_row ( | row | {
row . create_select_menu ( | select | {
select
. custom_id ( " test_id " )
. options ( | options | {
for command in restrictable_commands {
options . create_option ( | opt | {
opt . label ( & command ) . value ( & command ) . default_selection (
restricted_commands . contains ( & command ) ,
)
} ) ;
}
options
} )
. min_values ( 0 )
. max_values ( len as u64 )
} )
} )
} ) ,
2020-10-12 20:01:27 +00:00
)
. await
. unwrap ( ) ;
2020-09-01 18:00:56 +00:00
}
2020-09-03 23:29:19 +00:00
2021-09-10 23:14:23 +00:00
/*
2020-10-26 19:50:51 +00:00
#[ command( " alias " ) ]
2020-09-03 23:29:19 +00:00
#[ supports_dm(false) ]
#[ permission_level(Managed) ]
2020-10-26 11:10:00 +00:00
async fn alias ( ctx : & Context , msg : & Message , args : String ) {
2020-12-18 11:46:22 +00:00
let ( pool , lm ) = get_ctx_data ( & ctx ) . await ;
2020-09-03 23:29:19 +00:00
2020-11-22 12:09:57 +00:00
let language = UserData ::language_of ( & msg . author , & pool ) . await ;
2020-09-03 23:29:19 +00:00
let guild_id = msg . guild_id . unwrap ( ) . as_u64 ( ) . to_owned ( ) ;
let matches_opt = REGEX_ALIAS . captures ( & args ) ;
if let Some ( matches ) = matches_opt {
let name = matches . name ( " name " ) . unwrap ( ) . as_str ( ) ;
let command_opt = matches . name ( " cmd " ) . map ( | m | m . as_str ( ) ) ;
match name {
" list " = > {
let aliases = sqlx ::query! (
"
SELECT name , command FROM command_aliases WHERE guild_id = ( SELECT id FROM guilds WHERE guild = ? )
2020-10-12 20:01:27 +00:00
" ,
guild_id
)
. fetch_all ( & pool )
. await
. unwrap ( ) ;
2020-09-03 23:29:19 +00:00
2020-10-12 20:01:27 +00:00
let content = iter ::once ( " Aliases: " . to_string ( ) ) . chain (
aliases
. iter ( )
. map ( | row | format! ( " ** {} **: ` {} ` " , row . name , row . command ) ) ,
) ;
2020-09-28 15:11:27 +00:00
2020-10-01 17:07:27 +00:00
let _ = msg . channel_id . say_lines ( & ctx , content ) . await ;
2020-10-12 20:01:27 +00:00
}
2020-09-03 23:29:19 +00:00
" remove " = > {
if let Some ( command ) = command_opt {
2020-09-28 15:11:27 +00:00
let deleted_count = sqlx ::query! (
"
SELECT COUNT ( 1 ) AS count FROM command_aliases WHERE name = ? AND guild_id = ( SELECT id FROM guilds WHERE guild = ? )
" , command, guild_id)
. fetch_one ( & pool )
. await
. unwrap ( ) ;
2020-09-03 23:29:19 +00:00
sqlx ::query! (
"
DELETE FROM command_aliases WHERE name = ? AND guild_id = ( SELECT id FROM guilds WHERE guild = ? )
2020-10-12 20:01:27 +00:00
" ,
command ,
guild_id
)
. execute ( & pool )
. await
. unwrap ( ) ;
2020-09-03 23:29:19 +00:00
2020-11-22 01:31:50 +00:00
let content = lm
2020-11-22 12:09:57 +00:00
. get ( & language , " alias/removed " )
2020-10-12 20:01:27 +00:00
. replace ( " {count} " , & deleted_count . count . to_string ( ) ) ;
2020-09-28 15:11:27 +00:00
let _ = msg . channel_id . say ( & ctx , content ) . await ;
2020-10-12 20:01:27 +00:00
} else {
let _ = msg
. channel_id
2020-11-22 12:09:57 +00:00
. say ( & ctx , lm . get ( & language , " alias/help " ) )
2020-10-12 20:01:27 +00:00
. await ;
2020-09-03 23:29:19 +00:00
}
2020-10-12 20:01:27 +00:00
}
2020-09-03 23:29:19 +00:00
name = > {
if let Some ( command ) = command_opt {
let res = sqlx ::query! (
"
INSERT INTO command_aliases ( guild_id , name , command ) VALUES ( ( SELECT id FROM guilds WHERE guild = ? ) , ? , ? )
" , guild_id, name, command)
. execute ( & pool )
. await ;
if res . is_err ( ) {
sqlx ::query! (
"
UPDATE command_aliases SET command = ? WHERE guild_id = ( SELECT id FROM guilds WHERE guild = ? ) AND name = ?
" , command, guild_id, name)
. execute ( & pool )
. await
. unwrap ( ) ;
}
2020-11-22 12:09:57 +00:00
let content = lm . get ( & language , " alias/created " ) . replace ( " {name} " , name ) ;
2020-09-28 15:11:27 +00:00
let _ = msg . channel_id . say ( & ctx , content ) . await ;
2020-10-12 20:01:27 +00:00
} else {
2020-09-03 23:29:19 +00:00
match sqlx ::query! (
"
2020-09-04 20:21:47 +00:00
SELECT command FROM command_aliases WHERE guild_id = ( SELECT id FROM guilds WHERE guild = ? ) AND name = ?
2020-09-03 23:29:19 +00:00
" , guild_id, name)
. fetch_one ( & pool )
. await {
Ok ( row ) = > {
2020-09-04 20:21:47 +00:00
let framework = ctx . data . read ( ) . await
. get ::< FrameworkCtx > ( ) . cloned ( ) . expect ( " Could not get FrameworkCtx from data " ) ;
let mut new_msg = msg . clone ( ) ;
2021-09-02 22:38:12 +00:00
new_msg . content = format! ( " <@ {} > {} " , & ctx . cache . current_user_id ( ) , row . command ) ;
2021-05-13 19:20:53 +00:00
new_msg . id = MessageId ( 0 ) ;
2020-09-04 20:21:47 +00:00
framework . dispatch ( ctx . clone ( ) , new_msg ) . await ;
2020-09-03 23:29:19 +00:00
} ,
Err ( _ ) = > {
2020-11-22 12:09:57 +00:00
let content = lm . get ( & language , " alias/not_found " ) . replace ( " {name} " , name ) ;
2020-09-28 15:11:27 +00:00
let _ = msg . channel_id . say ( & ctx , content ) . await ;
2020-09-03 23:29:19 +00:00
} ,
}
}
}
}
2020-10-12 20:01:27 +00:00
} else {
2021-04-12 21:33:02 +00:00
let prefix = ctx . prefix ( msg . guild_id ) . await ;
2020-09-28 12:42:20 +00:00
2020-12-18 17:41:36 +00:00
command_help ( ctx , msg , lm , & prefix , & language , " alias " ) . await ;
2020-09-03 23:29:19 +00:00
}
}
2021-09-10 23:14:23 +00:00
* /