atproto_api/
agent.rs

1pub mod atp_agent {
2    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
3    pub struct Session {
4        #[serde(rename = "accessJwt")]
5        access_jwt: String,
6        did: String,
7        //email: String,
8        handle: String,
9        #[serde(rename = "refreshJwt")]
10        refresh_jwt: String,
11    }
12
13    #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
14    struct RefreshSession {
15        #[serde(rename = "accessJwt")]
16        access_jwt: String,
17        #[serde(rename = "refreshJwt")]
18        refresh_jwt: String,
19        did: String,
20        handle: String,
21    }
22
23    #[derive(serde::Serialize)]
24    struct Auth {
25        identifier: String,
26        password: String,
27    }
28
29    #[derive(Debug, Clone)]
30    pub struct AtpAgent {
31        pub service: String,
32        pub session: Option<Session>,
33    }
34
35    #[async_trait::async_trait]
36    pub trait Agent: Sized {
37        //fn default() -> AtpAgent;
38        fn new(url: String) -> AtpAgent;
39        async fn login(
40            mut self,
41            identifier: String,
42            password: String,
43        ) -> Result<AtpAgent, Box<dyn std::error::Error>>;
44        async fn refresh_session(mut self) -> Result<(), Box<dyn std::error::Error>>;
45        async fn get(
46            mut self,
47            lexicon: String,
48            parameters: serde_json::Value,
49        ) -> Result<serde_json::Value, Box<dyn std::error::Error>>;
50    }
51
52    #[async_trait::async_trait]
53    impl Agent for AtpAgent {
54        // WARNING: default() is deprecated. You can
55        // read more about this decision here:
56        // https://todo.sr.ht/~jordanreger/atproto_api/7
57
58        // AtpAgent::default()
59        // fn default() -> AtpAgent {
60        //     AtpAgent {
61        //         service: "https://bsky.social/".to_string(),
62        //         session: None,
63        //     }
64        // }
65
66        // AtpAgent::new("https://fjall.net".to_string())
67        fn new(url: String) -> AtpAgent {
68            AtpAgent {
69                service: crate::tools::validate_url(&url),
70                session: None,
71            }
72        }
73
74        // com.atproto.server.createSession
75        async fn login(
76            mut self,
77            identifier: String,
78            password: String,
79        ) -> Result<AtpAgent, Box<dyn std::error::Error>> {
80            let service = &self.service;
81
82            let client = reqwest::Client::builder().build()?;
83
84            let auth = Auth {
85                identifier: identifier.into(),
86                password: password.into(),
87            };
88
89            let session = client
90                .post(format!("{service}xrpc/com.atproto.server.createSession"))
91                .header("User-Agent", "atproto_api/0.1.0")
92                .json(&auth)
93                .send()
94                .await?
95                .json::<serde_json::Value>()
96                .await?;
97
98            let res = match &session.get("error") {
99                Some(x) if !x.is_null() => {
100                    let error = &session.get("error").unwrap().to_string().replace("\"", "");
101                    let message = &session
102                        .get("message")
103                        .unwrap()
104                        .to_string()
105                        .replace("\"", "");
106                    panic!(
107                        "{}",
108                        format!("{service} returns error {error}: {message}").to_string()
109                    )
110                }
111                _ => session,
112            };
113
114            let session: Session = serde_json::from_value(res).unwrap();
115
116            self.session = Some(session);
117            Ok(self)
118        }
119
120        // com.atproto.server.refreshSession
121        async fn refresh_session(mut self) -> Result<(), Box<dyn std::error::Error>> {
122            let session = self.session.unwrap();
123            let refresh = RefreshSession {
124                access_jwt: session.access_jwt,
125                refresh_jwt: session.refresh_jwt,
126                did: session.did,
127                handle: session.handle,
128            };
129            let jwt = &refresh.refresh_jwt;
130            let service = &self.service;
131            let client = reqwest::Client::builder().build()?;
132            let session = client
133                .post(format!("{service}xrpc/com.atproto.server.refreshSession"))
134                .header("User-Agent", "atproto_api/0.1.0")
135                .header("Authorization", format!("Bearer {jwt}"))
136                .query(&refresh)
137                .send()
138                .await?
139                .json::<serde_json::Value>()
140                .await?;
141
142            let res = match &session.get("error") {
143                Some(x) if !x.is_null() => {
144                    let error = &session.get("error").unwrap().to_string().replace("\"", "");
145                    let message = &session
146                        .get("message")
147                        .unwrap()
148                        .to_string()
149                        .replace("\"", "");
150                    panic!("{}", format!("{service} returns error {error}: {message}"))
151                }
152                _ => session,
153            };
154
155            let refreshed_session: Session = serde_json::from_value(res).unwrap();
156
157            self.session = Some(refreshed_session);
158            Ok(())
159        }
160
161        async fn get(
162            mut self,
163            lexicon: String,
164            parameters: serde_json::Value,
165        ) -> Result<serde_json::Value, Box<dyn std::error::Error>> {
166            let service = &self.service;
167            let session = &self.session.as_mut().unwrap();
168            let jwt = &session.access_jwt;
169            let client = reqwest::Client::builder().build()?;
170            let res = client
171                .get(format!("{service}xrpc/{lexicon}"))
172                .header("User-Agent", "atproto_api/0.1.0")
173                .header("Authorization", format!("Bearer {jwt}"))
174                .query(&parameters)
175                .send()
176                .await?
177                .json::<serde_json::Value>()
178                .await?;
179
180            let res = match &res.get("error") {
181                Some(x) if !x.is_null() => {
182                    let error = &res.get("error").unwrap().to_string().replace("\"", "");
183                    let message = &res.get("message").unwrap().to_string().replace("\"", "");
184                    panic!("{}", format!("{service} returns error {error}: {message}"))
185                }
186                _ => res,
187            };
188
189            let _refresh_session = self.refresh_session();
190
191            Ok(res)
192        }
193    }
194}