mod aggregator; mod types; mod plugin; mod util; mod log; use aggregator::MemberEvent; use log::Logger; use twilight_gateway::{Intents, Shard, ShardId}; use twilight_http::Client; pub use types::SystemThreadCommand; use crate::SystemUiEvent; use std::{num::NonZeroU64, sync::Arc}; use tokio::sync::Mutex; use plugin::get_plugins; use std::sync::mpsc::Sender as ThreadSender; use types::{Member, Response, System}; pub struct Manager; impl Manager { pub async fn start(system_name: String, system_config: crate::config::System, ui_sender : ThreadSender<(String, SystemUiEvent)>) { let gateway_intents = Intents::GUILD_MEMBERS | Intents::GUILD_PRESENCES | Intents::GUILD_MESSAGES | Intents::MESSAGE_CONTENT; let system = System { followed_user: NonZeroU64::try_from(system_config.reference_user_id.parse::().unwrap()).unwrap().into(), command_prefix: "!".to_string(), members: system_config.members.iter().map(|member| Member { discord_token: member.discord_token.clone(), message_pattern: member.message_pattern.clone(), shard: Arc::new(Mutex::new(Shard::new( ShardId::ONE, member.discord_token.clone(), gateway_intents.clone(), ))), client: Arc::new(Mutex::new(Client::new(member.discord_token.clone()))) }).collect(), }; let mut message_receiver = aggregator::MessageAggregator::start(&system); let logger = Logger::new(system_name.clone(), system_config.clone(), ui_sender.clone()); let (all_plugins, by_command) = get_plugins(); 'member_event: loop { match message_receiver.recv().await { None => (), Some(MemberEvent::GatewayConnect(member_token)) => { logger.log_connect(member_token).await; }, Some(MemberEvent::GatewayError(member_token)) => { logger.log_err(Some(member_token), "Non-fatal gateway error".to_string()).await; }, Some(MemberEvent::GatewayDisconnect(member_token)) => { logger.log_disconnect(member_token).await; }, Some(MemberEvent::Message(message, seen_by)) => { 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; } } } // 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"); 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;} } for plugin in &all_plugins { plugin.post_response(&logger, &system, &new_message, message.channel_id, &message_response).await; } } }, } }, } } } }