From d342a5eab56e2e4c68fc1361df93422a2bb405c6 Mon Sep 17 00:00:00 2001 From: hodasemi Date: Mon, 9 Mar 2020 13:00:04 +0100 Subject: [PATCH] Add tag command --- .vscode/settings.json | 3 +- src/main.rs | 2 +- src/player/commands/list.rs | 82 +++++++++++++++++++++++++++++------ src/player/commands/mod.rs | 1 + src/player/commands/play.rs | 72 +++++++++++++++++++++++++++++- src/player/commands/remove.rs | 42 ++++++++++++++++-- src/player/commands/tag.rs | 73 +++++++++++++++++++++++++++++++ src/player/mediadata.rs | 27 +++++++++++- src/player/prelude.rs | 1 + 9 files changed, 280 insertions(+), 23 deletions(-) create mode 100644 src/player/commands/tag.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 5917d66..9072e9a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,5 +4,6 @@ "activityBar.background": "#2B300C", "titleBar.activeBackground": "#3D4311", "titleBar.activeForeground": "#F9FBEF" - } + }, + "gitea.repo": "RMusicBot" } \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 540b623..c7c914e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,7 +44,7 @@ impl Default for Config { } #[group] -#[commands(ip, list, pause, play, remove, skip, stop)] +#[commands(ip, list, pause, play, remove, skip, stop, tag)] struct General; #[help] diff --git a/src/player/commands/list.rs b/src/player/commands/list.rs index e8d355c..a0200c8 100644 --- a/src/player/commands/list.rs +++ b/src/player/commands/list.rs @@ -1,5 +1,7 @@ use super::super::prelude::*; +use rusqlite::params; + use serenity::prelude::*; use serenity::{ framework::standard::{macros::command, Args, CommandResult}, @@ -7,7 +9,7 @@ use serenity::{ }; #[command] -fn list(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult { +fn list(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { if let Err(err) = channel_contains_author(ctx, msg) { msg.channel_id.say(&ctx.http, err)?; return Ok(()); @@ -26,22 +28,74 @@ fn list(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult { let media_lock = media.lock().unwrap(); - let playlist = media_lock.playlist(); + if args.len() == 0 { + let playlist = media_lock.playlist(); - output += &format!( - "{} {} queued\n", - playlist.len(), - if playlist.len() == 1 { "song" } else { "songs" } - ); + output += &format!( + "{} {} queued\n", + playlist.len(), + if playlist.len() == 1 { "song" } else { "songs" } + ); - let max_output = 5; + let max_output = 5; - for (i, song) in playlist.iter().enumerate() { - if i < max_output { - output += &format!("\t{}.\t{}\n", i + 1, song.name.clone()); - } else { - output += &format!("\t... and {} more", playlist.len() - max_output); - break; + for (i, song) in playlist.iter().enumerate() { + if i < max_output { + output += &format!("\t{}.\t{}\n", i + 1, song.name.clone()); + } else { + output += &format!("\t... and {} more", playlist.len() - max_output); + break; + } + } + } else { + let first_arg = args.single::()?; + + if first_arg == "--tags" { + // check all tables for this entry + let mut stmt = match media_lock + .db() + .prepare("SELECT name FROM sqlite_master WHERE type='table'") + { + Ok(statement) => statement, + Err(_) => { + return Err(serenity::framework::standard::CommandError( + "failed preparing data base access".to_string(), + )) + } + }; + + let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result) + { + Ok(rows) => rows, + Err(_) => { + return Err(serenity::framework::standard::CommandError( + "failed querying rows".to_string(), + )) + } + }; + + let mut tag_names = Vec::new(); + + for row in rows { + let table_name = match row { + Ok(name) => name, + Err(_) => { + return Err(serenity::framework::standard::CommandError( + "failed getting name from row".to_string(), + )) + } + }; + + if table_name != "Vulva3" { + tag_names.push(table_name); + } + } + + output += &format!("{} available tags:", tag_names.len()); + + for tag in tag_names.iter() { + output += &format!("\n\t{}", tag); + } } } diff --git a/src/player/commands/mod.rs b/src/player/commands/mod.rs index bd90598..d28cd76 100644 --- a/src/player/commands/mod.rs +++ b/src/player/commands/mod.rs @@ -5,3 +5,4 @@ pub mod play; pub mod remove; pub mod skip; pub mod stop; +pub mod tag; diff --git a/src/player/commands/play.rs b/src/player/commands/play.rs index 64fc608..46217e3 100644 --- a/src/player/commands/play.rs +++ b/src/player/commands/play.rs @@ -48,6 +48,15 @@ fn play(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { if first_arg == "--local" { handle_local_request(&mut media_lock, ctx, msg)?; + } else if first_arg == "--tag" { + let _dummy = args.single::()?; + let mut arg_list = args.single::()?; + + for arg in args.iter::() { + arg_list += &format!(" {}", arg?.trim()); + } + + handle_tag_request(&mut media_lock, ctx, msg, &arg_list)? } else if first_arg.starts_with("http") { handle_http_request(&mut media_lock, ctx, msg, first_arg)?; } else { @@ -60,6 +69,8 @@ fn play(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { handle_song_request(&mut media_lock, ctx, msg, &arg_list)? } } + + println!("current song name: {}", media_lock.song_name()); } WATCHER.call_once(|| { @@ -196,7 +207,7 @@ fn handle_http_request( if sql .execute( "INSERT INTO Vulva3 (name, link) - VALUES (?1, ?2)", + VALUES (?1, ?2)", params![song.name, url], ) .is_err() @@ -307,3 +318,62 @@ fn handle_song_request( Ok(()) } + +fn handle_tag_request( + media: &mut MediaData, + ctx: &serenity::client::Context, + msg: &serenity::model::channel::Message, + pattern: &str, +) -> VerboseResult<()> { + let mut songs = Vec::new(); + + { + let mut stmt = match media.db().prepare(&format!( + "SELECT name FROM sqlite_master WHERE type='table' AND name='{}'", + pattern + )) { + Ok(statement) => statement, + Err(_) => create_error!("failed preparing data base access"), + }; + + let mut rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result) + { + Ok(rows) => rows, + Err(_) => create_error!("failed querying rows"), + }; + + if let None = rows.next() { + msg.channel_id + .say(&ctx.http, format!("tag ({}) not found", pattern)) + .map_err(|err| format!("{}", err))?; + + return Ok(()); + } + + let mut stmt = match media.db().prepare(&format!("SELECT name FROM {}", pattern)) { + Ok(statement) => statement, + Err(_) => create_error!("failed preparing data base access"), + }; + + let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result) { + Ok(rows) => rows, + Err(_) => create_error!("failed querying rows"), + }; + + for name_result in rows { + let name = match name_result { + Ok(name) => name, + Err(_) => create_error!("failed getting name from row"), + }; + + songs.push(Song { name }); + } + } + + let mut rng = thread_rng(); + songs.shuffle(&mut rng); + + append_songs(media, ctx, msg, songs)?; + + Ok(()) +} diff --git a/src/player/commands/remove.rs b/src/player/commands/remove.rs index 43372db..a1acbaf 100644 --- a/src/player/commands/remove.rs +++ b/src/player/commands/remove.rs @@ -28,10 +28,44 @@ fn remove(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult { let song_name = media_lock.song_name(); let sql = media_lock.db(); - if let Err(_) = sql.execute("DELETE FROM Vulva3 WHERE name = ?", params![song_name]) { - return Err(serenity::framework::standard::CommandError( - "failed executing sql delete".to_string(), - )); + // check all tables for this entry + let mut stmt = match sql.prepare("SELECT name FROM sqlite_master WHERE type='table'") { + Ok(statement) => statement, + Err(_) => { + return Err(serenity::framework::standard::CommandError( + "failed preparing data base access".to_string(), + )) + } + }; + + let rows = match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result) { + Ok(rows) => rows, + Err(_) => { + return Err(serenity::framework::standard::CommandError( + "failed querying rows".to_string(), + )) + } + }; + + // delete song from all tables + for row in rows { + let table_name = match row { + Ok(name) => name, + Err(_) => { + return Err(serenity::framework::standard::CommandError( + "failed getting name from row".to_string(), + )) + } + }; + + if let Err(_) = sql.execute( + &format!("DELETE FROM {} WHERE name = ?", table_name), + params![song_name], + ) { + return Err(serenity::framework::standard::CommandError( + "failed executing sql delete".to_string(), + )); + } } if !song_name.is_empty() { diff --git a/src/player/commands/tag.rs b/src/player/commands/tag.rs new file mode 100644 index 0000000..eb68435 --- /dev/null +++ b/src/player/commands/tag.rs @@ -0,0 +1,73 @@ +use super::super::prelude::*; + +use rusqlite::params; + +use serenity::{ + framework::standard::{macros::command, Args, CommandResult}, + model::channel::Message, +}; + +// This imports `typemap`'s `Key` as `TypeMapKey`. +use serenity::prelude::*; + +#[command] +fn tag(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult { + if let Err(err) = channel_contains_author(ctx, msg) { + msg.channel_id.say(&ctx.http, err)?; + return Ok(()); + } + + let mut data = ctx.data.write(); + let media = match data.get_mut::() { + Some(media) => media, + None => { + msg.channel_id.say(&ctx.http, "could not find media data")?; + return Ok(()); + } + }; + + let media_lock = media.lock().unwrap(); + let mut tag = args.single::()?; + + for arg in args.iter::() { + tag += &format!(" {}", arg?.trim()); + } + + match media_lock.db().execute( + &format!( + "CREATE TABLE IF NOT EXISTS {} ( + name TEXT PRIMARY KEY + )", + tag + ), + params![], + ) { + Ok(_) => (), + Err(err) => { + println!("{}", err); + return Ok(()); + } + }; + + let song_name = media_lock.song_name(); + + if media_lock + .db() + .execute( + &format!( + "INSERT INTO {} (name) + VALUES (?1)", + tag + ), + params![song_name], + ) + .is_err() + { + msg.channel_id.say( + &ctx.http, + format!("could not add tag ({}) for {}", tag, song_name), + )?; + } + + Ok(()) +} diff --git a/src/player/mediadata.rs b/src/player/mediadata.rs index 92195ac..8df1396 100644 --- a/src/player/mediadata.rs +++ b/src/player/mediadata.rs @@ -162,8 +162,31 @@ impl MediaData { let sql = self.db(); - if let Err(_) = sql.execute("DELETE FROM Vulva3 WHERE name = ?", params![&first]) { - create_error!("failed executing sql delete"); + // check all tables for this entry + let mut stmt = + match sql.prepare("SELECT name FROM sqlite_master WHERE type='table'") { + Ok(statement) => statement, + Err(_) => create_error!("failed preparing data base access"), + }; + + let rows = + match stmt.query_map(params![], |row| row.get(0) as rusqlite::Result) { + Ok(rows) => rows, + Err(_) => create_error!("failed querying rows"), + }; + + for row in rows { + let table_name = match row { + Ok(name) => name, + Err(_) => create_error!("failed getting name from row"), + }; + + if let Err(_) = sql.execute( + &format!("DELETE FROM {} WHERE name = {}", table_name, first), + params![], + ) { + println!("{} does not contain {}", table_name, first); + } } } else { return Ok(first); diff --git a/src/player/prelude.rs b/src/player/prelude.rs index 053ac23..e03e7a1 100644 --- a/src/player/prelude.rs +++ b/src/player/prelude.rs @@ -7,6 +7,7 @@ pub use super::commands::play::*; pub use super::commands::remove::*; pub use super::commands::skip::*; pub use super::commands::stop::*; +pub use super::commands::tag::*; pub use super::eventhandler::Handler;