use lock_api::RawMutex; use std::sync::Arc; use rand::{seq::SliceRandom, thread_rng}; use super::super::prelude::*; use rusqlite::params; use serenity::prelude::*; use serenity::voice::LockedAudio; use serenity::{ framework::standard::{macros::command, Args, CommandResult}, model::channel::Message, }; #[command] fn play(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { if !channel_contains_author(ctx, msg) { println!( "user {} is not in the same voice channel as the bot", msg.author.name ); return Ok(()); } let mut data = ctx.data.write(); let media = match data.get_mut::<Media>() { Some(media) => media, None => { display_error_ok!(msg.channel_id.say(&ctx.http, "could not find media data")); return Ok(()); } }; if args.len() == 0 { if !check_for_continue(media.song_mut()) { print_error!(msg .channel_id .say(&ctx.http, "Must provide a URL to a video or audio")); } } else if args.len() == 1 { let arg = match args.single::<String>() { Ok(arg) => arg, // can't happen, since we tested for length == 1 Err(_) => return Ok(()), }; if arg == "--local" { handle_local_request(media, ctx, msg)?; } else if arg.starts_with("http") { handle_http_request(media, ctx, msg, &arg)?; } else { handle_song_request(media, ctx, msg, &arg)?; } } else { print_error!(msg.channel_id.say(&ctx.http, "Unsupported argument list")); } Ok(()) } fn check_for_continue(song_lock: &Option<LockedAudio>) -> bool { match song_lock { Some(song) => { let song_clone = song.clone(); let mut audio_lock = song_clone.lock(); audio_lock.play(); true } None => false, } } fn check_join_channel<T: RawMutex>( manager_lock: &Arc<lock_api::Mutex<T, serenity::client::bridge::voice::ClientVoiceManager>>, ctx: &Context, msg: &Message, ) { let guild = check_result_return!(guild(ctx, msg)); let guild_id = check_result_return!(guild_id(ctx, msg)); let channel_id = guild .read() .voice_states .get(&msg.author.id) .and_then(|voice_state| voice_state.channel_id); let connect_to = match channel_id { Some(channel) => channel, None => { display_error!(msg.reply(ctx, "Not in a voice channel")); return; } }; let mut manager = manager_lock.lock(); manager.join(guild_id, connect_to); } fn append_songs( media: &mut MediaData, ctx: &Context, msg: &Message, mut source: Vec<Song>, ) -> Result<(), String> { media.playlist_mut().append(&mut source); if let Some(manager_lock) = ctx.data.read().get::<VoiceManager>().cloned() { check_join_channel(&manager_lock, ctx, msg); // let check_finished = { // let channel_id = msg.channel_id; // let manager_lock_clone = manager_lock.clone(); // Arc::new(move || { // let callback = match media.next_callback.borrow().deref() { // Some(callback) => callback.clone(), // None => { // println!("next_callback not set!"); // return; // } // }; // MediaData::next_song(&media_clone, channel_id, &manager_lock_clone, callback); // }) // }; MediaData::start_playing(ctx, media, msg, &manager_lock); } Ok(()) } fn handle_http_request( media: &mut MediaData, ctx: &serenity::client::Context, msg: &serenity::model::channel::Message, url: &String, ) -> Result<(), String> { let mut names = Vec::new(); { let sql = media.db(); let mut stmt = match sql.prepare("SELECT name FROM Vulva3 WHERE link = ?") { Ok(statement) => statement, Err(_) => return Err("failed preparing data base access".to_string()), }; let rows = match stmt.query_map(&[url], |row| row.get(0) as rusqlite::Result<String>) { Ok(rows) => rows, Err(_) => return Err("failed querying rows".to_string()), }; for name_result in rows { let name = match name_result { Ok(name) => name, Err(_) => return Err("failed getting name from row".to_string()), }; names.push(name); } } if names.len() > 0 { print_error!(msg.channel_id.say(&ctx.http, "song already loaded!")); append_songs( media, ctx, msg, names.iter().map(|n| Song { name: n.clone() }).collect(), )?; } else { let source = match youtube_dl(&url) { Ok(source) => source, Err(why) => { println!("Err starting source: {:?}", why); print_error!(msg .channel_id .say(&ctx.http, format!("Error using youtube-dl: {}", why))); return Ok(()); } }; let mut info = if source.len() == 1 { "Finished downloading song:".to_string() } else { "Finished downloading songs:".to_string() }; { let sql = media.db(); for song in &source { info = format!("{}\n\t{}", info, song.name); if sql .execute( "INSERT INTO Vulva3 (name, link) VALUES (?1, ?2)", params![song.name, url], ) .is_err() { return Err("failed inserting songs into db".to_string()); } } } print_error!(msg.channel_id.say(&ctx.http, info)); append_songs(media, ctx, msg, source)?; } Ok(()) } fn handle_local_request( media: &mut MediaData, ctx: &serenity::client::Context, msg: &serenity::model::channel::Message, ) -> Result<(), String> { let mut songs = Vec::new(); { let sql = media.db(); let mut stmt = match sql.prepare("SELECT name FROM Vulva3") { Ok(statement) => statement, Err(_) => return Err("failed preparing data base access".to_string()), }; let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result<String>) { Ok(rows) => rows, Err(_) => return Err("failed querying rows".to_string()), }; for name_result in rows { let name = match name_result { Ok(name) => name, Err(_) => return Err("failed getting name from row".to_string()), }; songs.push(Song { name }); } } let mut rng = thread_rng(); songs.shuffle(&mut rng); append_songs(media, ctx, msg, songs)?; Ok(()) } fn handle_song_request( media: &mut MediaData, ctx: &serenity::client::Context, msg: &serenity::model::channel::Message, pattern: &str, ) -> Result<(), String> { let mut songs = Vec::new(); { let sql = media.db(); let mut stmt = match sql.prepare(&format!( "SELECT name FROM Vulva3 WHERE name LIKE '%{}%'", pattern )) { Ok(statement) => statement, Err(_) => return Err("failed preparing data base access".to_string()), }; let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result<String>) { Ok(rows) => rows, Err(_) => return Err("failed querying rows".to_string()), }; for name_result in rows { let name = match name_result { Ok(name) => name, Err(_) => return Err("failed getting name from row".to_string()), }; songs.push(Song { name }); } } if !songs.is_empty() { let mut rng = thread_rng(); songs.shuffle(&mut rng); append_songs(media, ctx, msg, songs)?; } else { print_error!(msg .channel_id .say(&ctx.http, format!("no song found with pattern {}", pattern))); } Ok(()) }