tangled.org trending bluesky account
1mod resolver;
2use crate::resolver::ApiDNSTxtResolver;
3use atrium_api::agent::Configure;
4use atrium_api::types::string::{Did, Handle};
5use atrium_common::resolver::Resolver;
6use atrium_identity::did::{CommonDidResolver, CommonDidResolverConfig, DEFAULT_PLC_DIRECTORY_URL};
7use atrium_identity::handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig};
8use atrium_oauth::DefaultHttpClient;
9use bsky_sdk::BskyAgent;
10use std::sync::Arc;
11use thiserror::Error;
12
13#[derive(Debug, Error)]
14pub enum Error {
15 #[error("General error: {0}")]
16 GeneralError(String),
17 #[error("Login error: {0}")]
18 LoginError(String),
19 #[error("Write error: {0}")]
20 WriteError(String),
21 #[error("Request error: {0}")]
22 RequestError(String),
23 #[error("Authentication required")]
24 NeedsAuthentication,
25 #[error("Parse error: {0}")]
26 ParseError(String),
27 #[error("Jet stream: {0}")]
28 JetstreamError(String),
29}
30
31#[derive(Clone)]
32pub struct BotApi {
33 pub agent: Arc<BskyAgent>,
34 handle_resolver: Arc<AtprotoHandleResolver<ApiDNSTxtResolver, DefaultHttpClient>>,
35 did_resolver: Arc<CommonDidResolver<DefaultHttpClient>>,
36}
37
38impl BotApi {
39 pub async fn new_logged_in(
40 handle: String,
41 password: String,
42 pds_url: String,
43 ) -> Result<Self, Error> {
44 // let session = get_new_session(pds_url);
45 // if let Err(error) = session.login(&handle, &password).await {
46 // return Err(Error::LoginError(error.to_string()));
47 // }
48
49 let agent = BskyAgent::builder()
50 .build()
51 .await
52 .expect("Failed to build agent");
53 agent.configure_endpoint(pds_url);
54 // let agent = Agent::new(session);
55 agent
56 .login(handle, password)
57 .await
58 .expect("Failed to login");
59 let handle_resolver = AtprotoHandleResolver::new(AtprotoHandleResolverConfig {
60 dns_txt_resolver: ApiDNSTxtResolver::default(),
61 http_client: Arc::new(DefaultHttpClient::default()),
62 });
63
64 let did_resolver = CommonDidResolver::new(CommonDidResolverConfig {
65 plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(),
66 http_client: Arc::new(DefaultHttpClient::default()),
67 });
68
69 Ok(Self {
70 agent: Arc::new(agent),
71 handle_resolver: Arc::new(handle_resolver),
72 did_resolver: Arc::new(did_resolver),
73 })
74 }
75
76 ///Takes a users handle like @baileytownsend.dev and resolves it ot the did
77 pub async fn resolve_handle(&self, handle: String) -> Result<Did, Error> {
78 let handle = Handle::new(handle).map_err(|e| Error::ParseError(e.to_string()))?;
79 self.handle_resolver
80 .resolve(&handle)
81 .await
82 .map_err(|e| Error::RequestError(e.to_string()))
83 }
84
85 /// Takes a did and returns a handle
86 pub async fn get_handle(&self, did: String) -> Result<String, Error> {
87 let did = Did::new(did).map_err(|e| Error::ParseError(e.to_string()))?;
88 match self.did_resolver.resolve(&did).await {
89 Ok(did_doc) => {
90 match did_doc.also_known_as {
91 None => Err(Error::ParseError(
92 "No also_known_as field found".to_string(),
93 ))?,
94 Some(also_known_as) => {
95 match also_known_as.is_empty() {
96 true => Err(Error::ParseError("also_known_as is empty".to_string()))?,
97 false => {
98 //also_known as a list starts the array with the highest priority handle
99 let formatted_handle =
100 format!("{}", also_known_as[0]).replace("at://", "");
101 Ok(formatted_handle)
102 }
103 }
104 }
105 }
106 }
107 Err(err) => Err(Error::ParseError(err.to_string()))?,
108 }
109 }
110}