binary_option_tools_core/pocketoption/ws/
ssid.rs

1use core::fmt;
2
3use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::{
7    general::traits::Credentials,
8    pocketoption::error::{PocketOptionError, PocketResult},
9};
10
11use super::regions::Regions;
12
13#[derive(Debug, Serialize, Deserialize, Clone)]
14pub struct SessionData {
15    session_id: String,
16    ip_address: String,
17    user_agent: String,
18    last_activity: u64,
19}
20
21#[derive(Debug, Serialize, Deserialize, Clone)]
22#[serde(rename_all = "camelCase")]
23pub struct Demo {
24    session: String,
25    is_demo: u32,
26    uid: u32,
27    platform: u32,
28}
29
30#[derive(Debug, Serialize, Clone)]
31#[serde(rename_all = "camelCase")]
32pub struct Real {
33    session: SessionData,
34    is_demo: u32,
35    uid: u32,
36    platform: u32,
37    raw: String,
38}
39
40#[derive(Debug, Serialize, Clone)]
41#[serde(untagged)]
42pub enum Ssid {
43    Demo(Demo),
44    Real(Real),
45}
46
47impl Ssid {
48    pub fn parse(data: impl ToString) -> PocketResult<Self> {
49        let data = data.to_string();
50        let parsed = data
51            .trim()
52            .strip_prefix(r#"42["auth","#)
53            .ok_or(PocketOptionError::SsidParsingError(
54                "Error parsing ssid string into object".into(),
55            ))?
56            .strip_suffix("]")
57            .ok_or(PocketOptionError::SsidParsingError(
58                "Error parsing ssid string into object".into(),
59            ))?;
60        let ssid: Demo = serde_json::from_str(parsed)
61            .map_err(|e| PocketOptionError::SsidParsingError(e.to_string()))?;
62        if ssid.is_demo == 1 {
63            Ok(Self::Demo(ssid))
64        } else {
65            let real = Real {
66                raw: data,
67                is_demo: ssid.is_demo,
68                session: php_serde::from_bytes(ssid.session.as_bytes()).map_err(|e| {
69                    PocketOptionError::SsidParsingError(format!("Error parsing session data, {e}"))
70                })?,
71                uid: ssid.uid,
72                platform: ssid.platform,
73            };
74            Ok(Self::Real(real))
75        }
76    }
77
78    pub async fn server(&self) -> PocketResult<String> {
79        match self {
80            Self::Demo(_) => Ok(Regions::DEMO.to_string()),
81            Self::Real(_) => Regions.get_server().await.map(|s| s.to_string()),
82        }
83    }
84
85    pub async fn servers(&self) -> PocketResult<Vec<String>> {
86        match self {
87            Self::Demo(_) => Ok(vec![Regions::DEMO.to_string()]),
88            Self::Real(_) => Ok(Regions
89                .get_servers()
90                .await?
91                .iter()
92                .map(|s| s.to_string())
93                .collect()),
94        }
95    }
96
97    pub fn user_agent(&self) -> String {
98        match self {
99            Self::Demo(_) => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36".into(),
100            Self::Real(real) => real.session.user_agent.clone()
101        }
102    }
103
104    pub fn demo(&self) -> bool {
105        match self {
106            Self::Demo(_) => true,
107            Self::Real(_) => false,
108        }
109    }
110}
111impl fmt::Display for Demo {
112    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
113        let ssid = serde_json::to_string(&self).map_err(|_| fmt::Error)?;
114        write!(f, r#"42["auth",{}]"#, ssid)
115    }
116}
117
118impl fmt::Display for Real {
119    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
120        write!(f, "{}", self.raw)
121    }
122}
123
124impl fmt::Display for Ssid {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        match self {
127            Self::Demo(demo) => demo.fmt(f),
128            Self::Real(real) => real.fmt(f),
129        }
130    }
131}
132
133impl<'de> Deserialize<'de> for Ssid {
134    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
135    where
136        D: serde::Deserializer<'de>,
137    {
138        let data: Value = Value::deserialize(deserializer)?;
139        Ssid::parse(data).map_err(serde::de::Error::custom)
140    }
141}
142
143impl Credentials for Ssid {}
144
145#[cfg(test)]
146mod tests {
147    use super::*;
148    use std::error::Error;
149
150    #[test]
151    fn test_descerialize_session() -> Result<(), Box<dyn Error>> {
152        let session_raw = b"a:4:{s:10:\"session_id\";s:32:\"ae3aa847add89c341ec18d8ae5bf8527\";s:10:\"ip_address\";s:15:\"191.113.157.139\";s:10:\"user_agent\";s:120:\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 OPR/114.\";s:13:\"last_activity\";i:1732926685;}31666d2dc07fdd866353937b97901e2b";
153        let session: SessionData = php_serde::from_bytes(session_raw)?;
154        dbg!(&session);
155        let session_php = php_serde::to_vec(&session)?;
156        dbg!(String::from_utf8(session_php).unwrap());
157        Ok(())
158    }
159
160    #[test]
161    fn test_parse_ssid() -> Result<(), Box<dyn Error>> {
162        let ssids = [
163            r#"42["auth",{"session":"looc69ct294h546o368s0lct7d","isDemo":1,"uid":87742848,"platform":2}]	"#,
164            r#"42["auth",{"session":"a:4:{s:10:\"session_id\";s:32:\"ae3aa847add89c341ec18d8ae5bf8527\";s:10:\"ip_address\";s:15:\"191.113.157.139\";s:10:\"user_agent\";s:120:\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36 OPR/114.\";s:13:\"last_activity\";i:1732926685;}31666d2dc07fdd866353937b97901e2b","isDemo":0,"uid":87742848,"platform":2}]	"#,
165            r#"42["auth",{"session":"vtftn12e6f5f5008moitsd6skl","isDemo":1,"uid":27658142,"platform":2}]"#,
166        ];
167        for ssid in ssids {
168            let valid = Ssid::parse(ssid)?;
169            dbg!(valid);
170        }
171        Ok(())
172    }
173}