use regex::Regex; use twilight_http::Client; use twilight_gateway::Shard; use twilight_mention::ParseMention; use twilight_model::channel::Message; use twilight_model::id::{marker::{ChannelMarker, UserMarker}, Id}; use std::{collections::HashMap, sync::Arc}; use tokio::sync::Mutex; use futures::future::join_all; #[derive(Clone)] pub struct Member { pub discord_token: String, pub user_id: Arc>>>, pub message_pattern: Regex, pub shard: Arc>, pub client: Arc>, } #[derive(Clone)] pub struct System { pub followed_user: Id, pub command_prefix: String, pub members: Vec, pub message_cache: Arc, (Member, Message)>>> } #[derive(Clone)] pub enum Response { DefaultNoop, ExplicitNoop { delete_source: bool, member: Option }, Proxy {member: Member, content: String}, } pub enum SystemThreadCommand { Restart, ReloadConfig, ShutdownSystem, ShutdownAll, } impl System { pub async fn resolve_mention<'system>(&'system self, maybe_mention: Option<&str>) -> Option<&'system Member> { if let Some(mention) = maybe_mention { if let Ok(mention) = Id::::parse(mention) { return self.get_member_by_id(mention).await; } } None } pub async fn get_member_by_id<'system>(&'system self, search_id: Id) -> Option<&'system Member> { for member in &self.members { let is_member = {member.user_id.lock().await.map(|id| id == search_id).unwrap_or(false)}; if is_member { return Some(&member) } } return None } pub async fn cache_most_recent_message(&self, channel: Id, message: Message, member: Member) { self.message_cache.lock().await.insert(channel, (member, message)); } pub async fn get_most_recent_message(&self, channel: Id) -> Option<(Message, Member)> { self.message_cache.lock().await.get(&channel).map(|(member, message)| (message.clone(), member.clone())) } // Returns either the most recent message or replied message, with bool to indicate // if it was the most recent message (and the cache should be updated if that's modified) pub async fn resolve_message_target(&self, message: &Message) -> Option<(Message, bool)> { let most_recent_message = self.get_most_recent_message(message.channel_id).await; if let Some(result) = async { match message.kind { twilight_model::channel::message::MessageType::Regular => Some((most_recent_message?.0, true)), twilight_model::channel::message::MessageType::Reply => Some(( message.referenced_message.as_ref()?.as_ref().clone(), message.referenced_message.as_ref()?.id == most_recent_message?.0.id )), _ => return None, } }.await { return Some(result) } else { let member_ids = join_all(self.members.iter().map(|member| async { member.user_id.lock().await.clone() })).await.iter().filter_map(|opt| *opt).collect::>(); for member in &self.members { let client = member.client.lock().await; let lookup: Result, LookupError> = async { Ok(client .channel_messages(message.channel_id) .limit(10)? .await? .model() .await? ) }.await; if let Ok(recent_messages) = lookup { let recent_message = recent_messages.into_iter().find( |message| member_ids.contains(&message.author.id) ); if let Some(most_recent_message) = recent_message { return Some((most_recent_message, true)) } else { // Successful lookup, but no recent messages, there's no point // checking with the other members return None } } } None } } } struct LookupError; impl From for LookupError { fn from(_value: twilight_validate::request::ValidationError) -> Self { Self } } impl From for LookupError { fn from(_value: twilight_http::Error) -> Self { Self } } impl From for LookupError { fn from(_value: twilight_http::response::DeserializeBodyError) -> Self { Self } }