summary refs log tree commit diff
diff options
context:
space:
mode:
authorAshelyn Rose <git@ashen.earth>2025-03-01 15:42:30 -0700
committerAshelyn Rose <git@ashen.earth>2025-03-01 15:42:30 -0700
commit04bbe74642f5b6257361014e0c122a33c9fa4d70 (patch)
tree74e8e46345bed02b77b80941fdcc82b9a096e30d
parent93eea2ba1ef9a6493801ea7211dd8d046588d28a (diff)
Refactor command handling
-rw-r--r--src/system/mod.rs79
-rw-r--r--src/system/plugin.rs36
-rw-r--r--src/system/plugin/autoproxy.rs51
-rw-r--r--src/system/plugin/prefixes.rs19
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
     }
 }