Add tag command

This commit is contained in:
hodasemi 2020-03-09 13:00:04 +01:00
parent 3f6fc4e4e0
commit d342a5eab5
9 changed files with 280 additions and 23 deletions

View file

@ -4,5 +4,6 @@
"activityBar.background": "#2B300C", "activityBar.background": "#2B300C",
"titleBar.activeBackground": "#3D4311", "titleBar.activeBackground": "#3D4311",
"titleBar.activeForeground": "#F9FBEF" "titleBar.activeForeground": "#F9FBEF"
} },
"gitea.repo": "RMusicBot"
} }

View file

@ -44,7 +44,7 @@ impl Default for Config {
} }
#[group] #[group]
#[commands(ip, list, pause, play, remove, skip, stop)] #[commands(ip, list, pause, play, remove, skip, stop, tag)]
struct General; struct General;
#[help] #[help]

View file

@ -1,5 +1,7 @@
use super::super::prelude::*; use super::super::prelude::*;
use rusqlite::params;
use serenity::prelude::*; use serenity::prelude::*;
use serenity::{ use serenity::{
framework::standard::{macros::command, Args, CommandResult}, framework::standard::{macros::command, Args, CommandResult},
@ -7,7 +9,7 @@ use serenity::{
}; };
#[command] #[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) { if let Err(err) = channel_contains_author(ctx, msg) {
msg.channel_id.say(&ctx.http, err)?; msg.channel_id.say(&ctx.http, err)?;
return Ok(()); return Ok(());
@ -26,22 +28,74 @@ fn list(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult {
let media_lock = media.lock().unwrap(); let media_lock = media.lock().unwrap();
let playlist = media_lock.playlist(); if args.len() == 0 {
let playlist = media_lock.playlist();
output += &format!( output += &format!(
"{} {} queued\n", "{} {} queued\n",
playlist.len(), playlist.len(),
if playlist.len() == 1 { "song" } else { "songs" } if playlist.len() == 1 { "song" } else { "songs" }
); );
let max_output = 5; let max_output = 5;
for (i, song) in playlist.iter().enumerate() { for (i, song) in playlist.iter().enumerate() {
if i < max_output { if i < max_output {
output += &format!("\t{}.\t{}\n", i + 1, song.name.clone()); output += &format!("\t{}.\t{}\n", i + 1, song.name.clone());
} else { } else {
output += &format!("\t... and {} more", playlist.len() - max_output); output += &format!("\t... and {} more", playlist.len() - max_output);
break; break;
}
}
} else {
let first_arg = args.single::<String>()?;
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<String>)
{
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);
}
} }
} }

View file

@ -5,3 +5,4 @@ pub mod play;
pub mod remove; pub mod remove;
pub mod skip; pub mod skip;
pub mod stop; pub mod stop;
pub mod tag;

View file

@ -48,6 +48,15 @@ fn play(ctx: &mut Context, msg: &Message, mut args: Args) -> CommandResult {
if first_arg == "--local" { if first_arg == "--local" {
handle_local_request(&mut media_lock, ctx, msg)?; handle_local_request(&mut media_lock, ctx, msg)?;
} else if first_arg == "--tag" {
let _dummy = args.single::<String>()?;
let mut arg_list = args.single::<String>()?;
for arg in args.iter::<String>() {
arg_list += &format!(" {}", arg?.trim());
}
handle_tag_request(&mut media_lock, ctx, msg, &arg_list)?
} else if first_arg.starts_with("http") { } else if first_arg.starts_with("http") {
handle_http_request(&mut media_lock, ctx, msg, first_arg)?; handle_http_request(&mut media_lock, ctx, msg, first_arg)?;
} else { } 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)? handle_song_request(&mut media_lock, ctx, msg, &arg_list)?
} }
} }
println!("current song name: {}", media_lock.song_name());
} }
WATCHER.call_once(|| { WATCHER.call_once(|| {
@ -196,7 +207,7 @@ fn handle_http_request(
if sql if sql
.execute( .execute(
"INSERT INTO Vulva3 (name, link) "INSERT INTO Vulva3 (name, link)
VALUES (?1, ?2)", VALUES (?1, ?2)",
params![song.name, url], params![song.name, url],
) )
.is_err() .is_err()
@ -307,3 +318,62 @@ fn handle_song_request(
Ok(()) 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<String>)
{
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<String>) {
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(())
}

View file

@ -28,10 +28,44 @@ fn remove(ctx: &mut Context, msg: &Message, _: Args) -> CommandResult {
let song_name = media_lock.song_name(); let song_name = media_lock.song_name();
let sql = media_lock.db(); let sql = media_lock.db();
if let Err(_) = sql.execute("DELETE FROM Vulva3 WHERE name = ?", params![song_name]) { // check all tables for this entry
return Err(serenity::framework::standard::CommandError( let mut stmt = match sql.prepare("SELECT name FROM sqlite_master WHERE type='table'") {
"failed executing sql delete".to_string(), 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<String>) {
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() { if !song_name.is_empty() {

View file

@ -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::<Media>() {
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::<String>()?;
for arg in args.iter::<String>() {
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(())
}

View file

@ -162,8 +162,31 @@ impl MediaData {
let sql = self.db(); let sql = self.db();
if let Err(_) = sql.execute("DELETE FROM Vulva3 WHERE name = ?", params![&first]) { // check all tables for this entry
create_error!("failed executing sql delete"); 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<String>) {
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 { } else {
return Ok(first); return Ok(first);

View file

@ -7,6 +7,7 @@ pub use super::commands::play::*;
pub use super::commands::remove::*; pub use super::commands::remove::*;
pub use super::commands::skip::*; pub use super::commands::skip::*;
pub use super::commands::stop::*; pub use super::commands::stop::*;
pub use super::commands::tag::*;
pub use super::eventhandler::Handler; pub use super::eventhandler::Handler;