1use anyhow::Context as _;
11use moka::future::{Cache, CacheBuilder};
12use once_cell::sync::Lazy;
13use serenity::all::{ChannelId, GuildChannel, GuildId};
14use serenity::client::Context;
15use std::collections::HashMap;
16
17pub struct CacheArgs {
19 pub guild_id: GuildId,
21 pub channel_id: ChannelId,
23}
24
25pub static GUILD_CHANNEL_LIST_CACHE: Lazy<Cache<GuildId, HashMap<ChannelId, GuildChannel>>> = {
29 Lazy::new(|| {
30 CacheBuilder::new(500)
31 .name("guild_channel_list_cache")
32 .time_to_idle(std::time::Duration::from_secs(3600))
33 .time_to_live(std::time::Duration::from_secs(43200))
34 .build()
35 })
36};
37
38pub static GUILD_CHANNEL_CACHE: Lazy<Cache<ChannelId, GuildChannel>> = {
42 Lazy::new(|| {
43 CacheBuilder::new(500)
44 .name("guild_channel_cache")
45 .time_to_idle(std::time::Duration::from_secs(3600))
46 .time_to_live(std::time::Duration::from_secs(43200))
47 .build()
48 })
49};
50
51impl CacheArgs {
52 pub async fn get(&self, ctx: &Context) -> anyhow::Result<GuildChannel> {
59 match GUILD_CHANNEL_CACHE.get(&self.channel_id).await {
60 Some(channel) => Ok(channel),
61 None => {
62 if let Some(channels) = GUILD_CHANNEL_LIST_CACHE.get(&self.guild_id).await {
63 return channels
64 .get(&self.channel_id)
65 .cloned()
66 .ok_or_else(|| anyhow::anyhow!("Channel not found in cache"));
67 }
68
69 let channel_list = self.get_channel_list_from_api(ctx).await?;
70 let channel = match channel_list.get(&self.channel_id).cloned() {
71 Some(c) => c,
72 None => {
73 let data = self
74 .guild_id
75 .get_active_threads(&ctx.http)
76 .await
77 .context("Failed to get active threads")?;
78 data.threads
79 .iter()
80 .find(|t| t.id == self.channel_id)
81 .cloned()
82 .ok_or_else(|| anyhow::anyhow!("Channel not found in cache"))?
83 }
84 };
85
86 GUILD_CHANNEL_CACHE
87 .insert(self.channel_id, channel.clone())
88 .await;
89 Ok(channel)
90 }
91 }
92 }
93
94 async fn get_channel_list_from_api(
96 &self,
97 ctx: &Context,
98 ) -> anyhow::Result<HashMap<ChannelId, GuildChannel>> {
99 let guild = ctx
100 .http
101 .get_guild(self.guild_id)
102 .await
103 .context("Failed to get guild")?;
104 let channels = guild
105 .channels(&ctx)
106 .await
107 .context("Failed to get channel list")?;
108
109 GUILD_CHANNEL_LIST_CACHE
110 .insert(self.guild_id, channels.clone())
111 .await;
112
113 Ok(channels)
114 }
115}