diff options
author | Ashelyn Rose <git@ashen.earth> | 2025-03-01 19:14:24 -0700 |
---|---|---|
committer | Ashelyn Rose <git@ashen.earth> | 2025-03-01 19:14:24 -0700 |
commit | c3063561e247ddeba1ca337af23f53832e45001e (patch) | |
tree | d698e781da3a91ea249aba4248bd705a72554b15 | |
parent | dc69de9e3535e3ca49f6b74c54d2c32d218c2d81 (diff) |
Reproxy
-rw-r--r-- | src/system/mod.rs | 5 | ||||
-rw-r--r-- | src/system/plugin.rs | 26 | ||||
-rw-r--r-- | src/system/plugin/autoproxy.rs | 146 | ||||
-rw-r--r-- | src/system/plugin/prefixes.rs | 2 | ||||
-rw-r--r-- | src/system/plugin/reproxy.rs | 62 | ||||
-rw-r--r-- | src/system/types.rs | 3 |
6 files changed, 157 insertions, 87 deletions
diff --git a/src/system/mod.rs b/src/system/mod.rs index c33c481..595e69c 100644 --- a/src/system/mod.rs +++ b/src/system/mod.rs @@ -84,13 +84,14 @@ impl Manager { } // Handle as message - let mut message_response = Response::Noop { delete_source: false }; + let mut message_response = Response::DefaultNoop; for plugin in &all_plugins { plugin.handle_message(&logger, &system, &message, &mut message_response).await; } match message_response.clone() { - Response::Noop { delete_source } => { + Response::DefaultNoop => (), + Response::ExplicitNoop { delete_source, member: _ } => { if delete_source { let client = system.members.iter().find(|m| m.discord_token == seen_by).map(|m| m.client.clone()) .expect("No such client"); diff --git a/src/system/plugin.rs b/src/system/plugin.rs index 8e6066f..043b4d0 100644 --- a/src/system/plugin.rs +++ b/src/system/plugin.rs @@ -1,21 +1,19 @@ -mod autoproxy; -mod edit; -// mod ghost; -mod prefixes; -// mod reproxy; - -use std::{collections::HashMap, sync::Arc}; - use async_trait::async_trait; - -use edit::Edit; +use std::{collections::HashMap, sync::Arc}; use twilight_model::{channel::Message, id::{marker::ChannelMarker, Id}}; -use super::log::Logger; use crate::system::types::{System, Response}; +use super::log::Logger; + +mod autoproxy; +mod edit; +mod prefixes; +mod reproxy; -pub use prefixes::ProxyPrefixes; -pub use autoproxy::Autoproxy; +use edit::Edit; +use prefixes::ProxyPrefixes; +use autoproxy::Autoproxy; +use reproxy::Reproxy; #[derive(Copy, Clone, Debug)] pub enum PluginCommand { @@ -37,6 +35,8 @@ pub trait SeancePlugin<'system> { pub fn get_plugins<'system>() -> (Vec<Arc<Box<dyn SeancePlugin<'system>>>>, HashMap<&'static str, (PluginCommand, Arc<Box<dyn SeancePlugin<'system>>>)>) { let all_plugins : Vec<Arc<Box<dyn SeancePlugin<'system>>>> = vec![ Arc::new(Box::new(ProxyPrefixes::new())), + // Before autoproxy because it should only recognize explicitly prefixed messages + Arc::new(Box::new(Reproxy::new())), Arc::new(Box::new(Autoproxy::new())), Arc::new(Box::new(Edit::new())), ]; diff --git a/src/system/plugin/autoproxy.rs b/src/system/plugin/autoproxy.rs index d9beaf0..03e20b8 100644 --- a/src/system/plugin/autoproxy.rs +++ b/src/system/plugin/autoproxy.rs @@ -28,6 +28,44 @@ impl Autoproxy { } } +impl Autoproxy { + // Sets the latch and sets a timeout to unlatch it if + // state is identical when the timeout goes off + async fn set_latch(&self, member: &Member, latch_time: &Timestamp) { + let starting_state = {self.current_state.lock().await.clone()}; + + let is_latch = match starting_state { + InnerState::LatchActive { current_member: _, last_message: _ } + | InnerState::LatchInactive => true, + _ => false, + }; + + if !is_latch { + return + } + + {*self.current_state.lock().await = InnerState::LatchActive { + current_member: member.clone(), + last_message: latch_time.clone(), + }}; + + let state_arc = self.current_state.clone(); + let sent_member = member.clone(); + let sent_timestamp = latch_time.clone(); + + tokio::spawn(async move { + sleep(Duration::from_secs(15 * 60)).await; + let current_state = {state_arc.lock().await.clone()}; + + if let InnerState::LatchActive { current_member, last_message } = current_state { + if sent_member.discord_token == current_member.discord_token && sent_timestamp.as_micros() == last_message.as_micros() { + {*state_arc.lock().await = InnerState::LatchInactive}; + } + } + }); + } +} + #[async_trait] impl<'system> SeancePlugin<'system> for Autoproxy { fn get_commands(&self) -> Vec<PluginCommand> { @@ -76,31 +114,44 @@ impl<'system> SeancePlugin<'system> for Autoproxy { logger.log_line(None, "Deleting source message".to_string()).await; } - *response = Response::Noop { delete_source: message.content == "\\\\" }; + *response = Response::ExplicitNoop { delete_source: message.content == "\\\\", member: None }; return } - if let Response::Noop { delete_source: _ } = response { - let current_state = {self.current_state.lock().await.clone()}; - match current_state { - InnerState::Off => return, - InnerState::LatchInactive => return, - InnerState::Member { current_member } => { - logger.log_line(Some(current_member.discord_token.clone()), "Proxying via member mode autoproxy".to_string()).await; - *response = Response::Proxy { - member: current_member.clone(), - content: message.content.clone(), - } - }, - InnerState::LatchActive { current_member, last_message: _ } => { - logger.log_line(Some(current_member.discord_token.clone()), "Proxying via autoproxy latch".to_string()).await; - *response = Response::Proxy { - member: current_member.clone(), - content: message.content.clone(), - } - }, - } + match response { + Response::DefaultNoop => { + match starting_state { + InnerState::Member { current_member } => { + logger.log_line(Some(current_member.discord_token.clone()), "Proxying via member mode autoproxy".to_string()).await; + *response = Response::Proxy { + member: current_member.clone(), + content: message.content.clone(), + } + }, + InnerState::LatchActive { current_member, last_message: _ } => { + logger.log_line(Some(current_member.discord_token.clone()), "Proxying via autoproxy latch".to_string()).await; + *response = Response::Proxy { + member: current_member.clone(), + content: message.content.clone(), + } + }, + _ => (), + } + }, + // This gets set by reproxy, so update our autoproxy for it + Response::ExplicitNoop { delete_source: _, member } => { + if let Some(member) = member { + match starting_state { + InnerState::LatchInactive + | InnerState::LatchActive { current_member: _, last_message: _ }=> { + self.set_latch(&member, &message.timestamp).await; + }, + _ => (), + } + } + }, + _ => () } } @@ -109,57 +160,12 @@ impl<'system> SeancePlugin<'system> for Autoproxy { Response::Proxy { member, content: _ } => { let current_state = {self.current_state.lock().await.clone()}; match current_state { - InnerState::Off => return, - InnerState::Member { current_member: _ } => return, - InnerState::LatchInactive => { - logger.log_line(Some(member.discord_token.clone()), "Setting autoproxy latch".to_string()).await; - {*self.current_state.lock().await = InnerState::LatchActive { - current_member: member.clone(), - last_message: message.timestamp, - }}; - - let state_arc = self.current_state.clone(); - let sent_member = member.clone(); - let sent_timestamp = message.timestamp.clone(); - - tokio::spawn(async move { - sleep(Duration::from_secs(15 * 60)).await; - let current_state = {state_arc.lock().await.clone()}; - - if let InnerState::LatchActive { current_member, last_message } = current_state { - if sent_member.discord_token == current_member.discord_token && sent_timestamp.as_micros() == last_message.as_micros() { - {*state_arc.lock().await = InnerState::LatchInactive}; - } - } - }); - }, - InnerState::LatchActive { current_member: _, last_message: _ } => { + InnerState::LatchInactive + |InnerState::LatchActive { current_member: _, last_message: _ }=> { logger.log_line(Some(member.discord_token.clone()), "Setting autoproxy latch".to_string()).await; - {*self.current_state.lock().await = InnerState::LatchActive { - current_member: member.clone(), - last_message: message.timestamp, - }}; - - let state_arc = self.current_state.clone(); - let sent_member = member.clone(); - let sent_timestamp = message.timestamp.clone(); - let logger = logger.clone(); - - tokio::spawn(async move { - sleep(Duration::from_secs(15 * 60)).await; - let current_state = {state_arc.lock().await.clone()}; - logger.log_line(Some(sent_member.discord_token.clone()), "Latch timeout".to_string()).await; - - if let InnerState::LatchActive { current_member, last_message } = current_state { - if sent_member.discord_token == current_member.discord_token && sent_timestamp.as_micros() == last_message.as_micros() { - logger.log_line(Some(sent_member.discord_token.clone()), "Latch expired".to_string()).await; - {*state_arc.lock().await = InnerState::LatchInactive}; - } else { - logger.log_line(Some(sent_member.discord_token.clone()), "Timeout no longer valid".to_string()).await; - } - } - }); + self.set_latch(&member, &message.timestamp).await; }, + _ => return, } }, _ => return, diff --git a/src/system/plugin/prefixes.rs b/src/system/plugin/prefixes.rs index 55a48d3..c613d16 100644 --- a/src/system/plugin/prefixes.rs +++ b/src/system/plugin/prefixes.rs @@ -24,7 +24,7 @@ impl<'system> SeancePlugin<'system> for ProxyPrefixes { } 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 { + if let Response::DefaultNoop = response { for member in &system.members { match member.message_pattern.captures(message.content.as_str()) { None => continue, diff --git a/src/system/plugin/reproxy.rs b/src/system/plugin/reproxy.rs new file mode 100644 index 0000000..145bed8 --- /dev/null +++ b/src/system/plugin/reproxy.rs @@ -0,0 +1,62 @@ +use async_trait::async_trait; +use twilight_model::id::{marker::ChannelMarker, Id}; +use twilight_model::channel::Message; +use crate::system::util; +use crate::system::{log::Logger, types::{Response, System}}; + +use super::{PluginCommand, SeancePlugin}; + +pub struct Reproxy; + +impl Reproxy { + pub fn new() -> Self { + Self + } +} + +#[async_trait] +impl<'system> SeancePlugin<'system> for Reproxy { + fn get_commands(&self) -> Vec<PluginCommand> { + vec![] + } + + async fn handle_command<'message>(&self, _logger: &'system Logger, _system: &'system System, _message: &'message Message, _command: PluginCommand, _args: Vec<&'message str>) { + unreachable!("Reproxy 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::Proxy { member, content } = response { + if content.as_str().trim() == "*" { + let most_recent_message = system.get_most_recent_message(message.channel_id).await; + if let Some((reproxy_target, update_most_recent)) = match message.kind { + twilight_model::channel::message::MessageType::Regular => most_recent_message.map(|(message, _)| (message, true)), + twilight_model::channel::message::MessageType::Reply => async {Some(( + message.referenced_message.as_ref()?.as_ref().clone(), + message.referenced_message.as_ref()?.id == most_recent_message?.0.id + ))}.await, + _ => todo!(), + } { + if let Ok(new_message) = util::duplicate_message(&member.client, &reproxy_target, reproxy_target.content.as_str()).await { + let _ = {member.client.lock().await.delete_message(reproxy_target.channel_id, reproxy_target.id).await}; + + if update_most_recent { + system.cache_most_recent_message(new_message.channel_id, new_message, member.clone()).await; + } + + *response = Response::ExplicitNoop { delete_source: true, member: Some(member.clone()) }; + } else { + logger.log_err(None, format!("Error reproxying message")).await; + *response = Response::ExplicitNoop { delete_source: false, member: None }; + } + } else { + logger.log_err(None, format!("Could not find reproxy target")).await; + *response = Response::ExplicitNoop { delete_source: false, member: None }; + } + } + } + } + + async fn post_response<'message>(&self, _logger: &'system Logger, _system: &'system System, _message: &'message Message, _channel: Id<ChannelMarker>, _response: &'message Response) { + // noop + } +} diff --git a/src/system/types.rs b/src/system/types.rs index 4143bfd..aaef165 100644 --- a/src/system/types.rs +++ b/src/system/types.rs @@ -26,8 +26,9 @@ pub struct System { #[derive(Clone)] pub enum Response { + DefaultNoop, + ExplicitNoop { delete_source: bool, member: Option<Member> }, Proxy {member: Member, content: String}, - Noop {delete_source: bool}, } pub enum SystemThreadCommand { |