2020-04-29 22:09:31 +00:00
#[ macro_use ]
extern crate lazy_static ;
2020-04-13 23:22:31 +00:00
use serenity ::{
2020-04-26 00:51:51 +00:00
client ::{
2020-04-26 22:12:31 +00:00
bridge ::{
gateway ::GatewayIntents ,
voice ::ClientVoiceManager ,
} ,
Client , Context ,
2020-04-26 00:51:51 +00:00
} ,
framework ::standard ::{
2020-04-30 22:57:22 +00:00
Args , CommandResult , CheckResult , StandardFramework , Reason ,
2020-04-26 22:12:31 +00:00
macros ::{
2020-04-30 22:57:22 +00:00
command , group , check ,
2020-04-26 22:12:31 +00:00
}
2020-04-26 00:51:51 +00:00
} ,
model ::{
2020-05-19 20:03:07 +00:00
channel ::Message ,
guild ::Guild ,
2020-04-30 00:21:34 +00:00
id ::{
GuildId ,
RoleId ,
} ,
2020-05-19 20:03:07 +00:00
voice ::VoiceState ,
2020-04-26 00:51:51 +00:00
} ,
2020-04-28 00:24:06 +00:00
prelude ::{
Mutex as SerenityMutex ,
*
} ,
2020-05-18 22:44:51 +00:00
voice ::{
2020-05-19 15:29:57 +00:00
AudioSource ,
2020-05-18 22:44:51 +00:00
ffmpeg ,
2020-05-19 15:29:57 +00:00
Handler as VoiceHandler ,
pcm ,
2020-05-18 22:44:51 +00:00
} ,
2020-04-13 23:22:31 +00:00
} ;
use sqlx ::{
Pool ,
2020-04-26 22:12:31 +00:00
mysql ::{
MySqlPool ,
MySqlConnection ,
}
2020-04-13 23:22:31 +00:00
} ;
use dotenv ::dotenv ;
2020-04-26 22:12:31 +00:00
use tokio ::{
fs ::File ,
2020-05-18 22:44:51 +00:00
io ::empty ,
2020-04-29 01:20:52 +00:00
process ::Command ,
2020-05-19 15:29:57 +00:00
sync ::{
Mutex ,
MutexGuard
} ,
2020-05-18 22:44:51 +00:00
time ,
2020-04-26 22:12:31 +00:00
} ;
use std ::{
2020-05-18 22:44:51 +00:00
collections ::HashSet ,
2020-04-26 22:12:31 +00:00
env ,
path ::Path ,
sync ::Arc ,
2020-04-29 01:20:52 +00:00
time ::Duration ,
2020-04-26 22:12:31 +00:00
} ;
2020-04-29 01:20:52 +00:00
use std ::fmt ::Formatter ;
2020-04-13 23:22:31 +00:00
struct SQLPool ;
impl TypeMapKey for SQLPool {
type Value = Pool < MySqlConnection > ;
}
2020-04-26 00:51:51 +00:00
struct VoiceManager ;
impl TypeMapKey for VoiceManager {
2020-04-28 00:24:06 +00:00
type Value = Arc < SerenityMutex < ClientVoiceManager > > ;
2020-04-26 00:51:51 +00:00
}
2020-05-18 22:44:51 +00:00
struct VoiceGuilds ;
impl TypeMapKey for VoiceGuilds {
type Value = Arc < Mutex < HashSet < GuildId > > > ;
}
2020-04-28 00:24:06 +00:00
static THEME_COLOR : u32 = 0x00e0f3 ;
2020-04-29 22:09:31 +00:00
lazy_static! {
static ref MAX_SOUNDS : u32 = {
dotenv ( ) . unwrap ( ) ;
env ::var ( " MAX_SOUNDS " ) . unwrap ( ) . parse ::< u32 > ( ) . unwrap ( )
} ;
2020-04-30 00:21:34 +00:00
static ref PATREON_GUILD : u64 = {
dotenv ( ) . unwrap ( ) ;
env ::var ( " PATREON_GUILD " ) . unwrap ( ) . parse ::< u64 > ( ) . unwrap ( )
} ;
static ref PATREON_ROLE : u64 = {
dotenv ( ) . unwrap ( ) ;
env ::var ( " PATREON_ROLE " ) . unwrap ( ) . parse ::< u64 > ( ) . unwrap ( )
} ;
2020-04-29 22:09:31 +00:00
}
2020-04-13 23:22:31 +00:00
#[ group ]
2020-05-19 20:03:07 +00:00
#[ commands(info, help, list_sounds, change_public, search_sounds, show_popular_sounds, show_random_sounds, set_greet_sound) ]
2020-04-30 22:57:22 +00:00
struct AllUsers ;
#[ group ]
2020-05-19 20:03:07 +00:00
#[ commands(play, upload_new_sound, change_volume, delete_sound) ]
2020-04-30 22:57:22 +00:00
#[ checks(role_check) ]
struct RoleManagedUsers ;
#[ group ]
2020-05-16 20:28:47 +00:00
#[ commands(change_prefix, set_allowed_roles) ]
2020-04-30 22:57:22 +00:00
#[ checks(permission_check) ]
struct PermissionManagedUsers ;
#[ check ]
#[ name( " role_check " ) ]
2020-05-16 23:40:35 +00:00
async fn role_check ( ctx : & Context , msg : & Message , _args : & mut Args ) -> CheckResult {
2020-04-30 22:57:22 +00:00
2020-05-16 23:40:35 +00:00
async fn check_for_roles ( ctx : & & Context , msg : & & Message ) -> Result < ( ) , Box < dyn std ::error ::Error > > {
2020-04-30 22:57:22 +00:00
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
let user_member = msg . member ( & ctx ) . await ;
match user_member {
Some ( member ) = > {
let user_roles : String = member . roles
. iter ( )
. map ( | r | ( * r . as_u64 ( ) ) . to_string ( ) )
. collect ::< Vec < String > > ( )
. join ( " , " ) ;
let guild_id = * msg . guild_id . unwrap ( ) . as_u64 ( ) ;
let res = sqlx ::query! (
"
SELECT COUNT ( 1 ) as count
FROM roles
WHERE guild_id = ? AND role IN ( ? )
" ,
guild_id , user_roles
) . fetch_one ( & pool ) . await ? ;
if res . count > 0 {
Ok ( ( ) )
}
else {
Err ( Box ::new ( ErrorTypes ::NotEnoughRoles ) )
}
}
None = > {
Err ( Box ::new ( ErrorTypes ::NotEnoughRoles ) )
}
}
}
if check_for_roles ( & ctx , & msg ) . await . is_ok ( ) {
CheckResult ::Success
}
else {
perform_permission_check ( ctx , & msg ) . await
}
}
#[ check ]
#[ name( " permission_check " ) ]
2020-05-16 23:40:35 +00:00
async fn permission_check ( ctx : & Context , msg : & Message , _args : & mut Args ) -> CheckResult {
2020-04-30 22:57:22 +00:00
perform_permission_check ( ctx , & msg ) . await
}
async fn perform_permission_check ( ctx : & Context , msg : & & Message ) -> CheckResult {
if let Some ( guild_id ) = msg . guild_id {
if let Ok ( member ) = guild_id . member ( ctx . clone ( ) , msg . author . id ) . await {
if let Ok ( perms ) = member . permissions ( ctx ) . await {
if perms . manage_guild ( ) {
return CheckResult ::Success
}
}
}
}
CheckResult ::Failure ( Reason ::User ( String ::from ( " User needs `Manage Guild` permission " ) ) )
}
2020-04-26 00:51:51 +00:00
struct Sound {
name : String ,
id : u32 ,
2020-04-29 01:20:52 +00:00
plays : u32 ,
public : bool ,
server_id : u64 ,
uploader_id : u64 ,
2020-04-26 00:51:51 +00:00
src : Vec < u8 > ,
}
2020-04-13 23:22:31 +00:00
2020-04-29 01:20:52 +00:00
#[ derive(Debug) ]
enum ErrorTypes {
InvalidFile ,
2020-04-30 22:57:22 +00:00
NotEnoughRoles ,
2020-04-29 01:20:52 +00:00
}
impl std ::error ::Error for ErrorTypes { }
impl std ::fmt ::Display for ErrorTypes {
fn fmt ( & self , f : & mut Formatter < '_ > ) -> std ::fmt ::Result {
write! ( f , " ErrorTypes " )
}
}
impl Sound {
2020-05-16 20:28:47 +00:00
async fn search_for_sound ( query : & str , guild_id : u64 , user_id : u64 , db_pool : MySqlPool , strict : bool ) -> Result < Vec < Sound > , sqlx ::Error > {
2020-04-29 01:20:52 +00:00
fn extract_id ( s : & str ) -> Option < u32 > {
2020-05-16 23:40:35 +00:00
if s . len ( ) > 3 & & s . to_lowercase ( ) . starts_with ( " id: " ) {
2020-04-29 01:20:52 +00:00
match s [ 3 .. ] . parse ::< u32 > ( ) {
Ok ( id ) = > Some ( id ) ,
Err ( _ ) = > None
}
}
else {
None
}
}
2020-05-16 23:40:35 +00:00
if let Some ( id ) = extract_id ( & query ) {
2020-04-29 01:20:52 +00:00
let sound = sqlx ::query_as_unchecked! (
Self ,
"
SELECT *
FROM sounds
WHERE id = ? AND (
public = 1 OR
uploader_id = ? OR
server_id = ?
)
" ,
id , user_id , guild_id
)
2020-05-16 20:28:47 +00:00
. fetch_all ( & db_pool )
2020-04-29 01:20:52 +00:00
. await ? ;
Ok ( sound )
}
else {
let name = query ;
2020-05-16 20:28:47 +00:00
let sound ;
2020-04-29 01:20:52 +00:00
2020-05-16 20:28:47 +00:00
if strict {
sound = sqlx ::query_as_unchecked! (
Self ,
"
2020-04-29 01:20:52 +00:00
SELECT *
FROM sounds
WHERE name = ? AND (
public = 1 OR
uploader_id = ? OR
server_id = ?
)
ORDER BY rand ( ) , public = 1 , server_id = ? , uploader_id = ?
2020-05-16 20:28:47 +00:00
" ,
name , user_id , guild_id , guild_id , user_id
)
. fetch_all ( & db_pool )
. await ? ;
}
else {
sound = sqlx ::query_as_unchecked! (
Self ,
"
SELECT *
FROM sounds
WHERE name LIKE CONCAT ( '%' , ? , '%' ) AND (
public = 1 OR
uploader_id = ? OR
server_id = ?
)
ORDER BY rand ( ) , public = 1 , server_id = ? , uploader_id = ?
" ,
name , user_id , guild_id , guild_id , user_id
)
. fetch_all ( & db_pool )
. await ? ;
}
2020-04-29 01:20:52 +00:00
Ok ( sound )
}
}
2020-05-19 15:29:57 +00:00
async fn store_sound_source ( & self ) -> Result < Box < dyn AudioSource > , Box < dyn std ::error ::Error > > {
2020-04-29 01:20:52 +00:00
let caching_location = env ::var ( " CACHING_LOCATION " ) . unwrap_or ( String ::from ( " /tmp " ) ) ;
let path_name = format! ( " {} /sound- {} " , caching_location , self . id ) ;
let path = Path ::new ( & path_name ) ;
if ! path . exists ( ) {
use tokio ::prelude ::* ;
let mut file = File ::create ( & path ) . await ? ;
file . write_all ( self . src . as_ref ( ) ) . await ? ;
}
2020-05-19 15:29:57 +00:00
Ok ( ffmpeg ( path_name ) . await ? )
2020-04-29 01:20:52 +00:00
}
2020-05-19 15:29:57 +00:00
async fn count_user_sounds ( user_id : u64 , db_pool : MySqlPool ) -> Result < u32 , sqlx ::error ::Error > {
2020-05-18 00:18:37 +00:00
let c = sqlx ::query! (
"
SELECT COUNT ( 1 ) as count
FROM sounds
WHERE uploader_id = ?
" ,
user_id
)
. fetch_one ( & db_pool )
. await ? . count ;
Ok ( c as u32 )
}
async fn count_named_user_sounds ( user_id : u64 , name : & String , db_pool : MySqlPool ) -> Result < u32 , sqlx ::error ::Error > {
let c = sqlx ::query! (
"
SELECT COUNT ( 1 ) as count
FROM sounds
WHERE
uploader_id = ? AND
name = ?
" ,
user_id , name
)
. fetch_one ( & db_pool )
. await ? . count ;
Ok ( c as u32 )
}
async fn set_as_greet ( & self , user_id : u64 , db_pool : MySqlPool ) -> Result < ( ) , Box < dyn std ::error ::Error > > {
sqlx ::query! (
"
UPDATE users
SET
join_sound_id = ?
WHERE
user = ?
" ,
self . id , user_id
)
. execute ( & db_pool )
. await ? ;
Ok ( ( ) )
}
2020-04-29 01:20:52 +00:00
async fn commit ( & self , db_pool : MySqlPool ) -> Result < ( ) , Box < dyn std ::error ::Error > > {
sqlx ::query! (
"
UPDATE sounds
SET
plays = ? ,
public = ?
WHERE
id = ?
" ,
self . plays , self . public , self . id
)
. execute ( & db_pool )
. await ? ;
Ok ( ( ) )
}
2020-05-16 20:28:47 +00:00
async fn delete ( & self , db_pool : MySqlPool ) -> Result < ( ) , Box < dyn std ::error ::Error > > {
sqlx ::query! (
"
DELETE
FROM sounds
WHERE id = ?
" ,
self . id
)
. execute ( & db_pool )
. await ? ;
Ok ( ( ) )
}
2020-04-29 01:20:52 +00:00
async fn create_anon ( name : & str , src_url : & str , server_id : u64 , user_id : u64 , db_pool : MySqlPool ) -> Result < u64 , Box < dyn std ::error ::Error + Send > > {
async fn process_src ( src_url : & str ) -> Option < Vec < u8 > > {
let future = Command ::new ( " ffmpeg " )
. arg ( " -i " )
. arg ( src_url )
. arg ( " -loglevel " )
. arg ( " error " )
. arg ( " -b:a " )
. arg ( " 28000 " )
. arg ( " -f " )
. arg ( " opus " )
2020-04-30 22:57:22 +00:00
. arg ( " -fs " )
. arg ( " 1048576 " )
2020-04-29 01:20:52 +00:00
. arg ( " pipe:1 " )
. output ( ) ;
let output = future . await ;
match output {
Ok ( out ) = > {
2020-04-30 22:57:22 +00:00
if out . status . success ( ) {
2020-04-29 01:20:52 +00:00
Some ( out . stdout )
}
else {
None
}
}
Err ( _ ) = > None ,
}
}
let source = process_src ( src_url ) . await ;
match source {
Some ( data ) = > {
match sqlx ::query! (
"
INSERT INTO sounds ( name , server_id , uploader_id , public , src )
2020-05-19 15:29:57 +00:00
VALUES ( ? , ? , ? , 1 , ? )
2020-04-29 01:20:52 +00:00
" ,
name , server_id , user_id , data
)
. execute ( & db_pool )
. await {
Ok ( u ) = > Ok ( u ) ,
Err ( e ) = > Err ( Box ::new ( e ) )
}
}
None = > Err ( Box ::new ( ErrorTypes ::InvalidFile ) )
}
}
2020-05-04 00:07:47 +00:00
async fn get_user_sounds ( user_id : u64 , db_pool : MySqlPool ) -> Result < Vec < Sound > , Box < dyn std ::error ::Error > > {
let sounds = sqlx ::query_as_unchecked! (
Sound ,
"
SELECT *
FROM sounds
WHERE uploader_id = ?
" ,
user_id
) . fetch_all ( & db_pool ) . await ? ;
Ok ( sounds )
}
async fn get_guild_sounds ( guild_id : u64 , db_pool : MySqlPool ) -> Result < Vec < Sound > , Box < dyn std ::error ::Error > > {
let sounds = sqlx ::query_as_unchecked! (
Sound ,
"
SELECT *
FROM sounds
WHERE server_id = ?
" ,
guild_id
) . fetch_all ( & db_pool ) . await ? ;
Ok ( sounds )
}
2020-04-29 01:20:52 +00:00
}
2020-04-28 00:24:06 +00:00
struct GuildData {
2020-05-19 15:29:57 +00:00
pub id : u64 ,
2020-04-28 16:17:32 +00:00
pub name : Option < String > ,
pub prefix : String ,
pub volume : u8 ,
2020-04-28 00:24:06 +00:00
}
impl GuildData {
2020-04-28 16:17:32 +00:00
async fn get_from_id ( guild_id : u64 , db_pool : MySqlPool ) -> Option < GuildData > {
2020-04-28 00:24:06 +00:00
let guild = sqlx ::query_as! (
GuildData ,
"
SELECT *
2020-05-19 15:29:57 +00:00
FROM servers
WHERE id = ?
2020-04-28 00:24:06 +00:00
" , guild_id
)
. fetch_one ( & db_pool )
2020-04-28 16:17:32 +00:00
. await ;
match guild {
Ok ( guild ) = > Some ( guild ) ,
2020-04-28 00:24:06 +00:00
2020-04-28 16:17:32 +00:00
Err ( _ ) = > None ,
}
2020-04-28 00:24:06 +00:00
}
2020-05-16 23:40:35 +00:00
async fn create_from_guild ( guild : Guild , db_pool : MySqlPool ) -> Result < GuildData , Box < dyn std ::error ::Error > > {
2020-05-16 20:28:47 +00:00
sqlx ::query! (
2020-04-28 00:24:06 +00:00
"
INSERT INTO servers ( id , name )
2020-05-19 15:29:57 +00:00
VALUES ( ? , ? )
2020-04-28 00:24:06 +00:00
" , guild.id.as_u64(), guild.name
)
. execute ( & db_pool )
. await ? ;
Ok ( GuildData {
id : * guild . id . as_u64 ( ) ,
2020-04-28 16:17:32 +00:00
name : Some ( guild . name . clone ( ) ) ,
2020-04-28 00:24:06 +00:00
prefix : String ::from ( " ? " ) ,
volume : 100 ,
} )
}
async fn commit ( & self , db_pool : MySqlPool ) -> Result < ( ) , Box < dyn std ::error ::Error > > {
sqlx ::query! (
"
UPDATE servers
SET
name = ? ,
prefix = ? ,
volume = ?
WHERE
id = ?
" ,
self . name , self . prefix , self . volume , self . id
)
. execute ( & db_pool )
. await ? ;
Ok ( ( ) )
}
}
2020-04-13 23:22:31 +00:00
// create event handler for bot
struct Handler ;
2020-04-26 22:12:31 +00:00
#[ serenity::async_trait ]
2020-05-19 20:03:07 +00:00
impl EventHandler for Handler {
async fn voice_state_update ( & self , ctx : Context , guild_id_opt : Option < GuildId > , old : Option < VoiceState > , new : VoiceState ) {
if let ( Some ( guild_id ) , Some ( user_channel ) ) = ( guild_id_opt , new . channel_id ) {
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
if old . is_none ( ) {
let join_id_res = sqlx ::query! (
"
SELECT join_sound_id
FROM users
WHERE user = ? AND join_sound_id IS NOT NULL
" ,
new . user_id . as_u64 ( )
)
. fetch_one ( & pool )
. await ;
match join_id_res {
Ok ( join_id_record ) = > {
let join_id = join_id_record . join_sound_id ;
let mut sound = sqlx ::query_as_unchecked! (
Sound ,
"
SELECT *
FROM sounds
WHERE id = ?
" ,
join_id
)
. fetch_one ( & pool )
. await . unwrap ( ) ;
let voice_manager_lock = ctx . data . read ( ) . await
. get ::< VoiceManager > ( ) . cloned ( ) . expect ( " Could not get VoiceManager from data " ) ;
let mut voice_manager = voice_manager_lock . lock ( ) . await ;
let voice_guilds_lock = ctx . data . read ( ) . await
. get ::< VoiceGuilds > ( ) . cloned ( ) . expect ( " Could not get VoiceGuilds from data " ) ;
let voice_guilds = voice_guilds_lock . lock ( ) . await ;
let guild_data = GuildData ::get_from_id ( * guild_id . as_u64 ( ) , pool . clone ( ) ) . await . unwrap ( ) ;
match voice_manager . get_mut ( guild_id ) {
Some ( handler ) = > {
// play sound
play_audio ( & mut sound , guild_data , handler , voice_guilds , pool ) . await ;
}
None = > {
// try & join a voice channel
if let Some ( handler ) = voice_manager . join ( guild_id , user_channel ) {
play_audio ( & mut sound , guild_data , handler , voice_guilds , pool ) . await ;
}
}
}
}
Err ( _ ) = > { }
}
}
}
}
}
async fn play_audio ( sound : & mut Sound , guild : GuildData , handler : & mut VoiceHandler , mut voice_guilds : MutexGuard < '_ , HashSet < GuildId > > , pool : MySqlPool )
-> Result < ( ) , Box < dyn std ::error ::Error > > {
let audio = handler . play_only ( sound . store_sound_source ( ) . await ? ) ;
{
let mut locked = audio . lock ( ) . await ;
locked . volume ( guild . volume as f32 / 100.0 ) ;
}
sound . plays + = 1 ;
sound . commit ( pool ) . await ? ;
voice_guilds . insert ( GuildId ( guild . id ) ) ;
Ok ( ( ) )
}
2020-04-13 23:22:31 +00:00
// entry point
2020-04-26 00:51:51 +00:00
#[ tokio::main ]
2020-04-26 22:12:31 +00:00
async fn main ( ) -> Result < ( ) , Box < dyn std ::error ::Error > > {
dotenv ( ) ? ;
2020-04-13 23:22:31 +00:00
2020-05-18 22:44:51 +00:00
let voice_guilds = Arc ::new ( Mutex ::new ( HashSet ::new ( ) ) ) ;
2020-04-26 00:51:51 +00:00
let framework = StandardFramework ::new ( )
2020-05-18 22:44:51 +00:00
. configure ( | c | c
. dynamic_prefix ( | ctx , msg | Box ::pin ( async move {
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
2020-04-28 00:24:06 +00:00
2020-05-18 22:44:51 +00:00
println! ( " dynamic_prefix: acquired pool and now about to query " ) ;
2020-04-28 00:24:06 +00:00
2020-05-18 22:44:51 +00:00
match GuildData ::get_from_id ( * msg . guild_id . unwrap ( ) . as_u64 ( ) , pool ) . await {
Some ( guild ) = > Some ( guild . prefix ) ,
None = > Some ( String ::from ( " ? " ) )
}
} ) )
. allow_dm ( false )
. ignore_bots ( true )
. ignore_webhooks ( true )
)
2020-04-30 22:57:22 +00:00
. group ( & ALLUSERS_GROUP )
. group ( & ROLEMANAGEDUSERS_GROUP )
. group ( & PERMISSIONMANAGEDUSERS_GROUP ) ;
2020-04-13 23:22:31 +00:00
2020-05-16 23:40:35 +00:00
let mut client = Client ::new ( & env ::var ( " DISCORD_TOKEN " ) . expect ( " Missing token from environment " ) )
. intents ( GatewayIntents ::GUILD_VOICE_STATES | GatewayIntents ::GUILD_MESSAGES | GatewayIntents ::GUILDS )
. framework ( framework )
. event_handler ( Handler )
. await . expect ( " Error occurred creating client " ) ;
2020-04-13 23:22:31 +00:00
2020-04-26 00:51:51 +00:00
{
2020-04-26 22:12:31 +00:00
let pool = MySqlPool ::new ( & env ::var ( " DATABASE_URL " ) . expect ( " No database URL provided " ) ) . await . unwrap ( ) ;
2020-04-13 23:22:31 +00:00
2020-04-26 00:51:51 +00:00
let mut data = client . data . write ( ) . await ;
2020-04-13 23:22:31 +00:00
data . insert ::< SQLPool > ( pool ) ;
2020-04-26 00:51:51 +00:00
data . insert ::< VoiceManager > ( Arc ::clone ( & client . voice_manager ) ) ;
2020-05-18 22:44:51 +00:00
data . insert ::< VoiceGuilds > ( voice_guilds . clone ( ) ) ;
2020-04-13 23:22:31 +00:00
}
2020-05-18 22:44:51 +00:00
let cvm = Arc ::clone ( & client . voice_manager ) ;
// select on the client and client auto disconnector (when the client terminates, terminate the disconnector
tokio ::select! {
_ = client . start ( ) = > { }
_ = disconnect_from_inactive ( cvm , voice_guilds ) = > { }
} ;
2020-04-26 22:12:31 +00:00
Ok ( ( ) )
2020-04-26 00:51:51 +00:00
}
2020-05-18 22:44:51 +00:00
async fn disconnect_from_inactive ( voice_manager_mutex : Arc < SerenityMutex < ClientVoiceManager > > , voice_guilds : Arc < Mutex < HashSet < GuildId > > > ) {
loop {
2020-05-19 20:03:07 +00:00
time ::delay_for ( Duration ::from_secs ( 30 ) ) . await ;
2020-05-18 22:44:51 +00:00
let mut voice_guilds_acquired = voice_guilds . lock ( ) . await ;
let mut voice_manager = voice_manager_mutex . lock ( ) . await ;
let mut to_remove = HashSet ::new ( ) ;
for guild in voice_guilds_acquired . iter ( ) {
let manager_opt = voice_manager . get_mut ( guild ) ;
if let Some ( manager ) = manager_opt {
let audio = manager . play_returning ( pcm ( false , empty ( ) ) ) ;
2020-05-19 20:03:07 +00:00
time ::delay_for ( Duration ::from_millis ( 10 ) ) . await ;
2020-05-18 22:44:51 +00:00
if ! audio . lock ( ) . await . playing {
manager . leave ( ) ;
to_remove . insert ( guild . clone ( ) ) ;
}
}
2020-05-19 20:03:07 +00:00
else {
to_remove . insert ( guild . clone ( ) ) ;
}
2020-05-18 22:44:51 +00:00
}
for val in to_remove . iter ( ) {
voice_guilds_acquired . remove ( val ) ;
}
}
}
2020-04-28 16:17:32 +00:00
#[ command( " play " ) ]
#[ aliases( " p " ) ]
2020-05-16 23:40:35 +00:00
async fn play ( ctx : & Context , msg : & Message , args : Args ) -> CommandResult {
2020-05-19 15:29:57 +00:00
2020-04-26 22:12:31 +00:00
let guild = match msg . guild ( & ctx . cache ) . await {
Some ( guild ) = > guild ,
2020-04-26 00:51:51 +00:00
2020-04-26 22:12:31 +00:00
None = > {
return Ok ( ( ) ) ;
2020-04-26 00:51:51 +00:00
}
2020-04-26 22:12:31 +00:00
} ;
2020-05-16 23:40:35 +00:00
let guild_id = guild . id ;
2020-04-26 00:51:51 +00:00
2020-05-16 23:40:35 +00:00
let channel_to_join = guild
2020-04-26 22:12:31 +00:00
. voice_states . get ( & msg . author . id )
. and_then ( | voice_state | voice_state . channel_id ) ;
2020-04-26 00:51:51 +00:00
2020-04-26 22:12:31 +00:00
match channel_to_join {
Some ( user_channel ) = > {
let search_term = args . rest ( ) ;
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
2020-05-16 23:40:35 +00:00
let mut sound_vec = Sound ::search_for_sound (
2020-04-28 00:24:06 +00:00
search_term ,
* guild_id . as_u64 ( ) ,
* msg . author . id . as_u64 ( ) ,
2020-05-16 23:40:35 +00:00
pool . clone ( ) ,
2020-05-16 20:28:47 +00:00
true ) . await ? ;
2020-05-16 23:40:35 +00:00
let sound_res = sound_vec . first_mut ( ) ;
2020-04-26 22:12:31 +00:00
2020-04-28 00:24:06 +00:00
match sound_res {
2020-05-16 20:28:47 +00:00
Some ( sound ) = > {
2020-04-28 00:24:06 +00:00
let voice_manager_lock = ctx . data . read ( ) . await
. get ::< VoiceManager > ( ) . cloned ( ) . expect ( " Could not get VoiceManager from data " ) ;
2020-04-26 22:12:31 +00:00
2020-04-28 00:24:06 +00:00
let mut voice_manager = voice_manager_lock . lock ( ) . await ;
2020-04-26 22:12:31 +00:00
2020-05-18 22:44:51 +00:00
let voice_guilds_lock = ctx . data . read ( ) . await
. get ::< VoiceGuilds > ( ) . cloned ( ) . expect ( " Could not get VoiceGuilds from data " ) ;
2020-05-19 15:29:57 +00:00
let voice_guilds = voice_guilds_lock . lock ( ) . await ;
let guild_data = GuildData ::get_from_id ( * guild_id . as_u64 ( ) , pool . clone ( ) ) . await . unwrap ( ) ;
2020-05-18 22:44:51 +00:00
2020-04-28 00:24:06 +00:00
match voice_manager . get_mut ( guild_id ) {
2020-04-26 22:12:31 +00:00
Some ( handler ) = > {
2020-04-28 00:24:06 +00:00
// play sound
2020-05-19 15:29:57 +00:00
play_audio ( sound , guild_data , handler , voice_guilds , pool ) . await ? ;
2020-04-26 22:12:31 +00:00
}
None = > {
2020-04-28 00:24:06 +00:00
// try & join a voice channel
match voice_manager . join ( guild_id , user_channel ) {
Some ( handler ) = > {
2020-05-19 15:29:57 +00:00
play_audio ( sound , guild_data , handler , voice_guilds , pool ) . await ? ;
2020-04-28 00:24:06 +00:00
}
None = > {
msg . channel_id . say ( & ctx , " Failed to join channel " ) . await ? ;
}
} ;
2020-04-26 22:12:31 +00:00
}
2020-04-28 00:24:06 +00:00
}
}
2020-05-16 20:28:47 +00:00
None = > {
2020-04-28 00:24:06 +00:00
msg . channel_id . say ( & ctx , " Couldn't find sound by term provided " ) . await ? ;
2020-04-26 22:12:31 +00:00
}
}
}
None = > {
msg . channel_id . say ( & ctx , " You are not in a voice chat! " ) . await ? ;
2020-04-26 00:51:51 +00:00
}
}
2020-04-26 22:12:31 +00:00
Ok ( ( ) )
2020-04-13 23:22:31 +00:00
}
2020-04-28 00:24:06 +00:00
#[ command ]
2020-05-16 23:40:35 +00:00
async fn help ( ctx : & Context , msg : & Message , _args : Args ) -> CommandResult {
2020-04-28 00:24:06 +00:00
msg . channel_id . send_message ( & ctx , | m | m
. embed ( | e | e
. title ( " Help " )
. color ( THEME_COLOR )
. description ( " Please visit our website at https://soundfx.jellywx.com/help " ) ) ) . await ? ;
Ok ( ( ) )
}
#[ command ]
2020-05-16 23:40:35 +00:00
async fn info ( ctx : & Context , msg : & Message , _args : Args ) -> CommandResult {
2020-04-28 00:24:06 +00:00
msg . channel_id . send_message ( & ctx , | m | m
. embed ( | e | e
. title ( " Info " )
. color ( THEME_COLOR )
. description ( " Default prefix: `?`
Reset prefix : ` @ SoundFX prefix ? `
Invite me : https ://discordapp.com/oauth2/authorize?client_id=430384808200372245&scope=bot&permissions=36703232
* * Welcome to SoundFX ! * *
Developer : < @ 203532103185465344 >
Find me on https ://discord.jellywx.com/ and on https://github.com/JellyWX :)
* * An online dashboard is available ! * * Visit https ://soundfx.jellywx.com/dashboard
There is a maximum sound limit per user . This can be removed by donating at https ://patreon.com/jellywx"))).await?;
Ok ( ( ) )
}
2020-04-28 16:17:32 +00:00
#[ command( " volume " ) ]
2020-05-16 23:40:35 +00:00
async fn change_volume ( ctx : & Context , msg : & Message , mut args : Args ) -> CommandResult {
2020-04-28 16:17:32 +00:00
let guild = match msg . guild ( & ctx . cache ) . await {
Some ( guild ) = > guild ,
None = > {
return Ok ( ( ) ) ;
}
} ;
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
2020-05-16 23:40:35 +00:00
let mut guild_data_opt = GuildData ::get_from_id ( * guild . id . as_u64 ( ) , pool . clone ( ) ) . await ;
2020-04-28 16:17:32 +00:00
if guild_data_opt . is_none ( ) {
2020-05-16 23:40:35 +00:00
guild_data_opt = Some ( GuildData ::create_from_guild ( guild , pool . clone ( ) ) . await . unwrap ( ) )
2020-04-28 16:17:32 +00:00
}
let mut guild_data = guild_data_opt . unwrap ( ) ;
if args . len ( ) = = 1 {
match args . single ::< u8 > ( ) {
Ok ( volume ) = > {
guild_data . volume = volume ;
guild_data . commit ( pool ) . await ? ;
msg . channel_id . say ( & ctx , format! ( " Volume changed to {} % " , volume ) ) . await ? ;
}
Err ( _ ) = > {
msg . channel_id . say ( & ctx ,
format! ( " Current server volume: {vol} %. Change the volume with ``` {prefix} volume <new volume>``` " ,
vol = guild_data . volume , prefix = guild_data . prefix ) ) . await ? ;
}
}
}
else {
msg . channel_id . say ( & ctx ,
format! ( " Current server volume: {vol} %. Change the volume with ``` {prefix} volume <new volume>``` " ,
vol = guild_data . volume , prefix = guild_data . prefix ) ) . await ? ;
}
Ok ( ( ) )
}
2020-04-28 16:40:26 +00:00
#[ command( " prefix " ) ]
2020-05-16 23:40:35 +00:00
async fn change_prefix ( ctx : & Context , msg : & Message , mut args : Args ) -> CommandResult {
2020-04-28 16:40:26 +00:00
let guild = match msg . guild ( & ctx . cache ) . await {
Some ( guild ) = > guild ,
None = > {
return Ok ( ( ) ) ;
}
} ;
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
let mut guild_data ;
{
2020-05-16 23:40:35 +00:00
let mut guild_data_opt = GuildData ::get_from_id ( * guild . id . as_u64 ( ) , pool . clone ( ) ) . await ;
2020-04-28 16:40:26 +00:00
if guild_data_opt . is_none ( ) {
2020-05-16 23:40:35 +00:00
guild_data_opt = Some ( GuildData ::create_from_guild ( guild , pool . clone ( ) ) . await . unwrap ( ) )
2020-04-28 16:40:26 +00:00
}
guild_data = guild_data_opt . unwrap ( ) ;
}
if args . len ( ) = = 1 {
match args . single ::< String > ( ) {
Ok ( prefix ) = > {
if prefix . len ( ) < = 5 {
guild_data . prefix = prefix ;
guild_data . commit ( pool ) . await ? ;
msg . channel_id . say ( & ctx , format! ( " Prefix changed to ` {} ` " , guild_data . prefix ) ) . await ? ;
}
else {
msg . channel_id . say ( & ctx , " Prefix must be less than 5 characters long " ) . await ? ;
}
}
Err ( _ ) = > {
msg . channel_id . say ( & ctx , format! ( " Usage: ` {prefix} prefix <new prefix>` " , prefix = guild_data . prefix ) ) . await ? ;
}
}
}
else {
msg . channel_id . say ( & ctx , format! ( " Usage: ` {prefix} prefix <new prefix>` " , prefix = guild_data . prefix ) ) . await ? ;
}
Ok ( ( ) )
}
2020-04-29 01:20:52 +00:00
#[ command( " upload " ) ]
2020-05-16 23:40:35 +00:00
async fn upload_new_sound ( ctx : & Context , msg : & Message , args : Args ) -> CommandResult {
2020-05-15 21:04:22 +00:00
let new_name = args . rest ( ) . to_string ( ) ;
2020-04-29 01:20:52 +00:00
if ! new_name . is_empty ( ) & & new_name . len ( ) < = 20 {
2020-04-29 22:09:31 +00:00
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
2020-05-15 21:04:22 +00:00
// need to check the name is not currently in use by the user
let count_name = Sound ::count_named_user_sounds ( * msg . author . id . as_u64 ( ) , & new_name , pool . clone ( ) ) . await ? ;
if count_name > 0 {
msg . channel_id . say ( & ctx , " You are already using that name. Please choose a unique name for your upload. " ) . await ? ;
}
2020-04-29 01:20:52 +00:00
2020-05-15 21:04:22 +00:00
else {
// need to check how many sounds user currently has
let count = Sound ::count_user_sounds ( * msg . author . id . as_u64 ( ) , pool . clone ( ) ) . await ? ;
let mut permit_upload = true ;
2020-04-29 22:09:31 +00:00
2020-05-15 21:04:22 +00:00
// need to check if user is patreon or nah
if count > = * MAX_SOUNDS {
2020-05-16 23:40:35 +00:00
let patreon_guild_member = GuildId ( * PATREON_GUILD ) . member ( ctx , msg . author . id ) . await ? ;
2020-05-15 21:04:22 +00:00
if patreon_guild_member . roles . contains ( & RoleId ( * PATREON_ROLE ) ) {
permit_upload = true ;
}
else {
permit_upload = false ;
}
2020-04-30 00:21:34 +00:00
}
2020-04-29 01:20:52 +00:00
2020-05-15 21:04:22 +00:00
if permit_upload {
msg . channel_id . say ( & ctx , " Please now upload an audio file under 1MB in size (larger files will be automatically trimmed): " ) . await ? ;
let reply = msg . channel_id . await_reply ( & ctx )
. author_id ( msg . author . id )
. timeout ( Duration ::from_secs ( 30 ) )
. await ;
match reply {
Some ( reply_msg ) = > {
if reply_msg . attachments . len ( ) = = 1 {
match Sound ::create_anon (
& new_name ,
& reply_msg . attachments [ 0 ] . url ,
* msg . guild_id . unwrap ( ) . as_u64 ( ) ,
* msg . author . id . as_u64 ( ) ,
pool ) . await {
Ok ( _ ) = > {
msg . channel_id . say ( & ctx , " Sound has been uploaded " ) . await ? ;
}
2020-04-30 00:21:34 +00:00
2020-05-15 21:04:22 +00:00
Err ( _ ) = > {
msg . channel_id . say ( & ctx , " Sound failed to upload. " ) . await ? ;
}
2020-04-30 00:21:34 +00:00
}
2020-05-15 21:04:22 +00:00
} else {
msg . channel_id . say ( & ctx , " Please upload 1 attachment following your upload command. Aborted " ) . await ? ;
2020-04-29 01:20:52 +00:00
}
}
2020-05-15 21:04:22 +00:00
None = > {
msg . channel_id . say ( & ctx , " Upload timed out. Please redo the command " ) . await ? ;
}
2020-04-30 00:21:34 +00:00
}
2020-04-29 01:20:52 +00:00
}
2020-05-15 21:04:22 +00:00
else {
msg . channel_id . say (
& ctx ,
format! (
" You have reached the maximum number of sounds ({}). Either delete some with `?delete` or join our Patreon for unlimited uploads at **https://patreon.com/jellywx** " ,
* MAX_SOUNDS ,
) ) . await ? ;
}
2020-04-30 00:21:34 +00:00
}
2020-04-29 01:20:52 +00:00
}
else {
msg . channel_id . say ( & ctx , " Usage: `?upload <name>`. Please ensure the name provided is less than 20 characters in length " ) . await ? ;
}
Ok ( ( ) )
}
2020-04-29 22:09:31 +00:00
2020-05-16 20:28:47 +00:00
#[ command( " roles " ) ]
2020-05-16 23:40:35 +00:00
async fn set_allowed_roles ( ctx : & Context , msg : & Message , args : Args ) -> CommandResult {
2020-04-30 22:57:22 +00:00
if args . len ( ) = = 0 {
msg . channel_id . say ( & ctx , " Usage: `?roles <role mentions or anything else to disable>`. Current roles: " ) . await ? ;
}
else {
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
let guild_id = * msg . guild_id . unwrap ( ) . as_u64 ( ) ;
sqlx ::query! (
"
DELETE FROM roles
WHERE guild_id = ?
" ,
guild_id
) . execute ( & pool ) . await ? ;
if msg . mention_roles . len ( ) > 0 {
for role in msg . mention_roles . iter ( ) . map ( | r | * r . as_u64 ( ) ) {
sqlx ::query! (
"
INSERT INTO roles ( guild_id , role )
VALUES
( ? , ? )
" ,
guild_id , role
) . execute ( & pool ) . await ? ;
}
msg . channel_id . say ( & ctx , " Specified roles whitelisted " ) . await ? ;
}
else {
msg . channel_id . say ( & ctx , " Role whitelisting disabled " ) . await ? ;
}
}
Ok ( ( ) )
}
2020-05-04 00:07:47 +00:00
2020-05-15 00:25:46 +00:00
#[ command( " list " ) ]
2020-05-16 23:40:35 +00:00
async fn list_sounds ( ctx : & Context , msg : & Message , args : Args ) -> CommandResult {
2020-05-04 00:07:47 +00:00
let pool = ctx . data . read ( ) . await
2020-05-15 00:25:46 +00:00
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
2020-05-04 00:07:47 +00:00
let sounds ;
2020-05-15 00:25:46 +00:00
let mut message_buffer ;
2020-05-04 00:07:47 +00:00
if args . rest ( ) = = " me " {
sounds = Sound ::get_user_sounds ( * msg . author . id . as_u64 ( ) , pool ) . await ? ;
2020-05-15 00:25:46 +00:00
message_buffer = " All your sounds: " . to_string ( ) ;
2020-05-04 00:07:47 +00:00
}
else {
sounds = Sound ::get_guild_sounds ( * msg . guild_id . unwrap ( ) . as_u64 ( ) , pool ) . await ? ;
2020-05-15 00:25:46 +00:00
message_buffer = " All sounds on this server: " . to_string ( ) ;
}
for sound in sounds {
message_buffer . push_str ( format! ( " ** {} ** ( {} ), " , sound . name , if sound . public { " 🔓 " } else { " 🔒 " } ) . as_str ( ) ) ;
if message_buffer . len ( ) > 2000 {
msg . channel_id . say ( & ctx , message_buffer ) . await ? ;
message_buffer = " " . to_string ( ) ;
}
}
if message_buffer . len ( ) > 0 {
msg . channel_id . say ( & ctx , message_buffer ) . await ? ;
2020-05-04 00:07:47 +00:00
}
Ok ( ( ) )
}
2020-05-15 21:04:22 +00:00
#[ command( " public " ) ]
2020-05-16 23:40:35 +00:00
async fn change_public ( ctx : & Context , msg : & Message , args : Args ) -> CommandResult {
2020-05-15 21:04:22 +00:00
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
let uid = msg . author . id . as_u64 ( ) ;
2020-05-16 20:28:47 +00:00
let name = args . rest ( ) ;
let gid = * msg . guild_id . unwrap ( ) . as_u64 ( ) ;
2020-05-15 21:04:22 +00:00
2020-05-16 20:28:47 +00:00
let mut sound_vec = Sound ::search_for_sound ( name , gid , * uid , pool . clone ( ) , true ) . await ? ;
let sound_result = sound_vec . first_mut ( ) ;
2020-05-15 21:04:22 +00:00
match sound_result {
2020-05-16 20:28:47 +00:00
Some ( sound ) = > {
2020-05-15 21:04:22 +00:00
if sound . uploader_id ! = * uid {
msg . channel_id . say ( & ctx , " You can only change the availability of sounds you have uploaded. Use `?list me` to view your sounds " ) . await ? ;
}
else {
if sound . public {
sound . public = false ;
msg . channel_id . say ( & ctx , " Sound has been set to private 🔒 " ) . await ? ;
} else {
sound . public = true ;
msg . channel_id . say ( & ctx , " Sound has been set to public 🔓 " ) . await ? ;
}
sound . commit ( pool ) . await ?
}
}
2020-05-16 20:28:47 +00:00
None = > {
2020-05-15 21:04:22 +00:00
msg . channel_id . say ( & ctx , " Sound could not be found by that name. " ) . await ? ;
}
}
Ok ( ( ) )
}
2020-05-16 20:28:47 +00:00
#[ command( " delete " ) ]
2020-05-16 23:40:35 +00:00
async fn delete_sound ( ctx : & Context , msg : & Message , args : Args ) -> CommandResult {
2020-05-16 20:28:47 +00:00
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
let uid = * msg . author . id . as_u64 ( ) ;
let gid = * msg . guild_id . unwrap ( ) . as_u64 ( ) ;
let name = args . rest ( ) ;
let sound_vec = Sound ::search_for_sound ( name , gid , uid , pool . clone ( ) , true ) . await ? ;
let sound_result = sound_vec . first ( ) ;
match sound_result {
Some ( sound ) = > {
if sound . uploader_id ! = uid & & sound . server_id ! = gid {
msg . channel_id . say ( & ctx , " You can only delete sounds from this guild or that you have uploaded. " ) . await ? ;
}
else {
sound . delete ( pool ) . await ? ;
msg . channel_id . say ( & ctx , " Sound has been deleted " ) . await ? ;
}
}
None = > {
msg . channel_id . say ( & ctx , " Sound could not be found by that name. " ) . await ? ;
}
}
Ok ( ( ) )
}
2020-05-18 00:18:37 +00:00
async fn format_search_results ( search_results : Vec < Sound > , msg : & Message , ctx : & Context ) -> Result < ( ) , Box < dyn std ::error ::Error > > {
2020-05-16 23:40:35 +00:00
let mut current_character_count = 0 ;
let title = " Public sounds matching filter: " ;
let field_iter = search_results . iter ( ) . take ( 25 ) . map ( | item | {
( & item . name , format! ( " ID: {} \n Plays: {} " , item . id , item . plays ) , false )
} ) . filter ( | item | {
current_character_count + = item . 0. len ( ) + item . 1. len ( ) ;
current_character_count < = 2048 - title . len ( )
} ) ;
msg . channel_id . send_message ( & ctx , | m | {
m . embed ( | e | { e
. title ( title )
. fields ( field_iter )
} )
} ) . await ? ;
2020-05-16 20:28:47 +00:00
Ok ( ( ) )
}
2020-05-18 00:18:37 +00:00
#[ command( " search " ) ]
async fn search_sounds ( ctx : & Context , msg : & Message , args : Args ) -> CommandResult {
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
let query = args . rest ( ) ;
let search_results = Sound ::search_for_sound ( query , * msg . guild_id . unwrap ( ) . as_u64 ( ) , * msg . author . id . as_u64 ( ) , pool , false ) . await ? ;
format_search_results ( search_results , msg , ctx ) . await ? ;
Ok ( ( ) )
}
2020-05-18 22:44:51 +00:00
#[ command( " popular " ) ]
async fn show_popular_sounds ( ctx : & Context , msg : & Message , _args : Args ) -> CommandResult {
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
let search_results = sqlx ::query_as_unchecked! (
Sound ,
"
SELECT * FROM sounds
2020-05-19 20:03:07 +00:00
ORDER BY plays DESC
LIMIT 25
2020-05-18 22:44:51 +00:00
"
)
. fetch_all ( & pool )
. await ? ;
format_search_results ( search_results , msg , ctx ) . await ? ;
Ok ( ( ) )
}
#[ command( " random " ) ]
async fn show_random_sounds ( ctx : & Context , msg : & Message , _args : Args ) -> CommandResult {
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
let search_results = sqlx ::query_as_unchecked! (
Sound ,
"
SELECT * FROM sounds
2020-05-19 15:29:57 +00:00
ORDER BY rand ( )
2020-05-19 20:03:07 +00:00
LIMIT 25
2020-05-18 22:44:51 +00:00
"
)
. fetch_all ( & pool )
. await ? ;
format_search_results ( search_results , msg , ctx ) . await ? ;
Ok ( ( ) )
}
2020-05-18 00:18:37 +00:00
#[ command( " greet " ) ]
async fn set_greet_sound ( ctx : & Context , msg : & Message , args : Args ) -> CommandResult {
let pool = ctx . data . read ( ) . await
. get ::< SQLPool > ( ) . cloned ( ) . expect ( " Could not get SQLPool from data " ) ;
let query = args . rest ( ) ;
let user_id = * msg . author . id . as_u64 ( ) ;
if query . len ( ) = = 0 {
sqlx ::query! (
"
UPDATE users
SET
join_sound_id = NULL
WHERE
user = ?
" ,
user_id
)
. execute ( & pool )
. await ? ;
msg . channel_id . say ( & ctx , " Your greet sound has been unset. " ) . await ? ;
}
else {
let sound_vec = Sound ::search_for_sound ( query , * msg . guild_id . unwrap ( ) . as_u64 ( ) , user_id , pool . clone ( ) , true ) . await ? ;
match sound_vec . first ( ) {
Some ( sound ) = > {
sound . set_as_greet ( user_id , pool ) . await ? ;
msg . channel_id . say ( & ctx , format! ( " Greet sound has been set to {} (ID {} ) " , sound . name , sound . id ) ) . await ? ;
}
None = > {
msg . channel_id . say ( & ctx , " Could not find a sound by that name. " ) . await ? ;
}
}
}
Ok ( ( ) )
}