1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
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::{collections::HashMap, num::NonZeroU64, sync::Arc};
use tokio::sync::Mutex;
use plugin::get_plugins;
use std::sync::mpsc::Sender as ThreadSender;
use types::{Member, Response, System};
use std::iter::once;
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::<u64>().unwrap()).unwrap().into(),
command_prefix: "!".to_string(),
members: system_config.members.iter().map(|member| Member {
discord_token: member.discord_token.clone(),
user_id: Arc::new(Mutex::new(None)),
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(),
message_cache: Arc::new(Mutex::new(HashMap::new())),
};
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, user_id)) => {
for member in &system.members {
if member.discord_token == member_token {
{*member.user_id.lock().await = Some(user_id)};
}
}
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(first_word) = words.next() {
if let Some((command, plugin)) = by_command
.get(first_word)
.map(|command| Some(command))
.unwrap_or_else(|| by_command.get(first_word.get(0..1).unwrap())) {
logger.log_line(None, format!("Handling command: {command:?}")).await;
let args : Vec<_> = match command {
plugin::PluginCommand::Word(_) => words.collect(),
plugin::PluginCommand::Char(_) => once(first_word).chain(words).collect(),
};
plugin.handle_command(&logger, &system, &message, *command, args).await;
continue 'member_event;
} else {
logger.log_line(None, format!("Unknown command: {first_word}")).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;}
} else {
system.cache_most_recent_message(new_message.channel_id, new_message.clone(), member.clone()).await;
}
for plugin in &all_plugins {
plugin.post_response(&logger, &system, &new_message, message.channel_id, &message_response).await;
}
}
},
}
},
}
}
}
}
|