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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
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<Mutex<Option<Id<UserMarker>>>>,
pub message_pattern: Regex,
pub shard: Arc<Mutex<Shard>>,
pub client: Arc<Mutex<Client>>,
}
#[derive(Clone)]
pub struct System {
pub followed_user: Id<UserMarker>,
pub command_prefix: String,
pub members: Vec<Member>,
pub message_cache: Arc<Mutex<HashMap<Id<ChannelMarker>, (Member, Message)>>>
}
#[derive(Clone)]
pub enum Response {
DefaultNoop,
ExplicitNoop { delete_source: bool, member: Option<Member> },
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::<UserMarker>::parse(mention) {
return self.get_member_by_id(mention).await;
}
}
None
}
pub async fn get_member_by_id<'system>(&'system self, search_id: Id<UserMarker>) -> 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<ChannelMarker>, message: Message, member: Member) {
self.message_cache.lock().await.insert(channel, (member, message));
}
pub async fn get_most_recent_message(&self, channel: Id<ChannelMarker>) -> 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::<Vec<_>>();
for member in &self.members {
let client = member.client.lock().await;
let lookup: Result<Vec<Message>, 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<twilight_validate::request::ValidationError> for LookupError {
fn from(_value: twilight_validate::request::ValidationError) -> Self {
Self
}
}
impl From<twilight_http::Error> for LookupError {
fn from(_value: twilight_http::Error) -> Self {
Self
}
}
impl From<twilight_http::response::DeserializeBodyError> for LookupError {
fn from(_value: twilight_http::response::DeserializeBodyError) -> Self {
Self
}
}
|