serenity/cache/mod.rs
1//! A cache containing data received from [`Shard`]s.
2//!
3//! Using the cache allows to avoid REST API requests via the [`http`] module where possible.
4//! Issuing too many requests will lead to ratelimits.
5//!
6//! # Use by Models
7//!
8//! Most models of Discord objects, such as the [`Message`], [`GuildChannel`], or [`Emoji`], have
9//! methods for interacting with that single instance. This feature is only compiled if the `model`
10//! feature is enabled. An example of this is [`Guild::edit`], which performs a check to ensure that
11//! the current user has the [Manage Guild] permission prior to actually performing the HTTP
12//! request. The cache is involved due to the function's use of unlocking the cache and retrieving
13//! the permissions of the current user. This is an inexpensive method of being able to access data
14//! required by these sugary methods.
15//!
16//! # Do I need the Cache?
17//!
18//! If you're asking this, the answer is likely "definitely yes" or "definitely no"; any in-between
19//! tends to be "yes". If you are low on RAM, and need to run on only a couple MB, then the answer
20//! is "definitely no". If you do not care about RAM and want your bot to be able to access data
21//! while needing to hit the REST API as little as possible, then the answer is "yes".
22//!
23//! [`Shard`]: crate::gateway::Shard
24//! [`http`]: crate::http
25//! [Manage Guild]: Permissions::MANAGE_GUILD
26
27use std::collections::{HashSet, VecDeque};
28use std::hash::Hash;
29#[cfg(feature = "temp_cache")]
30use std::sync::Arc;
31#[cfg(feature = "temp_cache")]
32use std::time::Duration;
33
34use dashmap::mapref::entry::Entry;
35use dashmap::mapref::one::{MappedRef, Ref};
36use dashmap::DashMap;
37#[cfg(feature = "temp_cache")]
38use mini_moka::sync::Cache as MokaCache;
39use parking_lot::RwLock;
40use tracing::instrument;
41
42pub use self::cache_update::CacheUpdate;
43pub use self::settings::Settings;
44use crate::model::prelude::*;
45
46mod cache_update;
47mod event;
48mod settings;
49pub(crate) mod wrappers;
50
51#[cfg(feature = "temp_cache")]
52pub(crate) use wrappers::MaybeOwnedArc;
53use wrappers::{BuildHasher, MaybeMap, ReadOnlyMapRef};
54
55type MessageCache = DashMap<ChannelId, HashMap<MessageId, Message>, BuildHasher>;
56
57struct NotSend;
58
59enum CacheRefInner<'a, K, V, T> {
60 #[cfg(feature = "temp_cache")]
61 Arc(Arc<V>),
62 DashRef(Ref<'a, K, V, BuildHasher>),
63 DashMappedRef(MappedRef<'a, K, T, V, BuildHasher>),
64 ReadGuard(parking_lot::RwLockReadGuard<'a, V>),
65}
66
67pub struct CacheRef<'a, K, V, T = ()> {
68 inner: CacheRefInner<'a, K, V, T>,
69 phantom: std::marker::PhantomData<*const NotSend>,
70}
71
72impl<'a, K, V, T> CacheRef<'a, K, V, T> {
73 fn new(inner: CacheRefInner<'a, K, V, T>) -> Self {
74 Self {
75 inner,
76 phantom: std::marker::PhantomData,
77 }
78 }
79
80 #[cfg(feature = "temp_cache")]
81 fn from_arc(inner: MaybeOwnedArc<V>) -> Self {
82 Self::new(CacheRefInner::Arc(inner.get_inner()))
83 }
84
85 fn from_ref(inner: Ref<'a, K, V, BuildHasher>) -> Self {
86 Self::new(CacheRefInner::DashRef(inner))
87 }
88
89 fn from_mapped_ref(inner: MappedRef<'a, K, T, V, BuildHasher>) -> Self {
90 Self::new(CacheRefInner::DashMappedRef(inner))
91 }
92
93 fn from_guard(inner: parking_lot::RwLockReadGuard<'a, V>) -> Self {
94 Self::new(CacheRefInner::ReadGuard(inner))
95 }
96}
97
98impl<K: Eq + Hash, V, T> std::ops::Deref for CacheRef<'_, K, V, T> {
99 type Target = V;
100
101 fn deref(&self) -> &Self::Target {
102 match &self.inner {
103 #[cfg(feature = "temp_cache")]
104 CacheRefInner::Arc(inner) => inner,
105 CacheRefInner::DashRef(inner) => inner.value(),
106 CacheRefInner::DashMappedRef(inner) => inner.value(),
107 CacheRefInner::ReadGuard(inner) => inner,
108 }
109 }
110}
111
112type Never = std::convert::Infallible;
113type MappedGuildRef<'a, T> = CacheRef<'a, GuildId, T, Guild>;
114
115pub type MemberRef<'a> = MappedGuildRef<'a, Member>;
116pub type GuildRoleRef<'a> = MappedGuildRef<'a, Role>;
117pub type UserRef<'a> = CacheRef<'a, UserId, User, Never>;
118pub type GuildRef<'a> = CacheRef<'a, GuildId, Guild, Never>;
119pub type SettingsRef<'a> = CacheRef<'a, Never, Settings, Never>;
120pub type GuildChannelRef<'a> = MappedGuildRef<'a, GuildChannel>;
121pub type CurrentUserRef<'a> = CacheRef<'a, Never, CurrentUser, Never>;
122pub type GuildRolesRef<'a> = MappedGuildRef<'a, HashMap<RoleId, Role>>;
123pub type GuildChannelsRef<'a> = MappedGuildRef<'a, HashMap<ChannelId, GuildChannel>>;
124pub type MessageRef<'a> = CacheRef<'a, ChannelId, Message, HashMap<MessageId, Message>>;
125pub type ChannelMessagesRef<'a> = CacheRef<'a, ChannelId, HashMap<MessageId, Message>, Never>;
126
127#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
128#[derive(Debug)]
129pub(crate) struct CachedShardData {
130 pub total: u32,
131 pub connected: HashSet<ShardId>,
132 pub has_sent_shards_ready: bool,
133}
134
135/// A cache containing data received from [`Shard`]s.
136///
137/// Using the cache allows to avoid REST API requests via the [`http`] module where possible.
138/// Issuing too many requests will lead to ratelimits.
139///
140/// This is the list of cached resources and the events that populate them:
141/// - channels: [`ChannelCreateEvent`], [`ChannelUpdateEvent`], [`GuildCreateEvent`]
142/// - guilds: [`GuildCreateEvent`]
143/// - unavailable_guilds: [`ReadyEvent`], [`GuildDeleteEvent`]
144/// - users: [`GuildMemberAddEvent`], [`GuildMemberRemoveEvent`], [`GuildMembersChunkEvent`],
145/// [`PresenceUpdateEvent`], [`ReadyEvent`]
146/// - presences: [`PresenceUpdateEvent`], [`ReadyEvent`]
147/// - messages: [`MessageCreateEvent`]
148///
149/// The documentation of each event contains the required gateway intents.
150///
151/// [`Shard`]: crate::gateway::Shard
152/// [`http`]: crate::http
153#[cfg_attr(feature = "typesize", derive(typesize::derive::TypeSize))]
154#[derive(Debug)]
155#[non_exhaustive]
156pub struct Cache {
157 // Temp cache:
158 // ---
159 /// Cache of channels that have been fetched via to_channel.
160 ///
161 /// The TTL for each value is configured in CacheSettings.
162 #[cfg(feature = "temp_cache")]
163 pub(crate) temp_channels: MokaCache<ChannelId, MaybeOwnedArc<GuildChannel>, BuildHasher>,
164 /// Cache of private channels created via create_dm_channel.
165 ///
166 /// The TTL for each value is configured in CacheSettings.
167 #[cfg(feature = "temp_cache")]
168 pub(crate) temp_private_channels: MokaCache<UserId, MaybeOwnedArc<PrivateChannel>, BuildHasher>,
169 /// Cache of messages that have been fetched via message.
170 ///
171 /// The TTL for each value is configured in CacheSettings.
172 #[cfg(feature = "temp_cache")]
173 pub(crate) temp_messages: MokaCache<MessageId, MaybeOwnedArc<Message>, BuildHasher>,
174 /// Cache of users who have been fetched from `to_user`.
175 ///
176 /// The TTL for each value is configured in CacheSettings.
177 #[cfg(feature = "temp_cache")]
178 pub(crate) temp_users: MokaCache<UserId, MaybeOwnedArc<User>, BuildHasher>,
179
180 // Channels cache:
181 /// A map of channel ids to the guilds in which the channel data is stored.
182 pub(crate) channels: MaybeMap<ChannelId, GuildId>,
183
184 // Guilds cache:
185 // ---
186 /// A map of guilds with full data available. This includes data like [`Role`]s and [`Emoji`]s
187 /// that are not available through the REST API.
188 pub(crate) guilds: MaybeMap<GuildId, Guild>,
189 /// A list of guilds which are "unavailable".
190 ///
191 /// Additionally, guilds are always unavailable for bot users when a Ready is received. Guilds
192 /// are "sent in" over time through the receiving of [`Event::GuildCreate`]s.
193 pub(crate) unavailable_guilds: MaybeMap<GuildId, ()>,
194
195 // Users cache:
196 // ---
197 /// A map of users that the current user sees.
198 ///
199 /// Users are added to - and updated from - this map via the following received events:
200 ///
201 /// - [`GuildMemberAdd`][`GuildMemberAddEvent`]
202 /// - [`GuildMemberRemove`][`GuildMemberRemoveEvent`]
203 /// - [`GuildMembersChunk`][`GuildMembersChunkEvent`]
204 /// - [`PresenceUpdate`][`PresenceUpdateEvent`]
205 /// - [`Ready`][`ReadyEvent`]
206 ///
207 /// Note, however, that users are _not_ removed from the map on removal events such as
208 /// [`GuildMemberRemove`][`GuildMemberRemoveEvent`], as other structs such as members or
209 /// recipients may still exist.
210 pub(crate) users: MaybeMap<UserId, User>,
211
212 // Messages cache:
213 // ---
214 pub(crate) messages: MessageCache,
215 /// Queue of message IDs for each channel.
216 ///
217 /// This is simply a vecdeque so we can keep track of the order of messages inserted into the
218 /// cache. When a maximum number of messages are in a channel's cache, we can pop the front and
219 /// remove that ID from the cache.
220 pub(crate) message_queue: DashMap<ChannelId, VecDeque<MessageId>, BuildHasher>,
221
222 // Miscellanous fixed-size data
223 // ---
224 /// Information about running shards
225 pub(crate) shard_data: RwLock<CachedShardData>,
226 /// The current user "logged in" and for which events are being received for.
227 ///
228 /// The current user contains information that a regular [`User`] does not, such as whether it
229 /// is a bot, whether the user is verified, etc.
230 ///
231 /// Refer to the documentation for [`CurrentUser`] for more information.
232 pub(crate) user: RwLock<CurrentUser>,
233 /// The settings for the cache.
234 settings: RwLock<Settings>,
235}
236
237impl Cache {
238 /// Creates a new cache.
239 #[inline]
240 #[must_use]
241 pub fn new() -> Self {
242 Self::default()
243 }
244
245 /// Creates a new cache instance with settings applied.
246 ///
247 /// # Examples
248 ///
249 /// ```rust
250 /// use serenity::cache::{Cache, Settings};
251 ///
252 /// let mut settings = Settings::default();
253 /// settings.max_messages = 10;
254 ///
255 /// let cache = Cache::new_with_settings(settings);
256 /// ```
257 #[instrument]
258 pub fn new_with_settings(settings: Settings) -> Self {
259 #[cfg(feature = "temp_cache")]
260 fn temp_cache<K, V>(ttl: Duration) -> MokaCache<K, V, BuildHasher>
261 where
262 K: Hash + Eq + Send + Sync + 'static,
263 V: Clone + Send + Sync + 'static,
264 {
265 MokaCache::builder().time_to_live(ttl).build_with_hasher(BuildHasher::default())
266 }
267
268 Self {
269 #[cfg(feature = "temp_cache")]
270 temp_private_channels: temp_cache(settings.time_to_live),
271 #[cfg(feature = "temp_cache")]
272 temp_channels: temp_cache(settings.time_to_live),
273 #[cfg(feature = "temp_cache")]
274 temp_messages: temp_cache(settings.time_to_live),
275 #[cfg(feature = "temp_cache")]
276 temp_users: temp_cache(settings.time_to_live),
277
278 channels: MaybeMap(settings.cache_channels.then(DashMap::default)),
279
280 guilds: MaybeMap(settings.cache_guilds.then(DashMap::default)),
281 unavailable_guilds: MaybeMap(settings.cache_guilds.then(DashMap::default)),
282
283 users: MaybeMap(settings.cache_users.then(DashMap::default)),
284
285 messages: DashMap::default(),
286 message_queue: DashMap::default(),
287
288 shard_data: RwLock::new(CachedShardData {
289 total: 1,
290 connected: HashSet::new(),
291 has_sent_shards_ready: false,
292 }),
293 user: RwLock::new(CurrentUser::default()),
294 settings: RwLock::new(settings),
295 }
296 }
297
298 /// Fetches the number of [`Member`]s that have not had data received.
299 ///
300 /// The important detail to note here is that this is the number of _member_s that have not had
301 /// data received. A single [`User`] may have multiple associated member objects that have not
302 /// been received.
303 ///
304 /// This can be used in combination with [`Shard::chunk_guild`], and can be used to determine
305 /// how many members have not yet been received.
306 ///
307 /// ```rust,no_run
308 /// # use serenity::model::prelude::*;
309 /// # use serenity::prelude::*;
310 /// struct Handler;
311 ///
312 /// #[serenity::async_trait]
313 /// # #[cfg(feature = "client")]
314 /// impl EventHandler for Handler {
315 /// async fn cache_ready(&self, ctx: Context, _: Vec<GuildId>) {
316 /// println!("{} unknown members", ctx.cache.unknown_members());
317 /// }
318 /// }
319 /// ```
320 ///
321 /// [`Shard::chunk_guild`]: crate::gateway::Shard::chunk_guild
322 pub fn unknown_members(&self) -> u64 {
323 let mut total = 0;
324
325 for guild_entry in self.guilds.iter() {
326 let guild = guild_entry.value();
327
328 let members = guild.members.len() as u64;
329
330 if guild.member_count > members {
331 total += guild.member_count - members;
332 }
333 }
334
335 total
336 }
337
338 /// Fetches a vector of all [`Guild`]s' Ids that are stored in the cache.
339 ///
340 /// Note that if you are utilizing multiple [`Shard`]s, then the guilds retrieved over all
341 /// shards are included in this count -- not just the current [`Context`]'s shard, if accessing
342 /// from one.
343 ///
344 /// # Examples
345 ///
346 /// Print all of the Ids of guilds in the Cache:
347 ///
348 /// ```rust,no_run
349 /// # use serenity::model::prelude::*;
350 /// # use serenity::prelude::*;
351 /// #
352 /// struct Handler;
353 ///
354 /// #[serenity::async_trait]
355 /// impl EventHandler for Handler {
356 /// async fn ready(&self, context: Context, _: Ready) {
357 /// let guilds = context.cache.guilds().len();
358 ///
359 /// println!("Guilds in the Cache: {}", guilds);
360 /// }
361 /// }
362 /// ```
363 ///
364 /// [`Context`]: crate::client::Context
365 /// [`Shard`]: crate::gateway::Shard
366 pub fn guilds(&self) -> Vec<GuildId> {
367 let unavailable_guilds = self.unavailable_guilds();
368
369 let unavailable_guild_ids = unavailable_guilds.iter().map(|i| *i.key());
370
371 self.guilds.iter().map(|i| *i.key()).chain(unavailable_guild_ids).collect()
372 }
373
374 /// Retrieves a [`GuildChannel`] from the cache based on the given Id.
375 #[inline]
376 #[deprecated = "Use Cache::guild and Guild::channels instead"]
377 pub fn channel<C: Into<ChannelId>>(&self, id: C) -> Option<GuildChannelRef<'_>> {
378 self.channel_(id.into())
379 }
380
381 fn channel_(&self, id: ChannelId) -> Option<GuildChannelRef<'_>> {
382 let guild_id = *self.channels.get(&id)?;
383 let guild_ref = self.guilds.get(&guild_id)?;
384 let channel = guild_ref.try_map(|g| g.channels.get(&id)).ok();
385 if let Some(channel) = channel {
386 return Some(CacheRef::from_mapped_ref(channel));
387 }
388
389 #[cfg(feature = "temp_cache")]
390 {
391 if let Some(channel) = self.temp_channels.get(&id) {
392 return Some(CacheRef::from_arc(channel));
393 }
394 }
395
396 None
397 }
398
399 /// Get a reference to the cached messages for a channel based on the given `Id`.
400 ///
401 /// # Examples
402 ///
403 /// Find all messages by user ID 8 in channel ID 7:
404 ///
405 /// ```rust,no_run
406 /// # let cache: serenity::cache::Cache = todo!();
407 /// let messages_in_channel = cache.channel_messages(7);
408 /// let messages_by_user = messages_in_channel
409 /// .as_ref()
410 /// .map(|msgs| msgs.values().filter(|m| m.author.id == 8).collect::<Vec<_>>());
411 /// ```
412 pub fn channel_messages(
413 &self,
414 channel_id: impl Into<ChannelId>,
415 ) -> Option<ChannelMessagesRef<'_>> {
416 self.messages.get(&channel_id.into()).map(CacheRef::from_ref)
417 }
418
419 /// Gets a reference to a guild from the cache based on the given `id`.
420 ///
421 /// # Examples
422 ///
423 /// Retrieve a guild from the cache and print its name:
424 ///
425 /// ```rust,no_run
426 /// # use serenity::cache::Cache;
427 /// #
428 /// # let cache = Cache::default();
429 /// // assuming the cache is in scope, e.g. via `Context`
430 /// if let Some(guild) = cache.guild(7) {
431 /// println!("Guild name: {}", guild.name);
432 /// };
433 /// ```
434 #[inline]
435 pub fn guild<G: Into<GuildId>>(&self, id: G) -> Option<GuildRef<'_>> {
436 self.guild_(id.into())
437 }
438
439 fn guild_(&self, id: GuildId) -> Option<GuildRef<'_>> {
440 self.guilds.get(&id).map(CacheRef::from_ref)
441 }
442
443 /// Returns the number of cached guilds.
444 pub fn guild_count(&self) -> usize {
445 self.guilds.len()
446 }
447
448 /// Retrieves a [`Guild`]'s member from the cache based on the guild's and user's given Ids.
449 ///
450 /// # Examples
451 ///
452 /// Retrieving the member object of the user that posted a message, in a
453 /// [`EventHandler::message`] context:
454 ///
455 /// ```rust,no_run
456 /// # use serenity::cache::Cache;
457 /// # use serenity::http::Http;
458 /// # use serenity::model::channel::Message;
459 /// #
460 /// # async fn run(http: Http, cache: Cache, message: Message) {
461 /// #
462 /// let roles_len = {
463 /// let channel = match cache.channel(message.channel_id) {
464 /// Some(channel) => channel,
465 /// None => {
466 /// if let Err(why) = message.channel_id.say(http, "Error finding channel data").await {
467 /// println!("Error sending message: {:?}", why);
468 /// }
469 /// return;
470 /// },
471 /// };
472 ///
473 /// cache.member(channel.guild_id, message.author.id).map(|m| m.roles.len())
474 /// };
475 ///
476 /// let message_res = if let Some(roles_len) = roles_len {
477 /// let msg = format!("You have {} roles", roles_len);
478 /// message.channel_id.say(&http, &msg).await
479 /// } else {
480 /// message.channel_id.say(&http, "Error finding member data").await
481 /// };
482 ///
483 /// if let Err(why) = message_res {
484 /// println!("Error sending message: {:?}", why);
485 /// }
486 /// # }
487 /// ```
488 ///
489 /// [`EventHandler::message`]: crate::client::EventHandler::message
490 /// [`members`]: crate::model::guild::Guild::members
491 #[inline]
492 #[deprecated = "Use Cache::guild and Guild::members instead"]
493 pub fn member(
494 &self,
495 guild_id: impl Into<GuildId>,
496 user_id: impl Into<UserId>,
497 ) -> Option<MemberRef<'_>> {
498 self.member_(guild_id.into(), user_id.into())
499 }
500
501 fn member_(&self, guild_id: GuildId, user_id: UserId) -> Option<MemberRef<'_>> {
502 let member = self.guilds.get(&guild_id)?.try_map(|g| g.members.get(&user_id)).ok()?;
503 Some(CacheRef::from_mapped_ref(member))
504 }
505
506 #[inline]
507 #[deprecated = "Use Cache::guild and Guild::roles instead"]
508 pub fn guild_roles(&self, guild_id: impl Into<GuildId>) -> Option<GuildRolesRef<'_>> {
509 self.guild_roles_(guild_id.into())
510 }
511
512 fn guild_roles_(&self, guild_id: GuildId) -> Option<GuildRolesRef<'_>> {
513 let roles = self.guilds.get(&guild_id)?.map(|g| &g.roles);
514 Some(CacheRef::from_mapped_ref(roles))
515 }
516
517 /// This method clones and returns all unavailable guilds.
518 #[inline]
519 pub fn unavailable_guilds(&self) -> ReadOnlyMapRef<'_, GuildId, ()> {
520 self.unavailable_guilds.as_read_only()
521 }
522
523 /// This method returns all channels from a guild of with the given `guild_id`.
524 #[inline]
525 #[deprecated = "Use Cache::guild and Guild::channels instead"]
526 pub fn guild_channels(&self, guild_id: impl Into<GuildId>) -> Option<GuildChannelsRef<'_>> {
527 self.guild_channels_(guild_id.into())
528 }
529
530 fn guild_channels_(&self, guild_id: GuildId) -> Option<GuildChannelsRef<'_>> {
531 let channels = self.guilds.get(&guild_id)?.map(|g| &g.channels);
532 Some(CacheRef::from_mapped_ref(channels))
533 }
534
535 /// Returns the number of guild channels in the cache.
536 pub fn guild_channel_count(&self) -> usize {
537 self.channels.len()
538 }
539
540 /// Returns the number of shards.
541 #[inline]
542 pub fn shard_count(&self) -> u32 {
543 self.shard_data.read().total
544 }
545
546 /// Retrieves a [`Channel`]'s message from the cache based on the channel's and message's given
547 /// Ids.
548 ///
549 /// **Note**: This will clone the entire message.
550 ///
551 /// # Examples
552 ///
553 /// Retrieving the message object from a channel, in a [`EventHandler::message`] context:
554 ///
555 /// ```rust,no_run
556 /// # use serenity::cache::Cache;
557 /// # use serenity::model::channel::Message;
558 /// #
559 /// # fn run(cache: Cache, message: Message) {
560 /// #
561 /// match cache.message(message.channel_id, message.id) {
562 /// Some(m) => assert_eq!(message.content, m.content),
563 /// None => println!("No message found in cache."),
564 /// };
565 /// # }
566 /// ```
567 ///
568 /// [`EventHandler::message`]: crate::client::EventHandler::message
569 #[inline]
570 pub fn message<C, M>(&self, channel_id: C, message_id: M) -> Option<MessageRef<'_>>
571 where
572 C: Into<ChannelId>,
573 M: Into<MessageId>,
574 {
575 self.message_(channel_id.into(), message_id.into())
576 }
577
578 fn message_(&self, channel_id: ChannelId, message_id: MessageId) -> Option<MessageRef<'_>> {
579 #[cfg(feature = "temp_cache")]
580 if let Some(message) = self.temp_messages.get(&message_id) {
581 return Some(CacheRef::from_arc(message));
582 }
583
584 let channel_messages = self.messages.get(&channel_id)?;
585 let message = channel_messages.try_map(|messages| messages.get(&message_id)).ok()?;
586 Some(CacheRef::from_mapped_ref(message))
587 }
588
589 /// Retrieves a [`Guild`]'s role by their Ids.
590 ///
591 /// **Note**: This will clone the entire role. Instead, retrieve the guild and retrieve from
592 /// the guild's [`roles`] map to avoid this.
593 ///
594 /// # Examples
595 ///
596 /// Retrieve a role from the cache and print its name:
597 ///
598 /// ```rust,no_run
599 /// # use serenity::cache::Cache;
600 /// #
601 /// # let cache = Cache::default();
602 /// // assuming the cache is in scope, e.g. via `Context`
603 /// if let Some(role) = cache.role(7, 77) {
604 /// println!("Role with Id 77 is called {}", role.name);
605 /// };
606 /// ```
607 ///
608 /// [`Guild`]: crate::model::guild::Guild
609 /// [`roles`]: crate::model::guild::Guild::roles
610 #[inline]
611 #[deprecated = "Use Cache::guild and Guild::roles instead"]
612 pub fn role<G, R>(&self, guild_id: G, role_id: R) -> Option<GuildRoleRef<'_>>
613 where
614 G: Into<GuildId>,
615 R: Into<RoleId>,
616 {
617 self.role_(guild_id.into(), role_id.into())
618 }
619
620 fn role_(&self, guild_id: GuildId, role_id: RoleId) -> Option<GuildRoleRef<'_>> {
621 let role = self.guilds.get(&guild_id)?.try_map(|g| g.roles.get(&role_id)).ok()?;
622 Some(CacheRef::from_mapped_ref(role))
623 }
624
625 /// Returns the settings.
626 ///
627 /// # Examples
628 ///
629 /// Printing the maximum number of messages in a channel to be cached:
630 ///
631 /// ```rust
632 /// use serenity::cache::Cache;
633 ///
634 /// # fn test() {
635 /// let mut cache = Cache::new();
636 /// println!("Max settings: {}", cache.settings().max_messages);
637 /// # }
638 /// ```
639 pub fn settings(&self) -> SettingsRef<'_> {
640 CacheRef::from_guard(self.settings.read())
641 }
642
643 /// Sets the maximum amount of messages per channel to cache.
644 ///
645 /// By default, no messages will be cached.
646 pub fn set_max_messages(&self, max: usize) {
647 self.settings.write().max_messages = max;
648 }
649
650 /// Retrieves a [`User`] from the cache's [`Self::users`] map, if it exists.
651 ///
652 /// The only advantage of this method is that you can pass in anything that is indirectly a
653 /// [`UserId`].
654 ///
655 /// # Examples
656 ///
657 /// Retrieve a user from the cache and print their name:
658 ///
659 /// ```rust,no_run
660 /// # use serenity::client::Context;
661 /// #
662 /// # async fn test(context: &Context) -> Result<(), Box<dyn std::error::Error>> {
663 /// if let Some(user) = context.cache.user(7) {
664 /// println!("User with Id 7 is currently named {}", user.name);
665 /// }
666 /// # Ok(())
667 /// # }
668 /// ```
669 #[inline]
670 pub fn user<U: Into<UserId>>(&self, user_id: U) -> Option<UserRef<'_>> {
671 self.user_(user_id.into())
672 }
673
674 #[cfg(feature = "temp_cache")]
675 fn user_(&self, user_id: UserId) -> Option<UserRef<'_>> {
676 if let Some(user) = self.users.get(&user_id) {
677 Some(CacheRef::from_ref(user))
678 } else {
679 self.temp_users.get(&user_id).map(CacheRef::from_arc)
680 }
681 }
682
683 #[cfg(not(feature = "temp_cache"))]
684 fn user_(&self, user_id: UserId) -> Option<UserRef<'_>> {
685 self.users.get(&user_id).map(CacheRef::from_ref)
686 }
687
688 /// Clones all users and returns them.
689 #[inline]
690 pub fn users(&self) -> ReadOnlyMapRef<'_, UserId, User> {
691 self.users.as_read_only()
692 }
693
694 /// Returns the amount of cached users.
695 #[inline]
696 pub fn user_count(&self) -> usize {
697 self.users.len()
698 }
699
700 /// This method provides a reference to the user used by the bot.
701 #[inline]
702 pub fn current_user(&self) -> CurrentUserRef<'_> {
703 CacheRef::from_guard(self.user.read())
704 }
705
706 /// Returns a channel category matching the given ID
707 #[deprecated = "Use Cache::guild, Guild::channels, and GuildChannel::kind"]
708 pub fn category(&self, channel_id: ChannelId) -> Option<GuildChannelRef<'_>> {
709 #[allow(deprecated)]
710 let channel = self.channel(channel_id)?;
711 if channel.kind == ChannelType::Category {
712 Some(channel)
713 } else {
714 None
715 }
716 }
717
718 /// Returns the parent category of the given channel ID.
719 #[deprecated = "Use Cache::guild, Guild::channels, and GuildChannel::parent_id"]
720 pub fn channel_category_id(&self, channel_id: ChannelId) -> Option<ChannelId> {
721 #[allow(deprecated)]
722 self.channel(channel_id)?.parent_id
723 }
724
725 /// Clones all channel categories in the given guild and returns them.
726 pub fn guild_categories(&self, guild_id: GuildId) -> Option<HashMap<ChannelId, GuildChannel>> {
727 let guild = self.guilds.get(&guild_id)?;
728 Some(
729 guild
730 .channels
731 .iter()
732 .filter(|(_id, channel)| channel.kind == ChannelType::Category)
733 .map(|(id, channel)| (*id, channel.clone()))
734 .collect(),
735 )
736 }
737
738 /// Updates the cache with the update implementation for an event or other custom update
739 /// implementation.
740 ///
741 /// Refer to the documentation for [`CacheUpdate`] for more information.
742 ///
743 /// # Examples
744 ///
745 /// Refer to the [`CacheUpdate` examples].
746 ///
747 /// [`CacheUpdate` examples]: CacheUpdate#examples
748 #[instrument(skip(self, e))]
749 pub fn update<E: CacheUpdate>(&self, e: &mut E) -> Option<E::Output> {
750 e.update(self)
751 }
752
753 pub(crate) fn update_user_entry(&self, user: &User) {
754 if let Some(users) = &self.users.0 {
755 match users.entry(user.id) {
756 Entry::Vacant(e) => {
757 e.insert(user.clone());
758 },
759 Entry::Occupied(mut e) => {
760 e.get_mut().clone_from(user);
761 },
762 }
763 }
764 }
765}
766
767impl Default for Cache {
768 fn default() -> Self {
769 Self::new_with_settings(Settings::default())
770 }
771}
772
773#[cfg(test)]
774mod test {
775
776 use crate::cache::{Cache, CacheUpdate, Settings};
777 use crate::model::prelude::*;
778
779 #[test]
780 fn test_cache_messages() {
781 let settings = Settings {
782 max_messages: 2,
783 ..Default::default()
784 };
785 let cache = Cache::new_with_settings(settings);
786
787 // Test inserting one message into a channel's message cache.
788 let mut event = MessageCreateEvent {
789 message: Message {
790 id: MessageId::new(3),
791 guild_id: Some(GuildId::new(1)),
792 ..Default::default()
793 },
794 };
795
796 // Check that the channel cache doesn't exist.
797 assert!(!cache.messages.contains_key(&event.message.channel_id));
798 // Add first message, none because message ID 2 doesn't already exist.
799 assert!(event.update(&cache).is_none());
800 // None, it only returns the oldest message if the cache was already full.
801 assert!(event.update(&cache).is_none());
802 // Assert there's only 1 message in the channel's message cache.
803 assert_eq!(cache.messages.get(&event.message.channel_id).unwrap().len(), 1);
804
805 // Add a second message, assert that channel message cache length is 2.
806 event.message.id = MessageId::new(4);
807 assert!(event.update(&cache).is_none());
808 assert_eq!(cache.messages.get(&event.message.channel_id).unwrap().len(), 2);
809
810 // Add a third message, the first should now be removed.
811 event.message.id = MessageId::new(5);
812 assert!(event.update(&cache).is_some());
813
814 {
815 let channel = cache.messages.get(&event.message.channel_id).unwrap();
816
817 assert_eq!(channel.len(), 2);
818 // Check that the first message is now removed.
819 assert!(!channel.contains_key(&MessageId::new(3)));
820 }
821
822 let channel = GuildChannel {
823 id: event.message.channel_id,
824 guild_id: event.message.guild_id.unwrap(),
825 ..Default::default()
826 };
827
828 // Add a channel delete event to the cache, the cached messages for that channel should now
829 // be gone.
830 let mut delete = ChannelDeleteEvent {
831 channel: channel.clone(),
832 };
833 assert!(cache.update(&mut delete).is_some());
834 assert!(!cache.messages.contains_key(&delete.channel.id));
835
836 // Test deletion of a guild channel's message cache when a GuildDeleteEvent is received.
837 let mut guild_create = GuildCreateEvent {
838 guild: Guild {
839 id: GuildId::new(1),
840 channels: HashMap::from([(ChannelId::new(2), channel)]),
841 ..Default::default()
842 },
843 };
844 assert!(cache.update(&mut guild_create).is_none());
845 assert!(cache.update(&mut event).is_none());
846
847 let mut guild_delete = GuildDeleteEvent {
848 guild: UnavailableGuild {
849 id: GuildId::new(1),
850 unavailable: false,
851 },
852 };
853
854 // The guild existed in the cache, so the cache's guild is returned by the update.
855 assert!(cache.update(&mut guild_delete).is_some());
856
857 // Assert that the channel's message cache no longer exists.
858 assert!(!cache.messages.contains_key(&ChannelId::new(2)));
859 }
860}