diff --git a/README.md b/README.md index ec79fe0..db07514 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,8 @@ Clone this repository with `git clone --recursive https://git.sad.ovh/sophie/cop - 2 billion note per second parser with all modern midi features supported - Full economy system written using `sqlx` and postgres - Lots of generic MPP bot features such as following, moving between rooms, et cetera +- Suggestions via NTFY +- Very good argument system ## Todo - No todo :3 diff --git a/src/commands/eco/fish.rs b/src/commands/eco/fish.rs index 512b95a..e9efa9f 100644 --- a/src/commands/eco/fish.rs +++ b/src/commands/eco/fish.rs @@ -409,7 +409,7 @@ impl Command for FishCommand { tokio::spawn(async move { tokio::time::sleep(std::time::Duration::from_secs(timeout_secs as u64)).await; client_clone - .dm(format!("🎣 You can fish again now!"), player._id) + .dm("🎣 You can fish again now!", player._id.as_str()) .await; }); } diff --git a/src/commands/system/mod.rs b/src/commands/system/mod.rs index 763697b..491cc78 100644 --- a/src/commands/system/mod.rs +++ b/src/commands/system/mod.rs @@ -1,3 +1,3 @@ use crate::submods; -submods!(follow, help, launch, test, translate, about, rank); +submods!(follow, help, launch, test, translate, about, rank, suggest); diff --git a/src/commands/system/suggest.rs b/src/commands/system/suggest.rs new file mode 100644 index 0000000..78824c6 --- /dev/null +++ b/src/commands/system/suggest.rs @@ -0,0 +1,95 @@ +use crate::Configuration; +use crate::User; +use crate::client::Client; +use crate::client::ClientEvent; +use crate::client::Player; +use crate::commands::Command; +use crate::commands::argument::{ArgumentSpec, ArgumentType, ParsedArgument, ParsedArguments}; + +use async_trait::async_trait; + +pub struct SuggestCommand { + conf: Configuration, +} + +impl SuggestCommand { + pub fn new(conf: Configuration) -> Self { + Self { conf } + } +} + +#[async_trait] +impl Command for SuggestCommand { + fn name(&self) -> &'static str { + "suggest" + } + fn aliases(&self) -> &[&'static str] { + &["suggestion", "sg"] + } + fn category(&self) -> &'static str { + "system" + } + fn description(&self) -> &'static str { + "Suggest something to the bot owner." + } + fn argument_spec(&self) -> &'static [ArgumentSpec] { + &[ArgumentSpec { + name: "text", + arg_type: ArgumentType::GreedyString, + required: true, + default: None, + children: &[], + }] + } + + async fn constructed(&mut self, _: Client) {} + + async fn event(&mut self, _: Client, _: ClientEvent) {} + + async fn execute(&mut self, client: Client, player: Player, args: ParsedArguments, _: User) { + let suggestion = match args.get("text") { + Some(ParsedArgument::GreedyString(s)) if !s.is_empty() => s, + _ => { + client.message("Please provide a suggestion.").await; + return; + } + }; + + let ntfy_url = self.conf.commands.ntfy.clone(); + + if ntfy_url.is_none() { + client.message("Suggestions are currently disabled.").await; + return; + } + + let username = player.name.clone(); + let player_id_short = player._id.chars().take(6).collect::(); + let message_body = format!("{} ({}): {}", username, player_id_short, suggestion); + + let res = reqwest::Client::new() + .post(ntfy_url.unwrap()) + .header("Priority", "1") + .header("Title", "New Copper suggestion") + .body(message_body) + .send() + .await; + + match res { + Ok(resp) if resp.status().is_success() => { + client + .message("Your suggestion has been sent. Thank you!") + .await; + } + Ok(resp) => { + client + .message(format!("Failed to send suggestion: HTTP {}", resp.status())) + .await; + } + Err(e) => { + client + .message(format!("Failed to send suggestion: {}", e)) + .await; + } + } + } +} diff --git a/src/main.rs b/src/main.rs index a6354bd..59b4a13 100644 --- a/src/main.rs +++ b/src/main.rs @@ -114,27 +114,28 @@ macro_rules! register_all { )+ }; } -#[derive(Deserialize, Clone)] +#[derive(Deserialize, Clone, Debug)] pub struct Configuration { database: DatabaseConfig, commands: CommandsConfig, client: ClientConfig, } -#[derive(Deserialize, Clone)] +#[derive(Deserialize, Clone, Debug)] struct DatabaseConfig { url: String, } -#[derive(Deserialize, Clone)] +#[derive(Deserialize, Clone, Debug)] struct CommandsConfig { prefix: String, name: String, + ntfy: Option, copyparty: String, playlists: HashMap>, } -#[derive(Deserialize, Clone)] +#[derive(Deserialize, Clone, Debug)] struct ClientConfig { token: String, ws: String, @@ -171,6 +172,7 @@ async fn main() -> Result<(), Box> { [ StopCommand::new(midi_state.clone()), PlaylistCommand::new(midi_state.clone(), conf.clone()), + SuggestCommand::new(conf.clone()), QueueCommand::new(midi_state.clone()), SkipCommand::new(midi_state.clone()), LaunchCommand, diff --git a/src/midi_helper.rs b/src/midi_helper.rs index 81e7bbd..93a343a 100644 --- a/src/midi_helper.rs +++ b/src/midi_helper.rs @@ -1,8 +1,6 @@ use flume::Sender; use midiplayer_rs::midi::loader::load_midi_file; -use midiplayer_rs::midi::player::ParsedMidi; use midiplayer_rs::midi::player::parse_midi_events; -use midiplayer_rs::midi::player::play_parsed_events; use midiplayer_rs::midi::utils::get_time_100ns; use midiplayer_rs::midi::utils::unpack_event; use std::time::Duration; @@ -29,7 +27,7 @@ pub enum MidiEvent { seconds: u128, millis: u128, parse_time: Duration, - entry: QueueEntry, + entry: Box, }, } @@ -69,7 +67,7 @@ pub async fn play_midi( minutes, seconds, millis, - entry, + entry: Box::new(entry), parse_time: start.elapsed(), }) .await;