1use crate::errors::PdsError;
2use crate::CreateAccountRequest;
3use bsky_sdk::api::agent::atp_agent::AtpSession;
4use bsky_sdk::api::agent::Configure;
5use bsky_sdk::api::app::bsky::actor::defs::Preferences;
6use bsky_sdk::api::com::atproto::identity::sign_plc_operation::InputData;
7use bsky_sdk::api::com::atproto::repo::list_missing_blobs::RecordBlob;
8use bsky_sdk::api::types::string::{Cid, Did, Handle, Nsid};
9use bsky_sdk::api::types::Unknown;
10use bsky_sdk::BskyAgent;
11use ipld_core::ipld::Ipld;
12
13pub type GetAgentResult = Result<BskyAgent, Box<dyn std::error::Error>>;
14pub type RecommendedDidOutputData =
15 bsky_sdk::api::com::atproto::identity::get_recommended_did_credentials::OutputData;
16
17#[tracing::instrument(skip(agent, token))]
18pub async fn login_helper(
19 agent: &BskyAgent,
20 pds_host: &str,
21 did: &str,
22 token: &str,
23) -> Result<AtpSession, PdsError> {
24 use bsky_sdk::api::com::atproto::server::create_session::OutputData;
25 agent.configure_endpoint(pds_host.to_string());
26 match agent
27 .resume_session(AtpSession {
28 data: OutputData {
29 access_jwt: token.to_string(),
30 active: Some(true),
31 did: Did::new(did.to_string()).unwrap(),
32 did_doc: None,
33 email: None,
34 email_auth_factor: None,
35 email_confirmed: None,
36 handle: Handle::new("anothermigration.bsky.social".to_string()).unwrap(),
37 refresh_jwt: "".to_string(),
38 status: None,
39 },
40 extra_data: Ipld::Null,
41 })
42 .await
43 {
44 Ok(_) => {
45 tracing::info!("Successfully logged in");
46 Ok(agent.get_session().await.unwrap())
47 }
48 Err(e) => {
49 tracing::error!("Error logging in: {:?}", e);
50 Err(PdsError::Login)
51 }
52 }
53}
54
55#[tracing::instrument(skip(agent))]
56pub async fn describe_server(
57 agent: &BskyAgent,
58) -> Result<bsky_sdk::api::com::atproto::server::describe_server::OutputData, String> {
59 let result = agent.api.com.atproto.server.describe_server().await;
60 match result {
61 Ok(output) => {
62 tracing::info!("{:?}", output);
63 Ok(output.data)
64 }
65 Err(e) => {
66 tracing::error!("{:?}", e);
67 Err(String::from("Error"))
68 }
69 }
70}
71
72#[tracing::instrument(skip(agent))]
73pub async fn missing_blobs(agent: &BskyAgent) -> Result<Vec<RecordBlob>, PdsError> {
74 use bsky_sdk::api::com::atproto::repo::list_missing_blobs::{Parameters, ParametersData};
75 let result = agent
76 .api
77 .com
78 .atproto
79 .repo
80 .list_missing_blobs(Parameters {
81 data: ParametersData {
82 cursor: None,
83 limit: None,
84 },
85 extra_data: Ipld::Null,
86 })
87 .await;
88 match result {
89 Ok(output) => {
90 tracing::info!("{:?}", output);
91 Ok(output.blobs.clone())
92 }
93 Err(e) => {
94 tracing::error!("{:?}", e);
95 Err(PdsError::Validation)
96 }
97 }
98}
99
100#[tracing::instrument(skip(agent))]
101pub async fn get_blob(agent: &BskyAgent, cid: Cid, did: Did) -> Result<Vec<u8>, ()> {
102 use bsky_sdk::api::com::atproto::sync::get_blob::{Parameters, ParametersData};
103 let result = agent
104 .api
105 .com
106 .atproto
107 .sync
108 .get_blob(Parameters {
109 data: ParametersData {
110 cid,
111 did: did.parse().unwrap(),
112 },
113 extra_data: Ipld::Null,
114 })
115 .await;
116 match result {
117 Ok(output) => {
118 tracing::debug!("Successfully fetched blob: {:?}", output);
119 Ok(output.clone())
120 }
121 Err(e) => {
122 tracing::error!("Failed to fetch blob: {:?}", e);
123 Err(())
124 }
125 }
126}
127
128#[tracing::instrument(skip(agent, input))]
129pub async fn upload_blob(agent: &BskyAgent, input: Vec<u8>) -> Result<(), PdsError> {
130 let result = agent.api.com.atproto.repo.upload_blob(input).await;
131 match result {
132 Ok(output) => {
133 tracing::info!("Successfully uploaded blob");
134 tracing::debug!("{:?}", output);
135 Ok(())
136 }
137 Err(e) => {
138 tracing::error!("Failed to upload blob: {:?}", e);
139 Err(PdsError::Validation)
140 }
141 }
142}
143
144#[tracing::instrument(skip(agent))]
145pub async fn export_preferences(agent: &BskyAgent) -> Result<Preferences, PdsError> {
146 use bsky_sdk::api::app::bsky::actor::get_preferences::{Parameters, ParametersData};
147 let result = agent
148 .api
149 .app
150 .bsky
151 .actor
152 .get_preferences(Parameters {
153 data: ParametersData {},
154 extra_data: Ipld::Null,
155 })
156 .await;
157 match result {
158 Ok(output) => {
159 tracing::info!("Successfully exported preferences");
160 tracing::debug!("{:?}", output);
161 Ok(output.preferences.clone())
162 }
163 Err(e) => {
164 tracing::error!("Failed to export preferences: {:?}", e);
165 Err(PdsError::Validation)
166 }
167 }
168}
169
170#[tracing::instrument(skip(agent))]
171pub async fn import_preferences(
172 agent: &BskyAgent,
173 preferences: Preferences,
174) -> Result<(), PdsError> {
175 use bsky_sdk::api::app::bsky::actor::put_preferences::{Input, InputData};
176 let result = agent
177 .api
178 .app
179 .bsky
180 .actor
181 .put_preferences(Input {
182 data: InputData { preferences },
183 extra_data: Ipld::Null,
184 })
185 .await;
186 match result {
187 Ok(output) => {
188 tracing::info!("Successfully imported preferences");
189 tracing::debug!("{:?}", output);
190 Ok(())
191 }
192 Err(e) => {
193 tracing::error!("Failed to import preferences: {:?}", e);
194 Err(PdsError::Validation)
195 }
196 }
197}
198
199#[tracing::instrument(skip(agent))]
200pub async fn recommended_plc(agent: &BskyAgent) -> Result<RecommendedDidOutputData, PdsError> {
201 let result = agent
202 .api
203 .com
204 .atproto
205 .identity
206 .get_recommended_did_credentials()
207 .await;
208 match result {
209 Ok(output) => {
210 tracing::info!("Successfully imported preferences");
211 tracing::debug!("{:?}", output);
212 Ok(output.data)
213 }
214 Err(e) => {
215 tracing::error!("Failed to import preferences: {:?}", e);
216 Err(PdsError::Validation)
217 }
218 }
219}
220
221#[tracing::instrument(skip(agent))]
222pub async fn get_service_auth(agent: &BskyAgent, aud: &str) -> Result<String, PdsError> {
223 use bsky_sdk::api::com::atproto::server::get_service_auth::{Parameters, ParametersData};
224 let result = agent
225 .api
226 .com
227 .atproto
228 .server
229 .get_service_auth(Parameters {
230 data: ParametersData {
231 aud: aud.parse().unwrap(),
232 exp: None,
233 lxm: Some(Nsid::new("com.atproto.server.createAccount".to_string()).unwrap()),
234 },
235 extra_data: Ipld::Null,
236 })
237 .await;
238 match result {
239 Ok(output) => {
240 tracing::info!("Successfully requested service auth");
241 tracing::debug!("{:?}", output);
242 Ok(output.token.clone())
243 }
244 Err(e) => {
245 tracing::error!("Failed to request service auth: {:?}", e);
246 Err(PdsError::Runtime)
247 }
248 }
249}
250
251#[tracing::instrument(skip(agent))]
252pub async fn sign_plc(agent: &BskyAgent, plc_input_data: InputData) -> Result<Unknown, PdsError> {
253 use bsky_sdk::api::com::atproto::identity::sign_plc_operation::Input;
254 let result = agent
255 .api
256 .com
257 .atproto
258 .identity
259 .sign_plc_operation(Input {
260 data: plc_input_data,
261 extra_data: Ipld::Null,
262 })
263 .await;
264 match result {
265 Ok(output) => {
266 tracing::info!("Successfully signed token");
267 tracing::debug!("{:?}", output);
268 Ok(output.operation.clone())
269 }
270 Err(e) => {
271 tracing::error!("Failed to sign token: {:?}", e);
272 Err(PdsError::Validation)
273 }
274 }
275}
276
277#[tracing::instrument(skip(agent))]
278pub async fn account_import(agent: &BskyAgent, filepath: &str) -> Result<(), PdsError> {
279 let result = agent
280 .api
281 .com
282 .atproto
283 .repo
284 .import_repo(tokio::fs::read(filepath).await.unwrap())
285 .await;
286 match result {
287 Ok(_) => {
288 tracing::info!("Successfully signed token");
289 Ok(())
290 }
291 Err(e) => {
292 eprintln!("Error importing: {:?}", e);
293 Err(PdsError::AccountImport)
294 }
295 }
296}
297
298#[tracing::instrument(skip(agent))]
299pub async fn account_export(agent: &BskyAgent, did: &Did) -> Result<(), PdsError> {
300 use bsky_sdk::api::com::atproto::sync::get_repo::{Parameters, ParametersData};
301 let result = agent
302 .api
303 .com
304 .atproto
305 .sync
306 .get_repo(Parameters {
307 data: ParametersData {
308 did: did.clone(),
309 since: None,
310 },
311 extra_data: Ipld::Null,
312 })
313 .await;
314 match result {
315 Ok(output) => {
316 tokio::fs::write(did.as_str().to_string() + ".car", output)
317 .await
318 .unwrap();
319 tracing::info!("write success");
320 Ok(())
321 }
322 Err(e) => {
323 tracing::error!("Error exporting: {:?}", e);
324 Err(PdsError::AccountExport)
325 }
326 }
327}
328
329#[tracing::instrument(skip(agent))]
330pub async fn deactivate_account(agent: &BskyAgent) -> Result<(), PdsError> {
331 use bsky_sdk::api::com::atproto::server::deactivate_account::{Input, InputData};
332 let result = agent
333 .api
334 .com
335 .atproto
336 .server
337 .deactivate_account(Input {
338 data: InputData { delete_after: None },
339 extra_data: Ipld::Null,
340 })
341 .await;
342 match result {
343 Ok(output) => {
344 tracing::info!("Successfully deactivated account");
345 tracing::debug!("{:?}", output);
346 Ok(())
347 }
348 Err(e) => {
349 tracing::error!("Failed to deactivate account: {:?}", e);
350 Err(PdsError::Validation)
351 }
352 }
353}
354
355#[tracing::instrument(skip(agent))]
356pub async fn activate_account(agent: &BskyAgent) -> Result<(), PdsError> {
357 let result = agent.api.com.atproto.server.activate_account().await;
358 match result {
359 Ok(output) => {
360 tracing::info!("Successfully activated account");
361 tracing::debug!("{:?}", output);
362 Ok(())
363 }
364 Err(e) => {
365 tracing::error!("Failed to activate account: {:?}", e);
366 Err(PdsError::Validation)
367 }
368 }
369}
370
371#[tracing::instrument(skip(agent))]
372pub async fn submit_plc(agent: &BskyAgent, signed_plc: Unknown) -> Result<(), PdsError> {
373 use bsky_sdk::api::com::atproto::identity::submit_plc_operation::{Input, InputData};
374 let result = agent
375 .api
376 .com
377 .atproto
378 .identity
379 .submit_plc_operation(Input {
380 data: InputData {
381 operation: signed_plc,
382 },
383 extra_data: Ipld::Null,
384 })
385 .await;
386 match result {
387 Ok(output) => {
388 tracing::info!("Successfully submitted PLC Operation");
389 tracing::debug!("{:?}", output);
390 Ok(())
391 }
392 Err(e) => {
393 tracing::error!("Failed to submitted PLC Operation: {:?}", e);
394 Err(PdsError::Validation)
395 }
396 }
397}
398
399#[tracing::instrument(skip(agent))]
400async fn account_status(agent: &BskyAgent) -> Result<(), PdsError> {
401 match agent.api.com.atproto.server.check_account_status().await {
402 Ok(output) => {
403 tracing::info!("Successfully Got Account Status");
404 tracing::debug!("{:?}", output);
405 Ok(())
406 }
407 Err(e) => {
408 tracing::error!("{:?}", e);
409 Err(PdsError::AccountStatus)
410 }
411 }
412}
413
414#[tracing::instrument(skip(agent))]
415pub async fn request_token(agent: &BskyAgent) -> Result<(), PdsError> {
416 let result = agent
417 .api
418 .com
419 .atproto
420 .identity
421 .request_plc_operation_signature()
422 .await;
423 match result {
424 Ok(_) => Ok(()),
425 Err(e) => {
426 tracing::error!("{:?}", e);
427 Err(PdsError::Validation)
428 }
429 }
430}
431
432#[tracing::instrument(skip(account_request))]
433pub async fn create_account(
434 pds_host: &str,
435 account_request: &CreateAccountRequest,
436) -> Result<(), PdsError> {
437 use bsky_sdk::api::com::atproto::server::create_account::{Input, InputData};
438 let client = reqwest::Client::new();
439 let x = serde_json::to_string(&Input {
440 data: InputData {
441 did: Some(account_request.did.clone()),
442 email: account_request.email.clone(),
443 handle: account_request.handle.parse().unwrap(),
444 invite_code: account_request.invite_code.clone(),
445 password: account_request.password.clone(),
446 plc_op: None,
447 recovery_key: account_request.recovery_key.clone(),
448 verification_code: account_request.verification_code.clone(),
449 verification_phone: account_request.verification_phone.clone(),
450 },
451 extra_data: Ipld::Null,
452 })
453 .unwrap();
454 let result = client
455 .post(pds_host.to_string() + "/xrpc/com.atproto.server.createAccount")
456 .body(x)
457 .header("Content-Type", "application/json")
458 .bearer_auth(account_request.token.clone())
459 .send()
460 .await;
461 match result {
462 Ok(output) => match output.status() {
463 reqwest::StatusCode::OK => {
464 tracing::info!("Successfully created account");
465 }
466 _ => {
467 tracing::error!("Error creating account: {:?}", output);
468 tracing::error!("More: {:?}", output.text().await);
469 return Err(PdsError::Validation);
470 }
471 },
472 Err(e) => {
473 tracing::error!("Error creating account: {:?}", e);
474 return Err(PdsError::Validation);
475 }
476 }
477 Ok(())
478}