1use anyhow::Context as _;
11use moka::future::{Cache, CacheBuilder};
12use serenity::all::{ChannelId, GuildChannel, GuildId};
13use serenity::client::Context;
14use std::collections::HashMap;
15use std::sync::LazyLock;
16
17pub struct CacheArgs {
19 pub guild_id: GuildId,
21 pub channel_id: ChannelId,
23}
24
25pub static GUILD_CHANNEL_LIST_CACHE: LazyLock<Cache<GuildId, HashMap<ChannelId, GuildChannel>>> = {
29 LazyLock::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: LazyLock<Cache<ChannelId, GuildChannel>> = {
42 LazyLock::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 let channel_list =
63 if let Some(channels) = GUILD_CHANNEL_LIST_CACHE.get(&self.guild_id).await {
64 channels
65 } else {
66 self.get_channel_list_from_api(ctx).await?
67 };
68
69 let channel = match channel_list.get(&self.channel_id).cloned() {
70 Some(c) => c,
71 None => {
72 let data = self
73 .guild_id
74 .get_active_threads(&ctx.http)
75 .await
76 .context("Failed to get active threads")?;
77 data.threads
78 .iter()
79 .find(|t| t.id == self.channel_id)
80 .cloned()
81 .ok_or_else(|| anyhow::anyhow!("Channel not found in cache"))?
82 }
83 };
84
85 GUILD_CHANNEL_CACHE
86 .insert(self.channel_id, channel.clone())
87 .await;
88 Ok(channel)
89 }
90 }
91 }
92
93 async fn get_channel_list_from_api(
95 &self,
96 ctx: &Context,
97 ) -> anyhow::Result<HashMap<ChannelId, GuildChannel>> {
98 let guild = ctx
99 .http
100 .get_guild(self.guild_id)
101 .await
102 .context("Failed to get guild")?;
103 let channels = guild
104 .channels(&ctx)
105 .await
106 .context("Failed to get channel list")?;
107
108 GUILD_CHANNEL_LIST_CACHE
109 .insert(self.guild_id, channels.clone())
110 .await;
111
112 Ok(channels)
113 }
114}