Skip to main content

hinge_rs/client/
ratings.rs

1use super::HingeClient;
2use crate::errors::HingeError;
3use crate::models::{
4    CreateRate, CreateRateContent, CreateRateContentPrompt, LikeResponse, PhotoAsset,
5    PhotoAssetInput, RateInput, RateRespondRequest, RateRespondResponse, SkipInput,
6};
7use crate::storage::Storage;
8use chrono::Utc;
9use serde_json::json;
10use uuid::Uuid;
11
12impl<S: Storage + Clone> HingeClient<S> {
13    pub async fn skip(&mut self, input: SkipInput) -> Result<serde_json::Value, HingeError> {
14        let url = format!("{}/rate/v2/initiate", self.settings.base_url);
15        let payload = CreateRate {
16            rating_id: Uuid::new_v4().to_string().to_uppercase(),
17            hcm_run_id: None,
18            session_id: self.session_id.clone(),
19            content: None,
20            created: Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string(),
21            rating_token: input.rating_token,
22            initiated_with: None,
23            rating: "skip".into(),
24            has_pairing: false,
25            origin: Some(input.origin.unwrap_or_else(|| "compatibles".into())),
26            subject_id: input.subject_id.clone(),
27        };
28        let body = serde_json::to_value(&payload).map_err(|e| HingeError::Serde(e.to_string()))?;
29        let res = self.http_post(&url, &body).await?;
30        let body = self.parse_response(res).await?;
31
32        self.remove_recommendation(&input.subject_id);
33        if self.auto_persist
34            && let Some(path) = self.recs_cache_path()
35        {
36            let _ = self.save_recommendations(&path);
37        }
38        Ok(body)
39    }
40
41    pub async fn rate_user(&mut self, input: RateInput) -> Result<LikeResponse, HingeError> {
42        let mut hcm_run_id: Option<String> = None;
43        if let Some(text) = input.comment.as_deref() {
44            let run_id = self.run_text_review(text, &input.subject_id).await?;
45            hcm_run_id = Some(run_id);
46        }
47        let prompt_answer = input.answer_text.clone().unwrap_or_default();
48        let prompt_question = input.question_text.clone().unwrap_or_default();
49        let prompt_content_id = input.content_id.clone();
50
51        let content = if let Some(photo) = input.photo {
52            let PhotoAssetInput {
53                url,
54                content_id,
55                cdn_id,
56                bounding_box,
57                selfie_verified,
58            } = photo;
59            Some(CreateRateContent {
60                comment: input.comment.clone(),
61                photo: Some(PhotoAsset {
62                    id: None,
63                    url,
64                    cdn_id,
65                    content_id,
66                    prompt_id: None,
67                    caption: None,
68                    width: None,
69                    height: None,
70                    video_url: None,
71                    selfie_verified,
72                    bounding_box,
73                    location: None,
74                    source: None,
75                    source_id: None,
76                    p_hash: None,
77                }),
78                prompt: None,
79            })
80        } else {
81            let prompt = CreateRateContentPrompt {
82                answer: prompt_answer,
83                content_id: prompt_content_id,
84                question: prompt_question,
85            };
86            Some(CreateRateContent {
87                comment: input.comment.clone(),
88                photo: None,
89                prompt: Some(prompt),
90            })
91        };
92        let payload = CreateRate {
93            rating_id: Uuid::new_v4().to_string().to_uppercase(),
94            hcm_run_id,
95            session_id: self.session_id.clone(),
96            content,
97            created: Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string(),
98            rating_token: input.rating_token,
99            initiated_with: Some(if input.use_superlike.unwrap_or(false) {
100                "superlike".into()
101            } else {
102                "standard".into()
103            }),
104            rating: if input.comment.is_some() {
105                "note".into()
106            } else {
107                "like".into()
108            },
109            has_pairing: false,
110            origin: Some(input.origin.unwrap_or_else(|| "compatibles".into())),
111            subject_id: input.subject_id,
112        };
113        let url = format!("{}/rate/v2/initiate", self.settings.base_url);
114        let body = serde_json::to_value(&payload).map_err(|e| HingeError::Serde(e.to_string()))?;
115        let res = self.http_post(&url, &body).await?;
116        let body = self.parse_response::<LikeResponse>(res).await?;
117        if self.auto_persist
118            && let Some(path) = self.recs_cache_path()
119        {
120            let _ = self.save_recommendations(&path);
121        }
122        Ok(body)
123    }
124
125    pub async fn respond_rate(
126        &self,
127        mut payload: RateRespondRequest,
128    ) -> Result<RateRespondResponse, HingeError> {
129        if payload.rating_id.is_none() {
130            payload.rating_id = Some(Uuid::new_v4().to_string().to_uppercase());
131        }
132        if payload.session_id.is_none() {
133            payload.session_id = Some(self.session_id.clone());
134        }
135        if payload.created.is_none() {
136            payload.created = Some(Utc::now().format("%Y-%m-%dT%H:%M:%SZ").to_string());
137        }
138
139        let url = format!("{}/rate/v2/respond", self.settings.base_url);
140        let body = serde_json::to_value(&payload).map_err(|e| HingeError::Serde(e.to_string()))?;
141        let res = self.http_post(&url, &body).await?;
142        self.parse_response(res).await
143    }
144
145    async fn run_text_review(&self, text: &str, receiver_id: &str) -> Result<String, HingeError> {
146        let url = format!("{}/flag/textreview", self.settings.base_url);
147        let res = self
148            .http
149            .post(url)
150            .headers(self.default_headers()?)
151            .json(&json!({ "text": text, "receiverId": receiver_id }))
152            .send()
153            .await?;
154        if !res.status().is_success() {
155            return Err(HingeError::Http(format!("status {}", res.status())));
156        }
157        let v = self.parse_response::<serde_json::Value>(res).await?;
158        Ok(v.get("hcmRunId")
159            .and_then(|v| v.as_str())
160            .unwrap_or("")
161            .to_string())
162    }
163}