RMusicBot/src/player/commands/play.rs

310 lines
8.2 KiB
Rust
Raw Normal View History

2018-11-14 14:29:58 +00:00
use parking_lot;
use std::cell::RefCell;
2018-11-23 21:06:35 +00:00
use std::ops::{Deref, DerefMut};
2018-11-14 14:29:58 +00:00
use std::sync::{Arc, MutexGuard};
2018-11-14 15:43:53 +00:00
use rand::{seq::SliceRandom, thread_rng};
2018-11-14 14:29:58 +00:00
use super::super::prelude::*;
2019-04-03 09:42:06 +00:00
use rusqlite::params;
2019-07-12 08:39:03 +00:00
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, 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(());
2018-11-14 14:29:58 +00:00
}
2019-07-12 08:39:03 +00:00
let data = ctx.data.read();
let mut media = match data.get_mut::<MediaData>() {
Ok(media) => media,
Err(_) => {
msg.channel_id.say("could not find media data");
return Ok(());
2018-11-14 14:29:58 +00:00
}
2019-07-12 08:39:03 +00:00
};
2018-11-14 14:29:58 +00:00
2019-07-12 08:39:03 +00:00
if args.len() == 0 {
if !Self::check_for_continue(self.media.song_mut()?) {
print_error!(msg.channel_id.say("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(()),
2018-11-14 14:29:58 +00:00
};
2019-07-12 08:39:03 +00:00
if arg == "--local" {
self.handle_local_request(ctx, msg)?;
} else if arg.starts_with("http") {
self.handle_http_request(ctx, msg, &arg)?;
} else {
self.handle_song_request(ctx, msg, &arg)?;
2018-11-14 15:43:53 +00:00
}
2019-07-12 08:39:03 +00:00
} else {
print_error!(msg.channel_id.say("Unsupported argument list"));
}
2018-11-14 15:43:53 +00:00
2019-07-12 08:39:03 +00:00
Ok(())
}
2019-04-28 12:13:36 +00:00
2019-07-12 08:39:03 +00:00
fn check_for_continue(song_lock: MutexGuard<RefCell<Option<LockedAudio>>>) -> bool {
match song_lock.borrow_mut().deref_mut() {
Some(song) => {
let song_clone = song.clone();
let mut audio_lock = song_clone.lock();
2019-04-28 12:13:36 +00:00
2019-07-12 08:39:03 +00:00
audio_lock.play();
true
}
None => false,
}
}
2019-04-28 12:13:36 +00:00
2019-07-12 08:39:03 +00:00
fn check_join_channel(
manager_lock: &Arc<parking_lot::Mutex<serenity::client::bridge::voice::ClientVoiceManager>>,
msg: &serenity::model::channel::Message,
) {
let guild = match msg.guild() {
Some(guild) => guild,
None => {
display_error!(msg.channel_id.say("Groups and DMs not supported"));
2018-11-23 21:06:35 +00:00
2019-07-12 08:39:03 +00:00
return;
2019-04-28 12:13:36 +00:00
}
2019-07-12 08:39:03 +00:00
};
2018-11-23 21:06:35 +00:00
2019-07-12 08:39:03 +00:00
let guild_id = guild.read().id;
2018-11-14 15:43:53 +00:00
2019-07-12 08:39:03 +00:00
let channel_id = guild
.read()
.voice_states
.get(&msg.author.id)
.and_then(|voice_state| voice_state.channel_id);
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
let connect_to = match channel_id {
Some(channel) => channel,
None => {
display_error!(msg.reply("Not in a voice channel"));
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
return;
2019-04-03 09:42:06 +00:00
}
2019-07-12 08:39:03 +00:00
};
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
let mut manager = manager_lock.lock();
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
manager.join(guild_id, connect_to);
}
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
fn append_songs(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
mut source: Vec<Song>,
) -> Result<(), String> {
{
let playlist_mutex = self.media.playlist_mut()?;
playlist_mutex.borrow_mut().append(&mut source);
}
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
if let Some(manager_lock) = ctx.data.lock().get::<VoiceManager>().cloned() {
Self::check_join_channel(&manager_lock, msg);
let check_finished = {
let media_clone = self.media.clone();
let channel_id = msg.channel_id;
let manager_lock_clone = manager_lock.clone();
Arc::new(move || {
let callback = match media_clone.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);
})
};
2018-11-14 14:29:58 +00:00
2019-07-12 08:39:03 +00:00
MediaData::start_playing(&self.media, check_finished, msg.channel_id, &manager_lock);
}
2018-11-14 14:29:58 +00:00
2019-07-12 08:39:03 +00:00
Ok(())
}
2018-11-14 14:29:58 +00:00
2019-07-12 08:39:03 +00:00
fn handle_http_request(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
url: &String,
) -> Result<(), String> {
let sql = self.media.lock_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()),
};
let mut names = Vec::new();
for name_result in rows {
let name = match name_result {
Ok(name) => name,
Err(_) => return Err("failed getting name from row".to_string()),
};
2019-07-12 08:39:03 +00:00
names.push(name);
}
2019-07-12 08:39:03 +00:00
if names.len() > 0 {
print_error!(msg.channel_id.say("song already loaded!"));
2019-07-12 08:39:03 +00:00
self.append_songs(
ctx,
msg,
names.iter().map(|n| Song { name: n.clone() }).collect(),
)?;
2019-04-28 12:13:36 +00:00
2019-07-12 08:39:03 +00:00
return Ok(());
2018-11-14 14:29:58 +00:00
}
2019-07-12 08:39:03 +00:00
let source = match youtube_dl(&url) {
Ok(source) => source,
Err(why) => {
println!("Err starting source: {:?}", why);
2018-11-14 14:29:58 +00:00
2019-07-12 08:39:03 +00:00
print_error!(msg
.channel_id
.say(format!("Error using youtube-dl: {}", why)));
2018-11-14 15:43:53 +00:00
2019-07-12 08:39:03 +00:00
return Ok(());
}
};
let mut info = if source.len() == 1 {
"Finished downloading song:".to_string()
} else {
"Finished downloading songs:".to_string()
};
2018-11-14 15:43:53 +00:00
2019-07-12 08:39:03 +00:00
for song in &source {
info = format!("{}\n\t{}", info, song.name);
2019-04-28 12:13:36 +00:00
2019-07-12 08:39:03 +00:00
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());
2018-11-14 15:43:53 +00:00
}
2019-07-12 08:39:03 +00:00
}
2018-11-14 15:43:53 +00:00
2019-07-12 08:39:03 +00:00
print_error!(msg.channel_id.say(info));
2018-11-14 15:43:53 +00:00
2019-07-12 08:39:03 +00:00
self.append_songs(ctx, msg, source)?;
2019-04-28 12:13:36 +00:00
2019-07-12 08:39:03 +00:00
Ok(())
}
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
fn handle_local_request(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
) -> Result<(), String> {
let sql = self.media.lock_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()),
};
let mut songs = Vec::new();
for name_result in rows {
let name = match name_result {
Ok(name) => name,
Err(_) => return Err("failed getting name from row".to_string()),
2019-04-28 12:13:36 +00:00
};
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
songs.push(Song { name });
}
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
let mut rng = thread_rng();
songs.shuffle(&mut rng);
2019-04-28 12:13:36 +00:00
2019-07-12 08:39:03 +00:00
self.append_songs(ctx, msg, songs)?;
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
Ok(())
}
2019-04-03 09:42:06 +00:00
2019-07-12 08:39:03 +00:00
fn handle_song_request(
media: &mut MediaData,
ctx: &mut serenity::client::Context,
msg: &serenity::model::channel::Message,
pattern: &str,
) -> Result<(), String> {
let sql = self.media.lock_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()),
};
let mut songs = Vec::new();
for name_result in rows {
let name = match name_result {
Ok(name) => name,
Err(_) => return Err("failed getting name from row".to_string()),
};
2019-04-28 12:13:36 +00:00
2019-07-12 08:39:03 +00:00
songs.push(Song { name });
2019-04-03 09:42:06 +00:00
}
2018-11-14 14:29:58 +00:00
2019-07-12 08:39:03 +00:00
if !songs.is_empty() {
let mut rng = thread_rng();
songs.shuffle(&mut rng);
2018-11-14 14:29:58 +00:00
2019-07-12 08:39:03 +00:00
self.append_songs(ctx, msg, songs)?;
} else {
print_error!(msg
.channel_id
.say(format!("no song found with pattern {}", pattern)));
2018-11-14 14:29:58 +00:00
}
2019-07-12 08:39:03 +00:00
Ok(())
2018-11-14 14:29:58 +00:00
}