1use crate::{
2 checks::EnabledCheckData,
3 commands::{
4 deviantart::DeviantartClient,
5 fml::FmlClient,
6 iqdb::IqdbClient,
7 nekos::NekosClient,
8 quizizz::QuizizzClient,
9 r6stats::R6StatsClient,
10 r6tracker::R6TrackerClient,
11 reddit_embed::RedditEmbedData,
12 rule34::Rule34Client,
13 sauce_nao::SauceNaoClient,
14 shift::ShiftClient,
15 tic_tac_toe::TicTacToeData,
16 tiktok_embed::TikTokData,
17 urban::UrbanClient,
18 },
19 config::Config,
20 database::Database,
21 util::EncoderTask,
22};
23use anyhow::Context;
24use serenity::gateway::ShardManager;
25use std::{
26 collections::BTreeMap,
27 fmt::Debug,
28 sync::Arc,
29};
30use tracing::error;
31
32#[derive(Debug)]
34pub struct CacheStatsBuilder {
35 stats: BTreeMap<&'static str, BTreeMap<&'static str, f32>>,
36}
37
38impl CacheStatsBuilder {
39 pub fn new() -> Self {
41 Self {
42 stats: BTreeMap::new(),
43 }
44 }
45
46 pub fn publish_stat(&mut self, section: &'static str, name: &'static str, value: f32) {
48 self.stats.entry(section).or_default().insert(name, value);
49 }
50
51 pub fn into_inner(self) -> BTreeMap<&'static str, BTreeMap<&'static str, f32>> {
53 self.stats
54 }
55}
56
57impl Default for CacheStatsBuilder {
58 fn default() -> Self {
59 Self::new()
60 }
61}
62
63pub trait CacheStatsProvider {
65 fn publish_cache_stats(&self, cache_stats_builder: &mut CacheStatsBuilder);
67}
68
69#[derive(Debug)]
71pub struct ClientData {
72 pub shard_manager: Arc<ShardManager>,
74
75 pub nekos_client: NekosClient,
77 pub r6stats_client: R6StatsClient,
79 pub r6tracker_client: R6TrackerClient,
81 pub rule34_client: Rule34Client,
83 pub quizizz_client: QuizizzClient,
85 pub fml_client: FmlClient,
87 pub shift_client: ShiftClient,
89 pub reddit_embed_data: RedditEmbedData,
91 pub enabled_check_data: EnabledCheckData,
93 pub insta_client: insta::Client,
95 pub deviantart_client: DeviantartClient,
97 pub urban_client: UrbanClient,
99 pub xkcd_client: xkcd::Client,
101 pub tic_tac_toe_data: TicTacToeData,
103 pub iqdb_client: IqdbClient,
105 pub sauce_nao_client: SauceNaoClient,
107 pub open_ai_client: open_ai::Client,
109 pub yodaspeak: yodaspeak::Client,
111 pub tiktok_data: TikTokData,
113 pub encoder_task: EncoderTask,
115
116 pub db: Database,
118
119 pub config: Arc<Config>,
121}
122
123impl ClientData {
124 pub async fn init(
126 shard_manager: Arc<ShardManager>,
127 config: Arc<Config>,
128 db: Database,
129 ) -> anyhow::Result<Self> {
130 let cache_dir = config.cache_dir();
133 let encoder_task = EncoderTask::new();
134
135 let deviantart_client = DeviantartClient::new(&db)
136 .await
137 .context("failed to init deviantart client")?;
138 let tiktok_data = TikTokData::new(&cache_dir, encoder_task.clone())
139 .await
140 .context("failed to init tiktok data")?;
141
142 Ok(ClientData {
143 shard_manager,
144
145 nekos_client: Default::default(),
146 r6stats_client: Default::default(),
147 r6tracker_client: Default::default(),
148 rule34_client: Default::default(),
149 quizizz_client: Default::default(),
150 fml_client: FmlClient::new(config.fml.key.to_string()),
151 shift_client: ShiftClient::new(),
152 reddit_embed_data: Default::default(),
153 enabled_check_data: Default::default(),
154 insta_client: insta::Client::new(),
155 deviantart_client,
156 urban_client: Default::default(),
157 xkcd_client: Default::default(),
158 tic_tac_toe_data: Default::default(),
159 iqdb_client: Default::default(),
160 sauce_nao_client: SauceNaoClient::new(config.sauce_nao.api_key.as_str()),
161 open_ai_client: open_ai::Client::new(config.open_ai.api_key.as_str()),
162 yodaspeak: yodaspeak::Client::new(),
163 tiktok_data,
164 encoder_task,
165
166 db,
167
168 config,
169 })
170 }
171
172 pub fn generate_cache_stats(&self) -> BTreeMap<&'static str, BTreeMap<&'static str, f32>> {
176 let mut stat_builder = CacheStatsBuilder::new();
177
178 let cache_stat_providers: &[&dyn CacheStatsProvider] = &[
179 &self.fml_client,
180 &self.nekos_client,
181 &self.r6stats_client,
182 &self.r6tracker_client,
183 &self.reddit_embed_data,
184 &self.rule34_client,
185 &self.shift_client,
186 &self.deviantart_client,
187 &self.urban_client,
188 &self.iqdb_client,
189 ];
190
191 for cache_stat_provider in cache_stat_providers {
192 cache_stat_provider.publish_cache_stats(&mut stat_builder);
193 }
194
195 stat_builder.into_inner()
196 }
197
198 pub async fn shutdown(&self) {
203 if let Err(e) = self.encoder_task.shutdown().await {
204 error!("{:?}", e);
205 }
206 }
207}