diff options
author | Ashelyn Rose <git@ashen.earth> | 2025-03-01 15:42:30 -0700 |
---|---|---|
committer | Ashelyn Rose <git@ashen.earth> | 2025-03-01 15:42:30 -0700 |
commit | 04bbe74642f5b6257361014e0c122a33c9fa4d70 (patch) | |
tree | 74e8e46345bed02b77b80941fdcc82b9a096e30d | |
parent | 93eea2ba1ef9a6493801ea7211dd8d046588d28a (diff) |
Refactor command handling
-rw-r--r-- | src/system/mod.rs | 79 | ||||
-rw-r--r-- | src/system/plugin.rs | 36 | ||||
-rw-r--r-- | src/system/plugin/autoproxy.rs | 51 | ||||
-rw-r--r-- | src/system/plugin/prefixes.rs | 19 |
4 files changed, 105 insertions, 80 deletions
diff --git a/src/system/mod.rs b/src/system/mod.rs index 8e642ea..0f5f23c 100644 --- a/src/system/mod.rs +++ b/src/system/mod.rs @@ -12,7 +12,7 @@ pub use types::SystemThreadCommand; use crate::SystemUiEvent; use std::{num::NonZeroU64, sync::Arc}; use tokio::sync::Mutex; -use plugin::SeancePlugin; +use plugin::get_plugins; use std::sync::mpsc::Sender as ThreadSender; use types::{Member, Response, System}; @@ -39,12 +39,9 @@ impl Manager { let mut message_receiver = aggregator::MessageAggregator::start(&system); let logger = Logger::new(system_name.clone(), system_config.clone(), ui_sender.clone()); - let mut plugins : Vec<Box<dyn SeancePlugin>> = vec![ - Box::new(plugin::ProxyPrefixes), - Box::new(plugin::Autoproxy::new()), - ]; + let (all_plugins, by_command) = get_plugins(); - loop { + 'member_event: loop { match message_receiver.recv().await { None => (), Some(MemberEvent::GatewayConnect(member_token)) => { @@ -57,47 +54,49 @@ impl Manager { logger.log_disconnect(member_token).await; }, Some(MemberEvent::Message(message, seen_by)) => { - if message.content.starts_with(&system.command_prefix) { - logger.log_line(None, format!("Handling command: {}", message.content)).await; - for plugin in &mut plugins { - match plugin.handle_command(&logger, &system, &message).await { - plugin::CommandOutcome::Skipped => continue, - plugin::CommandOutcome::Handled => break, - plugin::CommandOutcome::Errored {message} => { - logger.log_err(None, message).await; - break - }, + if let Some(command_string) = message.content.strip_prefix(&system.command_prefix) { + let mut words = command_string.split_whitespace(); + if let Some(command) = words.next() { + if let Some(plugin) = by_command.get(command) { + logger.log_line(None, format!("Handling command: {command}")).await; + let args : Vec<_> = words.collect(); + + plugin.handle_command(&logger, &system, &message, args).await; + continue 'member_event; + } else { + logger.log_line(None, format!("Unknown command: {command}")).await; } } - } else { - let mut message_response = Response::Noop { delete_source: false }; - for plugin in &plugins { - plugin.handle_message(&logger, &system, &message, &mut message_response).await; - } + } + + // Handle as message + let mut message_response = Response::Noop { delete_source: false }; + for plugin in &all_plugins { + plugin.handle_message(&logger, &system, &message, &mut message_response).await; + } - match message_response.clone() { - Response::Noop { delete_source } => { - if delete_source { - let client = system.members.iter().find(|m| m.discord_token == seen_by).map(|m| m.client.clone()) - .expect("No such client"); + match message_response.clone() { + Response::Noop { delete_source } => { + if delete_source { + let client = system.members.iter().find(|m| m.discord_token == seen_by).map(|m| m.client.clone()) + .expect("No such client"); - let _ = client.lock().await.delete_message(message.channel_id, message.id) - .await; + let _ = client.lock().await.delete_message(message.channel_id, message.id) + .await; + } + }, + Response::Proxy { member, content } => { + if let Ok(new_message) = util::duplicate_message(&member.client, &message, content.as_str()).await { + if let Err(err) = {member.client.lock().await.delete_message(message.channel_id, message.id).await.map(|_| ()).map_err(|err| err.to_string()).clone() } { + logger.log_err(Some(member.discord_token), format!("Could not proxy message: {err}")).await; + {let _ = member.client.lock().await.delete_message(new_message.channel_id, new_message.id).await;} } - }, - Response::Proxy { member, content } => { - if let Ok(new_message) = util::duplicate_message(&member.client, &message, content.as_str()).await { - if let Err(err) = {member.client.lock().await.delete_message(message.channel_id, message.id).await.map(|_| ()).map_err(|err| err.to_string()).clone() } { - logger.log_err(Some(member.discord_token), format!("Could not proxy message: {err}")).await; - {let _ = member.client.lock().await.delete_message(new_message.channel_id, new_message.id).await;} - } - for plugin in &plugins { - plugin.post_response(&logger, &system, &new_message, message.channel_id, &message_response).await; - } + for plugin in &all_plugins { + plugin.post_response(&logger, &system, &new_message, message.channel_id, &message_response).await; } - }, - } + } + }, } }, } diff --git a/src/system/plugin.rs b/src/system/plugin.rs index d3c6764..4419955 100644 --- a/src/system/plugin.rs +++ b/src/system/plugin.rs @@ -1,5 +1,10 @@ mod autoproxy; +// mod edit; +// mod ghost; mod prefixes; +// mod reproxy; + +use std::{collections::HashMap, sync::Arc}; use async_trait::async_trait; @@ -12,16 +17,31 @@ pub use prefixes::ProxyPrefixes; pub use autoproxy::Autoproxy; #[async_trait] -pub trait SeancePlugin { - async fn handle_command(&self, logger: &Logger, system: &System, message: &Message) -> CommandOutcome; +pub trait SeancePlugin<'system> { + fn get_commands(&self) -> Vec<&'static str>; + + async fn handle_command<'message>(&self, logger: &'system Logger, system: &'system System, message: &'message Message, args: Vec<&'message str>); - async fn handle_message(&self, logger: &Logger, system: &System, message: &Message, response: &mut Response); + async fn handle_message<'message>(&self, logger: &'system Logger, system: &'system System, message: &'message Message, response: &'message mut Response); - async fn post_response(&self, logger: &Logger, system: &System, message: &Message, channel: Id<ChannelMarker>, response: &Response); + async fn post_response<'message>(&self, logger: &'system Logger, system: &'system System, message: &'message Message, channel: Id<ChannelMarker>, response: &'message Response); } -pub enum CommandOutcome { - Skipped, - Handled, - Errored {message: String}, +pub fn get_plugins<'system>() -> (Vec<Arc<Box<dyn SeancePlugin<'system>>>>, HashMap<&'static str, Arc<Box<dyn SeancePlugin<'system>>>>) { + let all_plugins : Vec<Arc<Box<dyn SeancePlugin<'system>>>> = vec![ + Arc::new(Box::new(ProxyPrefixes)), + Arc::new(Box::new(Autoproxy::new())), + ]; + + let by_commands = all_plugins.iter() + .map(|plugin| { + let commands = plugin.get_commands(); + commands.into_iter().map(|command| { + (command, plugin.clone()) + }) + }) + .flatten() + .collect(); + + (all_plugins, by_commands) } diff --git a/src/system/plugin/autoproxy.rs b/src/system/plugin/autoproxy.rs index 67d93a0..0bbebf5 100644 --- a/src/system/plugin/autoproxy.rs +++ b/src/system/plugin/autoproxy.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use tokio::sync::Mutex; use twilight_model::{channel::{Channel, Message}, id::{marker::ChannelMarker, Id}, util::Timestamp}; use crate::system::types::{System, Member, Response}; -use super::{CommandOutcome, SeancePlugin}; +use super::SeancePlugin; use tokio::time::sleep; use std::time::Duration; use super::Logger; @@ -29,30 +29,31 @@ impl Autoproxy { } #[async_trait] -impl SeancePlugin for Autoproxy { - async fn handle_command(&self, logger: &Logger, system: &System, message: &Message) -> CommandOutcome { - if message.content.starts_with(format!("{}auto ", system.command_prefix).as_str()) { - let args = message.content.replace(format!("{}auto ", system.command_prefix).as_str(), ""); - let mut remainder = args.split_whitespace(); - let first_word = remainder.next(); - - match first_word { - Some("off") => *self.current_state.lock().await = InnerState::Off, - Some("latch") => *self.current_state.lock().await = InnerState::LatchInactive, - Some("member") => { - return CommandOutcome::Errored { message: "Member mode not supported yet".to_string() } - }, - Some(other) => return CommandOutcome::Errored { message: format!("Unknown autoproxy mode: {other}") }, - None => return CommandOutcome::Errored { message: "Must specify autoproxy mode".to_string() }, - } +impl<'system> SeancePlugin<'system> for Autoproxy { + fn get_commands(&self) -> Vec<&'static str> { + vec!["auto"] + } - CommandOutcome::Handled - } else { - CommandOutcome::Skipped - } + async fn handle_command<'message>(&self, logger: &'system Logger, system: &'system System, message: &'message Message, args: Vec<&'message str>) { + let mut args = args.iter().map(|r| *r); + let first_word = args.next(); + + match first_word { + Some("off") => *self.current_state.lock().await = InnerState::Off, + Some("latch") => *self.current_state.lock().await = InnerState::LatchInactive, + Some("member") => { + logger.log_err(None, "Member mode not supported yet".to_string()).await; + }, + Some(other) => { + logger.log_err(None, format!("Unknown autoproxy mode: {other}")).await; + }, + None => { + logger.log_err(None, "Must specify autoproxy mode".to_string()).await; + } + }; } - async fn handle_message(&self, logger: &Logger, system: &System, message: &Message, response: &mut Response) { + async fn handle_message<'message>(&self, logger: &'system Logger, system: &'system System, message: &'message Message, response: &'message mut Response) { let starting_state = {self.current_state.lock().await.clone()}; if message.content.starts_with("\\") { logger.log_line(None, "Skipping proxy".to_string()).await; @@ -97,10 +98,9 @@ impl SeancePlugin for Autoproxy { } } - async fn post_response(&self, logger: &Logger, system: &System, message: &Message, channel: Id<ChannelMarker>, response: &Response) { + async fn post_response<'message>(&self, logger: &'system Logger, system: &'system System, message: &'message Message, channel: Id<ChannelMarker>, response: &'message Response) { match response { - Response::Noop { delete_source } => return, - Response::Proxy { member, content } => { + Response::Proxy { member, content: _ } => { let current_state = {self.current_state.lock().await.clone()}; match current_state { InnerState::Off => return, @@ -156,6 +156,7 @@ impl SeancePlugin for Autoproxy { }, } }, + _ => return, } } diff --git a/src/system/plugin/prefixes.rs b/src/system/plugin/prefixes.rs index 609dafd..42d8631 100644 --- a/src/system/plugin/prefixes.rs +++ b/src/system/plugin/prefixes.rs @@ -1,18 +1,23 @@ use async_trait::async_trait; use twilight_model::id::{marker::ChannelMarker, Id}; -use crate::system::{log::Logger, types::Response}; +use twilight_model::channel::Message; +use crate::system::{log::Logger, types::{Response, System}}; -use super::{CommandOutcome, SeancePlugin}; +use super::SeancePlugin; pub struct ProxyPrefixes; #[async_trait] -impl SeancePlugin for ProxyPrefixes { - async fn handle_command(&self, _logger: &Logger, _system: &crate::system::types::System, _message: &twilight_model::channel::Message) -> CommandOutcome { - CommandOutcome::Skipped +impl<'system> SeancePlugin<'system> for ProxyPrefixes { + fn get_commands(&self) -> Vec<&'static str> { + vec![] } - async fn handle_message(&self, logger: &Logger, system: &crate::system::types::System, message: &twilight_model::channel::Message, response: &mut crate::system::types::Response) { + async fn handle_command<'message>(&self, _logger: &'system Logger, _system: &'system System, _message: &'message Message, args: Vec<&'message str>) { + unreachable!("Prefix plugin has no commands") + } + + async fn handle_message<'message>(&self, logger: &'system Logger, system: &'system System, message: &'message Message, response: &'message mut Response) { if let Response::Noop { delete_source: _ } = response { for member in &system.members { match member.message_pattern.captures(message.content.as_str()) { @@ -30,7 +35,7 @@ impl SeancePlugin for ProxyPrefixes { } } - async fn post_response(&self, _logger: &Logger, _system: &crate::system::types::System, _message: &twilight_model::channel::Message, _channel: Id<ChannelMarker>, _response: &crate::system::types::Response) { + async fn post_response<'message>(&self, _logger: &'system Logger, _system: &'system System, _message: &'message Message, _channel: Id<ChannelMarker>, _response: &'message Response) { return } } |