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",
"titleBar.activeBackground": "#3D4311",
"titleBar.activeForeground": "#F9FBEF"
}
},
"gitea.repo": "RMusicBot"
}

View file

@ -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]

View file

@ -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::<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 skip;
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" {
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") {
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<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 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<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() {

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();
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<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 {
return Ok(first);

View file

@ -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;