From 0cd3202a9c40c8dbbd9d90d2eee58935caeffe67 Mon Sep 17 00:00:00 2001 From: Aleksei Voronov Date: Sun, 24 Sep 2023 20:26:34 +0200 Subject: [PATCH] Don't error out on profiles that don't exist anymore Nice little match there, sigh. Closes #1 --- README.md | 2 +- src/processes/profile_classifier.rs | 14 ++++++++---- src/services/bluesky/client.rs | 35 ++++++++++++++++++++++++----- 3 files changed, 41 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index b3d5335..667492e 100644 --- a/README.md +++ b/README.md @@ -14,7 +14,7 @@ Heavily WIP. Doesn't work yet at all, but does read the stream of posts as they - [x] Serve the feed - [x] Handle deleting of posts - [ ] Handle errors in the web service gracefully -- [ ] Handle missing profiles in the profile classifier +- [x] Handle missing profiles in the profile classifier - [ ] Add a way to mark a profile as being from a certain country manually - [ ] Handle reconnecting to websocket somehow - [ ] Publish the feed diff --git a/src/processes/profile_classifier.rs b/src/processes/profile_classifier.rs index 943e1b3..8c94544 100644 --- a/src/processes/profile_classifier.rs +++ b/src/processes/profile_classifier.rs @@ -54,10 +54,16 @@ impl ProfileClassifier { async fn fill_in_profile_details(&self, did: &str) -> Result<()> { let details = self.bluesky.fetch_profile_details(did).await?; - let country = self - .ai - .infer_country_of_living(&details.display_name, &details.description) - .await?; + + let country = match details { + Some(details) => { + self.ai + .infer_country_of_living(&details.display_name, &details.description) + .await? + } + None => "xx".to_owned(), + }; + self.database.store_profile_details(did, &country).await?; info!("Stored inferred country of living for {did}: {country}"); Ok(()) diff --git a/src/services/bluesky/client.rs b/src/services/bluesky/client.rs index 6eaffb9..8a195a5 100644 --- a/src/services/bluesky/client.rs +++ b/src/services/bluesky/client.rs @@ -1,9 +1,12 @@ +use std::matches; + use anyhow::{anyhow, Result}; use atrium_api::blob::BlobRef; use atrium_api::client::AtpServiceClient; use atrium_api::client::AtpServiceWrapper; use atrium_api::records::Record; use atrium_xrpc::client::reqwest::ReqwestClient; +use axum::http::StatusCode; use chrono::Utc; use futures::StreamExt; use log::error; @@ -104,7 +107,7 @@ impl Bluesky { Ok(()) } - pub async fn fetch_profile_details(&self, did: &str) -> Result { + pub async fn fetch_profile_details(&self, did: &str) -> Result> { let result = self .client .service @@ -117,17 +120,23 @@ impl Bluesky { repo: did.to_owned(), rkey: "self".to_owned(), }) - .await?; + .await; - let profile = match result.value { + let profile_data = match result { + Ok(profile_data) => profile_data, + Err(e) if is_missing_record_error(&e) => return Ok(None), + Err(e) => return Err(e.into()), + }; + + let profile = match profile_data.value { Record::AppBskyActorProfile(profile) => profile, _ => return Err(anyhow!("Big bad, no such profile")), }; - Ok(ProfileDetails { + Ok(Some(ProfileDetails { display_name: profile.display_name.unwrap_or_default(), description: profile.description.unwrap_or_default(), - }) + })) } pub async fn subscribe_to_operations( @@ -154,3 +163,19 @@ impl Bluesky { Ok(()) } } + +fn is_missing_record_error(error: &atrium_xrpc::error::Error) -> bool { + use atrium_xrpc::error::{Error, ErrorResponseBody, XrpcError, XrpcErrorKind}; + + matches!(error, + Error::XrpcResponse(XrpcError { + status: StatusCode::BAD_REQUEST, + error: + Some(XrpcErrorKind::Undefined(ErrorResponseBody { + error: Some(error_code), + message: Some(error_message), + })), + }) if error_code == "InvalidRequest" + && error_message.starts_with("Could not locate record") + ) +}