use async_trait::async_trait; 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 tokio::time::sleep; use std::time::Duration; pub struct Autoproxy { current_state: Arc>, } #[derive(Clone)] enum InnerState { Off, LatchActive { current_member: Member, last_message: Timestamp }, LatchInactive, Member { current_member: Member }, } impl Autoproxy { pub fn new() -> Self { Self { current_state: Arc::new(Mutex::new(InnerState::Off)) } } } #[async_trait] impl SeancePlugin for Autoproxy { async fn handle_command(&self, 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() }, } CommandOutcome::Handled } else { CommandOutcome::Skipped } } async fn handle_message(&self, system: &System, message: &Message, response: &mut Response) { if message.content.starts_with("\\") { if message.content.starts_with("\\\\") { if let InnerState::LatchActive {current_member: _, last_message: _} = {self.current_state.lock().await.clone()} { {*self.current_state.lock().await = InnerState::LatchInactive}; } } *response = Response::Noop { delete_source: message.content == "\\\\" }; 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 } => { *response = Response::Proxy { member: current_member.clone(), content: message.content.clone(), } }, InnerState::LatchActive { current_member, last_message: _ } => { *response = Response::Proxy { member: current_member.clone(), content: message.content.clone(), } }, } } } async fn post_response(&self, system: &System, message: &Message, channel: Id, response: &Response) { match response { Response::Noop { delete_source } => return, 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 => { {*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: _ } => { {*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}; } } }); }, } }, } } }