1use serde_json::{json, Value};
2use std::fs;
3use std::path::Path;
4use rand::prelude::IndexedRandom;
5
6pub fn load_avatars() -> Value {
7 let path = Path::new(".fur/avatars.json");
8 if path.exists() {
9 let content = fs::read_to_string(path).unwrap_or_default();
10 serde_json::from_str(&content).unwrap_or_else(|_| json!({}))
11 } else {
12 json!({})
13 }
14}
15
16pub fn save_avatars(avatars: &Value) {
17 let path = Path::new(".fur/avatars.json");
18 if let Ok(serialized) = serde_json::to_string_pretty(avatars) {
19 let _ = fs::write(path, serialized);
20 }
21}
22
23pub fn resolve_avatar(avatars: &Value, key: &str) -> (String, String) {
24 if let Some(emoji) = avatars.get(key).and_then(|v| v.as_str()) {
26 return (key.to_string(), emoji.to_string());
27 }
28
29 if let Some((name, _)) = avatars.as_object()
31 .and_then(|map| map.iter().find(|(_, v)| v.as_str() == Some(key)))
32 {
33 return (name.clone(), key.to_string());
34 }
35
36 (key.to_string(), "🐾".to_string()) }
38
39
40fn is_bot_name(name: &str) -> bool {
42 let n = name.to_lowercase();
43
44 const MARKERS: [&str; 8] = [
46 "gpt", "claude", "gemini", "bard", "grok", "bot", "ai", "llm",
47 ];
48
49 if MARKERS.iter().any(|m| n.contains(m)) {
51 return true;
52 }
53
54 if n.split(|c: char| !c.is_alphanumeric())
56 .any(|tok| tok == "agent")
57 {
58 return true;
59 }
60
61 false
62}
63
64pub fn get_random_emoji_for_name(name: &str) -> String {
68 if is_bot_name(name) {
69 return "🤖".to_string();
70 }
71 let pool = ["👤"];
72 pool.choose(&mut rand::rng()).unwrap_or(&"👔").to_string()
73}
74