1use std::env;
7
8#[derive(Debug, Clone)]
13pub struct BotConfig {
14 pub base_url: String,
16 pub redis_url: String,
18 pub discord_token: String,
20 pub link_code_ttl_secs: u64,
22 pub page_size: usize,
24 pub api_page_size: usize,
27 pub allow_direct_upload: bool,
29 pub max_upload_bytes: usize,
31 pub freeform_enabled: bool,
34 pub llm_enabled: bool,
39 pub ollama_url: String,
42 pub intent_model: String,
45 pub intent_timeout_secs: u64,
47 pub intent_cache_ttl: u64,
49 pub lemmy_enabled: bool,
51 pub lemmy_url: String,
53 pub lemmy_username: String,
55 pub lemmy_password: String,
57 pub lemmy_community: String,
59 pub lemmy_poll_secs: u64,
61}
62
63impl Default for BotConfig {
64 fn default() -> Self {
65 Self {
66 base_url: env::var("FANFIC_ARCHIVIST_BASE_URL")
67 .unwrap_or_else(|_| "http://localhost:8000".to_string()),
68 redis_url: env::var("FANFIC_ARCHIVIST_REDIS_URL")
69 .unwrap_or_else(|_| "redis://localhost:6379".to_string()),
70 discord_token: env::var("DISCORD_TOKEN").unwrap_or_default(),
71 link_code_ttl_secs: env::var("FANFIC_ARCHIVIST_LINK_TTL")
72 .ok()
73 .and_then(|v| v.parse().ok())
74 .unwrap_or(600),
75 page_size: env::var("FANFIC_ARCHIVIST_PAGE_SIZE")
76 .ok()
77 .and_then(|v| v.parse().ok())
78 .unwrap_or(5),
79 api_page_size: env::var("FANFIC_ARCHIVIST_API_PAGE_SIZE")
80 .ok()
81 .and_then(|v| v.parse().ok())
82 .unwrap_or(20),
83 allow_direct_upload: env::var("FANFIC_ARCHIVIST_ALLOW_UPLOAD")
84 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
85 .unwrap_or(true),
86 max_upload_bytes: env::var("FANFIC_ARCHIVIST_MAX_UPLOAD_BYTES")
87 .ok()
88 .and_then(|v| v.parse().ok())
89 .unwrap_or(25 * 1024 * 1024),
90 freeform_enabled: env::var("FANFIC_ARCHIVIST_FREEFORM")
91 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
92 .unwrap_or(true),
93 llm_enabled: env::var("FANFIC_ARCHIVIST_LLM_ENABLED")
94 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
95 .unwrap_or(false),
96 ollama_url: env::var("FANFIC_ARCHIVIST_OLLAMA_URL")
97 .unwrap_or_else(|_| "http://localhost:11434".to_string()),
98 intent_model: env::var("FANFIC_ARCHIVIST_INTENT_MODEL")
99 .unwrap_or_else(|_| "lfm2.5:8b".to_string()),
100 intent_timeout_secs: env::var("FANFIC_ARCHIVIST_INTENT_TIMEOUT_SECS")
101 .ok()
102 .and_then(|v| v.parse().ok())
103 .unwrap_or(15),
104 intent_cache_ttl: env::var("FANFIC_ARCHIVIST_INTENT_CACHE_TTL")
105 .ok()
106 .and_then(|v| v.parse().ok())
107 .unwrap_or(3600),
108 lemmy_enabled: env::var("FANFIC_ARCHIVIST_LEMMY_ENABLED")
109 .map(|v| v == "1" || v.eq_ignore_ascii_case("true"))
110 .unwrap_or(false),
111 lemmy_url: env::var("FANFIC_ARCHIVIST_LEMMY_URL")
112 .unwrap_or_else(|_| "https://reddthat.com".to_string()),
113 lemmy_username: env::var("FANFIC_ARCHIVIST_LEMMY_USERNAME")
114 .unwrap_or_default(),
115 lemmy_password: env::var("FANFIC_ARCHIVIST_LEMMY_PASSWORD")
116 .unwrap_or_default(),
117 lemmy_community: env::var("FANFIC_ARCHIVIST_LEMMY_COMMUNITY")
118 .unwrap_or_else(|_| "fanfiction".to_string()),
119 lemmy_poll_secs: env::var("FANFIC_ARCHIVIST_LEMMY_POLL_SECS")
120 .ok()
121 .and_then(|v| v.parse().ok())
122 .unwrap_or(120),
123 }
124 }
125}
126
127impl BotConfig {
128 pub fn url(&self, path: &str) -> String {
130 let path = path.trim_start_matches('/');
131 format!("{}/{}", self.base_url.trim_end_matches('/'), path)
132 }
133}
134
135#[cfg(test)]
136mod tests {
137 use super::*;
138
139 #[test]
140 fn url_joins_base_and_path() {
141 let cfg = BotConfig {
142 base_url: "http://localhost:8000".into(),
143 ..Default::default()
144 };
145 assert_eq!(cfg.url("/api/epub"), "http://localhost:8000/api/epub");
146 assert_eq!(cfg.url("api/meta"), "http://localhost:8000/api/meta");
147 }
148
149 #[test]
150 fn url_handles_trailing_slash() {
151 let cfg = BotConfig {
152 base_url: "http://localhost:8000/".into(),
153 ..Default::default()
154 };
155 assert_eq!(cfg.url("api/search"), "http://localhost:8000/api/search");
156 }
157}