1use std::{hash::Hash, mem};
2
3use serde::{Deserialize, Serialize, de::DeserializeOwned};
4use twilight_model::{
5 gateway::payload::incoming::GuildUpdate,
6 guild::{
7 AfkTimeout, DefaultMessageNotificationLevel, ExplicitContentFilter, Guild, GuildFeature,
8 MfaLevel, NSFWLevel, Permissions, PremiumTier, SystemChannelFlags, VerificationLevel,
9 },
10 id::{
11 Id,
12 marker::{ApplicationMarker, ChannelMarker, GuildMarker, UserMarker},
13 },
14 util::{ImageHash, Timestamp},
15};
16
17use crate::{
18 Cache, Error,
19 repository::{MappedSetRepository, Repository},
20};
21
22#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
23pub struct CachedGuild {
24 pub afk_channel_id: Option<Id<ChannelMarker>>,
25 pub afk_timeout: AfkTimeout,
26 pub application_id: Option<Id<ApplicationMarker>>,
27 pub banner: Option<ImageHash>,
28 pub default_message_notifications: DefaultMessageNotificationLevel,
29 pub description: Option<String>,
30 pub discovery_splash: Option<ImageHash>,
31 pub explicit_content_filter: ExplicitContentFilter,
32 pub features: Vec<GuildFeature>,
33 pub icon: Option<ImageHash>,
34 pub id: Id<GuildMarker>,
35 pub joined_at: Option<Timestamp>,
36 pub large: bool,
37 pub max_members: Option<u64>,
38 pub max_presences: Option<u64>,
39 pub max_video_channel_users: Option<u64>,
40 pub member_count: Option<u64>,
41 pub mfa_level: MfaLevel,
42 pub name: String,
43 pub nsfw_level: NSFWLevel,
44 pub owner_id: Id<UserMarker>,
45 pub owner: Option<bool>,
46 pub permissions: Option<Permissions>,
47 pub preferred_locale: String,
48 pub premium_progress_bar_enabled: bool,
49 pub premium_subscription_count: Option<u64>,
50 pub premium_tier: PremiumTier,
51 pub public_updates_channel_id: Option<Id<ChannelMarker>>,
52 pub rules_channel_id: Option<Id<ChannelMarker>>,
53 pub safety_alerts_channel_id: Option<Id<ChannelMarker>>,
54 pub splash: Option<ImageHash>,
55 pub system_channel_id: Option<Id<ChannelMarker>>,
56 pub system_channel_flags: SystemChannelFlags,
57 pub unavailable: Option<bool>,
58 pub vanity_url_code: Option<String>,
59 pub verification_level: VerificationLevel,
60 pub widget_channel_id: Option<Id<ChannelMarker>>,
61 pub widget_enabled: Option<bool>,
62}
63
64impl CachedGuild {
65 pub(crate) fn update_with_guild_update(&mut self, guild_update: &GuildUpdate) {
66 self.afk_channel_id = guild_update.afk_channel_id;
67 self.afk_timeout = guild_update.afk_timeout;
68 self.banner = guild_update.banner;
69 self.default_message_notifications = guild_update.default_message_notifications;
70 self.description = guild_update.description.clone();
71 self.features = guild_update.features.clone();
72 self.icon = guild_update.icon;
73 self.max_members = guild_update.max_members;
74 self.max_presences = Some(guild_update.max_presences.unwrap_or(25000));
75 self.mfa_level = guild_update.mfa_level;
76 self.name = guild_update.name.clone();
77 self.nsfw_level = guild_update.nsfw_level;
78 self.owner = guild_update.owner;
79 self.owner_id = guild_update.owner_id;
80 self.permissions = guild_update.permissions;
81 self.preferred_locale = guild_update.preferred_locale.clone();
82 self.premium_tier = guild_update.premium_tier;
83 self.premium_subscription_count
84 .replace(guild_update.premium_subscription_count.unwrap_or_default());
85 self.splash = guild_update.splash;
86 self.system_channel_id = guild_update.system_channel_id;
87 self.verification_level = guild_update.verification_level;
88 self.vanity_url_code = guild_update.vanity_url_code.clone();
89 self.widget_channel_id = guild_update.widget_channel_id;
90 self.widget_enabled = guild_update.widget_enabled;
91 }
92}
93
94impl From<Guild> for CachedGuild {
95 fn from(guild: Guild) -> Self {
96 let Guild {
97 afk_channel_id,
98 afk_timeout,
99 application_id,
100 banner,
101 default_message_notifications,
102 description,
103 discovery_splash,
104 explicit_content_filter,
105 features,
106 icon,
107 id,
108 joined_at,
109 large,
110 max_members,
111 max_presences,
112 max_video_channel_users,
113 member_count,
114 mfa_level,
115 name,
116 nsfw_level,
117 owner_id,
118 owner,
119 permissions,
120 preferred_locale,
121 premium_progress_bar_enabled,
122 premium_subscription_count,
123 premium_tier,
124 public_updates_channel_id,
125 rules_channel_id,
126 safety_alerts_channel_id,
127 splash,
128 system_channel_flags,
129 system_channel_id,
130 unavailable,
131 vanity_url_code,
132 verification_level,
133 widget_channel_id,
134 widget_enabled,
135 ..
136 } = guild;
137
138 Self {
139 afk_channel_id,
140 afk_timeout,
141 application_id,
142 banner,
143 default_message_notifications,
144 description,
145 discovery_splash,
146 explicit_content_filter,
147 features,
148 icon,
149 id,
150 joined_at,
151 large,
152 max_members,
153 max_presences,
154 max_video_channel_users,
155 member_count,
156 mfa_level,
157 name,
158 nsfw_level,
159 owner_id,
160 owner,
161 permissions,
162 preferred_locale,
163 premium_progress_bar_enabled,
164 premium_subscription_count,
165 premium_tier,
166 public_updates_channel_id,
167 rules_channel_id,
168 safety_alerts_channel_id,
169 splash,
170 system_channel_id,
171 system_channel_flags,
172 unavailable,
173 vanity_url_code,
174 verification_level,
175 widget_channel_id,
176 widget_enabled,
177 }
178 }
179}
180
181impl PartialEq<Guild> for CachedGuild {
182 fn eq(&self, other: &Guild) -> bool {
183 self.afk_channel_id == other.afk_channel_id
184 && self.afk_timeout == other.afk_timeout
185 && self.application_id == other.application_id
186 && self.banner == other.banner
187 && self.default_message_notifications == other.default_message_notifications
188 && self.description == other.description
189 && self.discovery_splash == other.discovery_splash
190 && self.explicit_content_filter == other.explicit_content_filter
191 && self.features == other.features
192 && self.icon == other.icon
193 && self.joined_at == other.joined_at
194 && self.large == other.large
195 && self.max_members == other.max_members
196 && self.max_presences == other.max_presences
197 && self.max_video_channel_users == other.max_video_channel_users
198 && self.member_count == other.member_count
199 && self.mfa_level == other.mfa_level
200 && self.name == other.name
201 && self.nsfw_level == other.nsfw_level
202 && self.owner_id == other.owner_id
203 && self.owner == other.owner
204 && self.permissions == other.permissions
205 && self.preferred_locale == other.preferred_locale
206 && self.premium_progress_bar_enabled == other.premium_progress_bar_enabled
207 && self.premium_subscription_count == other.premium_subscription_count
208 && self.premium_tier == other.premium_tier
209 && self.public_updates_channel_id == other.public_updates_channel_id
210 && self.rules_channel_id == other.rules_channel_id
211 && self.safety_alerts_channel_id == other.safety_alerts_channel_id
212 && self.splash == other.splash
213 && self.system_channel_id == other.system_channel_id
214 && self.system_channel_flags == other.system_channel_flags
215 && self.unavailable == other.unavailable
216 && self.vanity_url_code == other.vanity_url_code
217 && self.verification_level == other.verification_level
218 && self.widget_channel_id == other.widget_channel_id
219 && self.widget_enabled == other.widget_enabled
220 }
221}
222
223impl Cache {
224 pub(crate) async fn unavailable_guild(&self, guild_id: Id<GuildMarker>) -> Result<(), Error> {
225 self.unavailable_guilds.insert(&guild_id).await?;
226 self.delete_guild(guild_id, true).await?;
227
228 Ok(())
229 }
230
231 pub(crate) async fn cache_guild(&self, mut guild: Guild) -> Result<(), Error> {
232 for channel in &mut guild.channels {
233 channel.guild_id = Some(guild.id);
234 }
235 for thread in &mut guild.threads {
236 thread.guild_id = Some(guild.id);
237 }
238
239 self.cache_channels(mem::take(&mut guild.channels)).await?;
240 self.cache_channels(mem::take(&mut guild.threads)).await?;
241 self.cache_emojis(guild.id, mem::take(&mut guild.emojis))
242 .await?;
243 self.cache_members(guild.id, mem::take(&mut guild.members))
244 .await?;
245 self.cache_presences(guild.id, mem::take(&mut guild.presences))
246 .await?;
247 self.cache_roles(guild.id, mem::take(&mut guild.roles))
248 .await?;
249 self.cache_stickers(guild.id, mem::take(&mut guild.stickers))
250 .await?;
251 self.cache_voice_states(mem::take(&mut guild.voice_states))
252 .await?;
253 self.cache_stage_instances(guild.id, mem::take(&mut guild.stage_instances))
254 .await?;
255 self.cache_guild_scheduled_events(guild.id, mem::take(&mut guild.guild_scheduled_events))
256 .await?;
257
258 self.unavailable_guilds.remove(&guild.id).await?;
259 self.guilds
260 .insert(&guild.id, &CachedGuild::from(guild.clone()))
261 .await?;
262
263 Ok(())
264 }
265
266 pub(crate) async fn delete_guild(
267 &self,
268 guild_id: Id<GuildMarker>,
269 unavailable: bool,
270 ) -> Result<(), Error> {
271 async fn remove_ids<
272 T: Eq + Hash + Serialize + DeserializeOwned,
273 U: Serialize + DeserializeOwned,
274 >(
275 guild_map: &MappedSetRepository<Id<GuildMarker>, T>,
276 container: &Repository<T, U>,
277 guild_id: Id<GuildMarker>,
278 ) -> Result<(), Error> {
279 let ids = guild_map.members(&guild_id).await?;
280 container.remove_multi(&ids).await?;
281 Ok(())
282 }
283
284 if unavailable {
285 if let Some(mut guild) = self.guilds.get(&guild_id).await? {
286 guild.unavailable = Some(true);
287 self.guilds.insert(&guild_id, &guild).await?;
288 }
289 } else {
290 self.guilds.remove(&guild_id).await?;
291 }
292
293 remove_ids(&self.guild_channels, &self.channels, guild_id).await?;
294 remove_ids(&self.guild_emojis, &self.emojis, guild_id).await?;
295 remove_ids(&self.guild_roles, &self.roles, guild_id).await?;
296 remove_ids(&self.guild_stickers, &self.stickers, guild_id).await?;
297
298 self.voice_state_guilds.clear(&guild_id).await?;
299
300 let members_to_remove: Vec<_> = self
301 .guild_members
302 .members(&guild_id)
303 .await?
304 .into_iter()
305 .map(|user_id| (guild_id, user_id))
306 .collect();
307 self.members.remove_multi(members_to_remove.iter()).await?;
308 self.guild_members.clear(&guild_id).await?;
309
310 let presences_to_remove: Vec<_> = self
311 .guild_presences
312 .members(&guild_id)
313 .await?
314 .into_iter()
315 .map(|user_id| (guild_id, user_id))
316 .collect();
317 self.presences
318 .remove_multi(presences_to_remove.iter())
319 .await?;
320 self.guild_presences.clear(&guild_id).await?;
321
322 Ok(())
323 }
324
325 pub(crate) async fn update_guild(&self, guild_update: &GuildUpdate) -> Result<(), Error> {
326 let Some(mut guild) = self.guilds.get(&guild_update.id).await? else {
327 return Ok(());
328 };
329
330 guild.update_with_guild_update(guild_update);
331 self.guilds.insert(&guild.id, &guild).await?;
332
333 Ok(())
334 }
335}