1use super::HingeClient;
2use crate::errors::HingeError;
3use crate::models::{
4 AnswerEvaluateRequest, CreatePromptPollRequest, CreatePromptPollResponse,
5 CreateVideoPromptRequest, CreateVideoPromptResponse, Prompt, PromptsResponse,
6};
7use crate::prompts_manager::HingePromptsManager;
8use crate::storage::Storage;
9use serde_json::json;
10use std::path::Path;
11
12impl<S: Storage + Clone> HingeClient<S> {
13 pub async fn fetch_prompts(&mut self) -> Result<PromptsResponse, HingeError> {
14 if self.auto_persist
15 && let Some(path) = self.prompts_cache_path()
16 && Path::new(&path).exists()
17 && let Ok(text) = std::fs::read_to_string(&path)
18 && let Ok(val) = serde_json::from_str::<PromptsResponse>(&text)
19 {
20 return Ok(val);
21 }
22
23 let url = format!("{}/prompts", self.settings.base_url);
24 let payload = self.prompt_payload().await;
25 let res = self.http_post(&url, &payload).await?;
26 let body = self.parse_response::<PromptsResponse>(res).await?;
27 if self.auto_persist
28 && let Some(path) = self.prompts_cache_path()
29 {
30 let _ = std::fs::write(
31 &path,
32 serde_json::to_string_pretty(&body).unwrap_or_else(|_| "{}".into()),
33 );
34 }
35 Ok(body)
36 }
37
38 pub async fn fetch_prompts_manager(&mut self) -> Result<HingePromptsManager, HingeError> {
39 let resp = self.fetch_prompts().await?;
40 Ok(HingePromptsManager::new(resp))
41 }
42
43 pub async fn get_prompt_text(&mut self, prompt_id: &str) -> Result<String, HingeError> {
44 let mgr = self.fetch_prompts_manager().await?;
45 Ok(mgr.get_prompt_display_text(prompt_id))
46 }
47
48 pub async fn search_prompts(&mut self, query: &str) -> Result<Vec<Prompt>, HingeError> {
49 let mgr = self.fetch_prompts_manager().await?;
50 let items = mgr.search_prompts(query);
51 Ok(items.into_iter().cloned().collect())
52 }
53
54 pub async fn get_prompts_by_category(
55 &mut self,
56 category_slug: &str,
57 ) -> Result<Vec<Prompt>, HingeError> {
58 let mgr = self.fetch_prompts_manager().await?;
59 let items = mgr.get_prompts_by_category(category_slug);
60 Ok(items.into_iter().cloned().collect())
61 }
62
63 pub async fn prompt_payload(&mut self) -> serde_json::Value {
64 if !self.is_session_valid().await.unwrap_or(false) {
65 return json!({});
66 }
67 let preferences = match self.get_self_preferences().await {
68 Ok(v) => v,
69 Err(_) => return json!({}),
70 };
71 let profile = match self.get_self_profile().await {
72 Ok(v) => v,
73 Err(_) => return json!({}),
74 };
75 let mut preferences_dict = serde_json::to_value(&preferences).unwrap_or(json!({}));
76 let profile_dict = serde_json::to_value(&profile).unwrap_or(json!({}));
77
78 let selected: Vec<String> = preferences_dict
79 .get("preferences")
80 .and_then(|p| p.get("genderPreferences"))
81 .and_then(|v| v.as_array())
82 .map(|arr| {
83 arr.iter()
84 .filter_map(|x| x.as_u64().map(|n| n.to_string()))
85 .collect()
86 })
87 .unwrap_or_default();
88
89 let keep_selected = |mut d: serde_json::Value| {
90 if let serde_json::Value::Object(map) = &mut d
91 && !selected.is_empty()
92 {
93 map.retain(|k, _| selected.contains(k));
94 }
95 d
96 };
97
98 if let Some(obj) = preferences_dict
99 .get_mut("preferences")
100 .and_then(|p| p.get_mut("genderedHeightRanges"))
101 {
102 *obj = keep_selected(obj.clone());
103 }
104 if let Some(obj) = preferences_dict
105 .get_mut("preferences")
106 .and_then(|p| p.get_mut("genderedAgeRanges"))
107 {
108 *obj = keep_selected(obj.clone());
109 }
110
111 if let Some(db) = preferences_dict
112 .get_mut("preferences")
113 .and_then(|p| p.get_mut("dealbreakers"))
114 {
115 if let Some(obj) = db.get_mut("genderedHeight") {
116 *obj = keep_selected(obj.clone());
117 }
118 if let Some(obj) = db.get_mut("genderedAge") {
119 *obj = keep_selected(obj.clone());
120 }
121 }
122
123 let p = profile_dict
124 .get("content")
125 .map(unwrap_visible)
126 .unwrap_or(json!({}));
127 let loc_name = profile_dict
128 .get("content")
129 .and_then(|c| c.get("location"))
130 .and_then(|l| l.get("name"))
131 .cloned()
132 .unwrap_or(json!(null));
133
134 let profile_payload = json!({
135 "works": match p.get("works") { Some(v) if v.is_string() => json!([v]), _ => field_or(&p, "works", json!([])) },
136 "sexualOrientations": field_or(&p, "sexualOrientations", json!([])),
137 "didJustJoin": false,
138 "smoking": field_or(&p, "smoking", json!(null)),
139 "selfieVerified": field_or(&p, "selfieVerified", json!(false)),
140 "politics": field_or(&p, "politics", json!(null)),
141 "relationshipTypesText": field_or(&p, "relationshipTypesText", json!("")),
142 "datingIntention": field_or(&p, "datingIntention", json!(null)),
143 "height": field_or(&p, "height", json!(null)),
144 "children": field_or(&p, "children", json!(null)),
145 "religions": field_or(&p, "religions", json!([])),
146 "relationshipTypes": field_or(&p, "relationshipTypeIds", json!([])),
147 "educations": field_or(&p, "educations", json!([])),
148 "age": field_or(&p, "age", json!(null)),
149 "jobTitle": field_or(&p, "jobTitle", json!(null)),
150 "birthday": field_or(&p, "birthday", json!(null)),
151 "drugs": field_or(&p, "drugs", json!(null)),
152 "content": json!({}),
153 "hometown": field_or(&p, "hometown", json!(null)),
154 "firstName": field_or(&p, "firstName", json!(null)),
155 "familyPlans": field_or(&p, "familyPlans", json!(null)),
156 "location": json!({"name": loc_name}),
157 "marijuana": field_or(&p, "marijuana", json!(null)),
158 "pets": field_or(&p, "pets", json!([])),
159 "datingIntentionText": field_or(&p, "datingIntentionText", json!("")),
160 "educationAttained": field_or(&p, "educationAttained", json!(null)),
161 "ethnicities": field_or(&p, "ethnicities", json!([])),
162 "pronouns": field_or(&p, "pronouns", json!([])),
163 "languagesSpoken": field_or(&p, "languagesSpoken", json!([])),
164 "lastName": field_or(&p, "lastName", json!("")),
165 "ethnicitiesText": field_or(&p, "ethnicitiesText", json!("")),
166 "drinking": field_or(&p, "drinking", json!(null)),
167 "userId": field_or(&profile_dict, "userId", json!(null)),
168 "genderIdentityId": field_or(&p, "genderIdentityId", json!(null)),
169 });
170
171 json!({
172 "preferences": preferences_dict.get("preferences").cloned().unwrap_or(json!({})),
173 "profile": profile_payload
174 })
175 }
176
177 pub async fn evaluate_answer(
178 &self,
179 payload: AnswerEvaluateRequest,
180 ) -> Result<serde_json::Value, HingeError> {
181 let url = format!("{}/content/v1/answer/evaluate", self.settings.base_url);
182 let body = serde_json::to_value(&payload).map_err(|e| HingeError::Serde(e.to_string()))?;
183 let res = self.http_post(&url, &body).await?;
184 self.parse_response(res).await
185 }
186
187 pub async fn create_prompt_poll(
188 &self,
189 payload: CreatePromptPollRequest,
190 ) -> Result<CreatePromptPollResponse, HingeError> {
191 let url = format!("{}/content/v1/prompt_poll", self.settings.base_url);
192 let body = serde_json::to_value(&payload).map_err(|e| HingeError::Serde(e.to_string()))?;
193 let res = self.http_post(&url, &body).await?;
194 self.parse_response(res).await
195 }
196
197 pub async fn create_video_prompt(
198 &self,
199 payload: CreateVideoPromptRequest,
200 ) -> Result<CreateVideoPromptResponse, HingeError> {
201 let url = format!("{}/content/v1/video_prompt", self.settings.base_url);
202 let body = serde_json::to_value(&payload).map_err(|e| HingeError::Serde(e.to_string()))?;
203 let res = self.http_post(&url, &body).await?;
204 self.parse_response(res).await
205 }
206}
207
208fn field_or(value: &serde_json::Value, key: &str, default: serde_json::Value) -> serde_json::Value {
209 value.get(key).cloned().unwrap_or(default)
210}
211
212fn unwrap_visible(obj: &serde_json::Value) -> serde_json::Value {
213 match obj {
214 serde_json::Value::Object(m) => {
215 if m.contains_key("value") && m.contains_key("visible") {
216 unwrap_visible(&m["value"])
217 } else {
218 let mut out = serde_json::Map::new();
219 for (k, v) in m.iter() {
220 out.insert(k.clone(), unwrap_visible(v));
221 }
222 serde_json::Value::Object(out)
223 }
224 }
225 serde_json::Value::Array(arr) => {
226 serde_json::Value::Array(arr.iter().map(unwrap_visible).collect())
227 }
228 _ => obj.clone(),
229 }
230}