summary refs log tree commit diff
path: root/src/system
diff options
context:
space:
mode:
Diffstat (limited to 'src/system')
-rw-r--r--src/system/bot/client.rs18
-rw-r--r--src/system/bot/mod.rs5
-rw-r--r--src/system/message_parser.rs126
-rw-r--r--src/system/mod.rs114
4 files changed, 176 insertions, 87 deletions
diff --git a/src/system/bot/client.rs b/src/system/bot/client.rs
index c55759a..006ce8f 100644
--- a/src/system/bot/client.rs
+++ b/src/system/bot/client.rs
@@ -52,6 +52,8 @@ impl Client {
         let delete_result = client.delete_message(channel_id, message_id).await;
         let member_id = self.bot_conf.read().await.member_id;
 
+        drop(client);
+
         match delete_result {
             Err(err) => {
                 match &err.kind() {
@@ -60,11 +62,7 @@ impl Client {
                             // Code for "Missing Permissions": https://discord.com/developers/docs/topics/opcodes-and-status-codes#json-json-error-codes
                             if err.code == 50013 {
                                 println!("ERROR: Client {} doesn't have permissions to delete message", member_id);
-                                let _ = client.create_reaction(
-                                    channel_id,
-                                    message_id,
-                                    &RequestReactionType::Unicode { name: "🔐" }
-                                ).await;
+                                let _ = self.react_message(channel_id, message_id, &RequestReactionType::Unicode { name: "🔐" }).await;
                             }
                         },
                         _ => (),
@@ -78,6 +76,16 @@ impl Client {
         }
     }
 
+    pub async fn react_message(&self, channel_id: ChannelId, message_id: MessageId, react: &'_ RequestReactionType<'_>) -> Result<(), TwiError> {
+        let _ = self.client.lock().await.create_reaction(
+            channel_id,
+            message_id,
+            react
+        ).await;
+
+        return Ok(())
+    }
+
     pub async fn duplicate_message(&self, message: &TwiMessage, content: &str) -> Result<TwiMessage, MessageDuplicateError> {
         let client = self.client.lock().await;
 
diff --git a/src/system/bot/mod.rs b/src/system/bot/mod.rs
index 6bf8d78..3c4585f 100644
--- a/src/system/bot/mod.rs
+++ b/src/system/bot/mod.rs
@@ -5,6 +5,7 @@ use std::sync::Arc;
 use tokio::sync::mpsc::Sender;
 use tokio::sync::RwLock;
 use twilight_http::error::Error as TwiError;
+use twilight_http::request::channel::reaction::RequestReactionType;
 
 pub use super::types::*;
 pub use client::MessageDuplicateError;
@@ -73,6 +74,10 @@ impl Bot {
         self.client.delete_message(channel_id, message_id).await
     }
 
+    pub async fn react_message(&self, channel_id: ChannelId, message_id: MessageId, react: &'_ RequestReactionType<'_>) -> Result<(), TwiError> {
+        self.client.react_message(channel_id, message_id, react).await
+    }
+
     pub async fn duplicate_message(&self, message_id: &TwiMessage, content: &str) ->  Result<TwiMessage, MessageDuplicateError> {
         self.client.duplicate_message(message_id, content).await
     }
diff --git a/src/system/message_parser.rs b/src/system/message_parser.rs
new file mode 100644
index 0000000..b404064
--- /dev/null
+++ b/src/system/message_parser.rs
@@ -0,0 +1,126 @@
+use std::sync::LazyLock;
+use regex::Regex;
+
+use crate::config::{self, System};
+
+use super::{FullMessage, MemberId, MessageId, Timestamp};
+
+pub enum ParsedMessage {
+    Command(Command),
+    ProxiedMessage {
+        member_id: MemberId,
+        message_content: String,
+        latch: bool,
+    },
+    UnproxiedMessage,
+    LatchClear(MemberId),
+
+    // TODO: Figure out how to represent emotes
+    EmoteAdd(MemberId, MessageId, ()),
+    EmoteRemove(MemberId, MessageId, ()),
+}
+
+pub enum Command {
+    Edit(MessageId, String),
+    Reproxy(MessageId, MemberId),
+    Nick(MemberId, String),
+    ReloadSystemConfig,
+    ExitSéance,
+    UnknownCommand,
+}
+
+pub struct MessageParser {}
+
+static CORRECTION_REGEX: LazyLock<Regex> = LazyLock::new(|| {
+   Regex::new(r"^\*\B+$").unwrap()
+});
+
+impl MessageParser {
+    pub fn parse(message: &FullMessage, secondary_message: Option<&FullMessage>, system_config: &System, latch_state: Option<(MemberId, Timestamp)>) -> ParsedMessage {
+        if message.content == r"\\" {
+            return ParsedMessage::LatchClear(if let Some((member_id, _)) = latch_state {
+                member_id
+            } else {
+                0
+            })
+        }
+
+        if message.content.starts_with(r"\") {
+            return ParsedMessage::UnproxiedMessage
+        }
+
+        if message.content.starts_with(r"!") {
+            return ParsedMessage::Command(
+                MessageParser::parse_command(message, secondary_message, system_config, latch_state)
+            );
+        }
+
+        if CORRECTION_REGEX.is_match(message.content.as_str()) {
+            if let Some(parse) = MessageParser::check_correction(message, secondary_message) {
+                return parse
+            }
+        }
+
+        if let Some(parse) = MessageParser::check_member_patterns(message, system_config) {
+            return parse
+        }
+
+        if let Some(parse) = MessageParser::check_autoproxy(message, latch_state) {
+            return parse
+        }
+
+        // If nothing else
+        ParsedMessage::UnproxiedMessage
+    }
+
+    fn parse_command(message: &FullMessage, secondary_message: Option<&FullMessage>, system_config: &System, latch_state: Option<(MemberId, Timestamp)>) -> Command {
+        
+        // If unable to parse
+        Command::UnknownCommand
+    }
+
+    fn check_correction(message: &FullMessage, secondary_message: Option<&FullMessage>) -> Option<ParsedMessage> {
+        None
+    }
+
+    fn check_member_patterns(message: &FullMessage, system_config: &System) -> Option<ParsedMessage> {
+        let matches_prefix = system_config.members.iter().enumerate().find_map(|(member_id, member)|
+            Some((member_id, member.matches_proxy_prefix(&message)?))
+        );
+
+        if let Some((member_id, matched_content)) = matches_prefix {
+            Some(ParsedMessage::ProxiedMessage {
+                member_id,
+                message_content: matched_content.to_string(),
+                latch: true,
+            })
+        } else {
+            None
+        }
+    }
+
+    fn check_autoproxy(message: &FullMessage, latch_state: Option<(MemberId, Timestamp)>) -> Option<ParsedMessage> {
+        if let Some((member_id, _)) = latch_state {
+            Some(ParsedMessage::ProxiedMessage {
+                member_id,
+                message_content: message.content.clone(),
+                latch: true,
+            })
+        } else {
+            None
+        }
+    }
+}
+
+impl crate::config::Member {
+    pub fn matches_proxy_prefix<'a>(&self, message: &'a FullMessage) -> Option<&'a str> {
+        match self.message_pattern.captures(message.content.as_str()) {
+            None => None,
+            Some(captures) => match captures.name("content") {
+                None => None,
+                Some(matched_content) => Some(matched_content.as_str()),
+            },
+        }
+    }
+}
+
diff --git a/src/system/mod.rs b/src/system/mod.rs
index 4cb99ea..3c6064b 100644
--- a/src/system/mod.rs
+++ b/src/system/mod.rs
@@ -4,7 +4,8 @@ use tokio::{
     sync::{mpsc::{channel, Sender}, RwLock},
     time::sleep,
 };
-use twilight_model::id::{marker::UserMarker, Id};
+use twilight_http::request::channel::reaction::RequestReactionType;
+use twilight_model::{channel::message::ReactionType, id::{marker::UserMarker, Id}};
 use twilight_model::util::Timestamp;
 
 use crate::config::{AutoproxyConfig, AutoproxyLatchScope, Member};
@@ -12,10 +13,15 @@ use crate::config::{AutoproxyConfig, AutoproxyLatchScope, Member};
 mod aggregator;
 mod bot;
 mod types;
+mod message_parser;
+
+use message_parser::MessageParser;
 use aggregator::MessageAggregator;
 use bot::Bot;
 pub use types::*;
 
+use self::message_parser::Command;
+
 
 pub struct Manager {
     pub name: String,
@@ -155,81 +161,37 @@ impl Manager {
     }
 
     async fn handle_message(&mut self, message: TwiMessage, timestamp: Timestamp) {
-        // TODO: Commands
-        if message.content.eq("!panic") {
-           self.bots.iter_mut().next().unwrap().1.shutdown().await;
-        }
+        let parsed_message = MessageParser::parse(&message, None, &self.config, self.latch_state);
 
-        // Escape sequence
-        if message.content.starts_with(r"\") {
-            if message.content == r"\\" {
-                let bot = if let Some((current_member, _)) = self.latch_state.clone() {
-                    self.bots
-                        .get(&current_member)
-                        .expect(format!("No client for member {}", current_member).as_str())
-                } else {
-                    self.bots.iter().next().expect("No clients!").1
-                };
+        match parsed_message {
+            message_parser::ParsedMessage::UnproxiedMessage => (),
 
-                // We don't really care about the outcome here, we don't proxy afterwards
-                let _ = bot.delete_message(message.channel_id, message.id).await;
-                self.latch_state = None
-            } else if message.content.starts_with(r"\\") {
+            message_parser::ParsedMessage::LatchClear(member_id) => {
+                let _ = self.bots.get(&member_id).unwrap().delete_message(message.channel_id, message.id).await;
                 self.latch_state = None;
-            }
-
-            return;
-        }
-
-        // TODO: Non-latching prefixes maybe?
-
-        // Check for prefix
-        let match_prefix =
-            self.config
-                .members
-                .iter()
-                .enumerate()
-                .find_map(|(member_id, member)| {
-                    Some((member_id, member.matches_proxy_prefix(&message)?))
-                });
-        if let Some((member_id, matched_content)) = match_prefix {
-            if let Ok(_) = self.proxy_message(&message, member_id, matched_content).await {
-                self.latch_state = Some((member_id, timestamp));
-                self.update_autoproxy_state_after_message(member_id, timestamp);
-                self.update_status_of_system().await;
-            }
-            return
-        }
+            },
 
-        // Check for autoproxy
-        if let Some(autoproxy_config) = &self.config.autoproxy {
-            match autoproxy_config {
-                AutoproxyConfig::Member { name } => {
-                    let (member_id, _member) = self
-                        .find_member_by_name(&name)
-                        .expect("Invalid autoproxy member name");
-                    self.proxy_message(&message, member_id, message.content.as_str())
-                        .await;
-                }
-                // TODO: Do something with the latch scope
-                // TODO: Do something with presence setting
-                AutoproxyConfig::Latch {
-                    scope,
-                    timeout_seconds,
-                    presence_indicator,
-                } => {
-                    if let Some((member, last_timestamp)) = self.latch_state.clone() {
-                        let time_since_last = timestamp.as_secs() - last_timestamp.as_secs();
-                        if time_since_last <= (*timeout_seconds).into() {
-                            if let Ok(_) = self.proxy_message(&message, member, message.content.as_str()).await {
-                                self.latch_state = Some((member, timestamp));
-                                self.update_autoproxy_state_after_message(member, timestamp);
-                                self.update_status_of_system().await;
-                            }
-                        }
+            message_parser::ParsedMessage::ProxiedMessage { member_id, message_content, latch } => {
+                if let Ok(_) = self.proxy_message(&message, member_id, message_content.as_str()).await {
+                    if latch {
+                        self.update_autoproxy_state_after_message(member_id, timestamp);
+                        self.update_status_of_system().await;
                     }
                 }
-            }
+            },
+
+            message_parser::ParsedMessage::Command(Command::UnknownCommand) => {
+                let member_id = if let Some((member_id, _)) = self.latch_state {
+                    member_id
+                } else {
+                    0
+                };
+
+                let _ = self.bots.get(&member_id).unwrap().react_message(message.channel_id, message.id, &RequestReactionType::Unicode { name: "⁉️" }).await;
+            },
+            message_parser::ParsedMessage::Command(_) => todo!(),
+            message_parser::ParsedMessage::EmoteAdd(_, _, _) => todo!(),
+            message_parser::ParsedMessage::EmoteRemove(_, _, _) => todo!(),
         }
     }
 
@@ -340,15 +302,3 @@ impl Manager {
     }
 }
 
-impl crate::config::Member {
-    pub fn matches_proxy_prefix<'a>(&self, message: &'a TwiMessage) -> Option<&'a str> {
-        match self.message_pattern.captures(message.content.as_str()) {
-            None => None,
-            Some(captures) => match captures.name("content") {
-                None => None,
-                Some(matched_content) => Some(matched_content.as_str()),
-            },
-        }
-    }
-}
-