mod resolver; use crate::resolver::ApiDNSTxtResolver; use atrium_api::agent::Configure; use atrium_api::types::string::{Did, Handle}; use atrium_common::resolver::Resolver; use atrium_identity::did::{CommonDidResolver, CommonDidResolverConfig, DEFAULT_PLC_DIRECTORY_URL}; use atrium_identity::handle::{AtprotoHandleResolver, AtprotoHandleResolverConfig}; use atrium_oauth::DefaultHttpClient; use bsky_sdk::BskyAgent; use std::sync::Arc; use thiserror::Error; #[derive(Debug, Error)] pub enum Error { #[error("General error: {0}")] GeneralError(String), #[error("Login error: {0}")] LoginError(String), #[error("Write error: {0}")] WriteError(String), #[error("Request error: {0}")] RequestError(String), #[error("Authentication required")] NeedsAuthentication, #[error("Parse error: {0}")] ParseError(String), #[error("Jet stream: {0}")] JetstreamError(String), } #[derive(Clone)] pub struct BotApi { pub agent: Arc, handle_resolver: Arc>, did_resolver: Arc>, } impl BotApi { pub async fn new_logged_in( handle: String, password: String, pds_url: String, ) -> Result { // let session = get_new_session(pds_url); // if let Err(error) = session.login(&handle, &password).await { // return Err(Error::LoginError(error.to_string())); // } let agent = BskyAgent::builder() .build() .await .expect("Failed to build agent"); agent.configure_endpoint(pds_url); // let agent = Agent::new(session); agent .login(handle, password) .await .expect("Failed to login"); let handle_resolver = AtprotoHandleResolver::new(AtprotoHandleResolverConfig { dns_txt_resolver: ApiDNSTxtResolver::default(), http_client: Arc::new(DefaultHttpClient::default()), }); let did_resolver = CommonDidResolver::new(CommonDidResolverConfig { plc_directory_url: DEFAULT_PLC_DIRECTORY_URL.to_string(), http_client: Arc::new(DefaultHttpClient::default()), }); Ok(Self { agent: Arc::new(agent), handle_resolver: Arc::new(handle_resolver), did_resolver: Arc::new(did_resolver), }) } ///Takes a users handle like @baileytownsend.dev and resolves it ot the did pub async fn resolve_handle(&self, handle: String) -> Result { let handle = Handle::new(handle).map_err(|e| Error::ParseError(e.to_string()))?; self.handle_resolver .resolve(&handle) .await .map_err(|e| Error::RequestError(e.to_string())) } /// Takes a did and returns a handle pub async fn get_handle(&self, did: String) -> Result { let did = Did::new(did).map_err(|e| Error::ParseError(e.to_string()))?; match self.did_resolver.resolve(&did).await { Ok(did_doc) => { match did_doc.also_known_as { None => Err(Error::ParseError( "No also_known_as field found".to_string(), ))?, Some(also_known_as) => { match also_known_as.is_empty() { true => Err(Error::ParseError("also_known_as is empty".to_string()))?, false => { //also_known as a list starts the array with the highest priority handle let formatted_handle = format!("{}", also_known_as[0]).replace("at://", ""); Ok(formatted_handle) } } } } } Err(err) => Err(Error::ParseError(err.to_string()))?, } } }