2021-11-13 17:37:31 +00:00
|
|
|
use std::path::Path;
|
2021-04-09 00:22:17 +00:00
|
|
|
|
|
|
|
use serde_json::Value;
|
|
|
|
|
|
|
|
use crate::models::profiles::types::ProfileCreateData;
|
|
|
|
use crate::utils::files::{save_file, FileError};
|
|
|
|
use crate::webfinger::types::JsonResourceDescriptor;
|
2021-10-09 23:59:45 +00:00
|
|
|
use super::activity::Object;
|
2021-04-09 00:22:17 +00:00
|
|
|
use super::actor::Actor;
|
|
|
|
use super::constants::ACTIVITY_CONTENT_TYPE;
|
|
|
|
|
|
|
|
#[derive(thiserror::Error, Debug)]
|
|
|
|
pub enum FetchError {
|
|
|
|
#[error("invalid URL")]
|
|
|
|
UrlError(#[from] url::ParseError),
|
|
|
|
|
|
|
|
#[error(transparent)]
|
|
|
|
RequestError(#[from] reqwest::Error),
|
|
|
|
|
|
|
|
#[error("json parse error")]
|
|
|
|
JsonParseError(#[from] serde_json::Error),
|
|
|
|
|
|
|
|
#[error("file error")]
|
|
|
|
FileError(#[from] FileError),
|
|
|
|
|
|
|
|
#[error("{0}")]
|
|
|
|
OtherError(&'static str),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn fetch_avatar_and_banner(
|
|
|
|
actor: &Actor,
|
2021-11-13 17:37:31 +00:00
|
|
|
media_dir: &Path,
|
2021-04-09 00:22:17 +00:00
|
|
|
) -> Result<(Option<String>, Option<String>), FetchError> {
|
|
|
|
let avatar = match &actor.icon {
|
|
|
|
Some(icon) => {
|
|
|
|
let file_name = fetch_attachment(
|
|
|
|
&icon.url,
|
|
|
|
media_dir,
|
|
|
|
).await?;
|
|
|
|
Some(file_name)
|
|
|
|
},
|
|
|
|
None => None,
|
|
|
|
};
|
|
|
|
let banner = match &actor.image {
|
|
|
|
Some(image) => {
|
|
|
|
let file_name = fetch_attachment(
|
|
|
|
&image.url,
|
|
|
|
media_dir,
|
|
|
|
).await?;
|
|
|
|
Some(file_name)
|
|
|
|
},
|
|
|
|
None => None,
|
|
|
|
};
|
|
|
|
Ok((avatar, banner))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn fetch_profile(
|
|
|
|
username: &str,
|
2021-11-10 14:32:27 +00:00
|
|
|
instance_host: &str,
|
2021-11-13 17:37:31 +00:00
|
|
|
media_dir: &Path,
|
2021-04-09 00:22:17 +00:00
|
|
|
) -> Result<ProfileCreateData, FetchError> {
|
2021-11-10 14:32:27 +00:00
|
|
|
let actor_address = format!("{}@{}", &username, &instance_host);
|
2021-04-09 00:22:17 +00:00
|
|
|
let webfinger_account_uri = format!("acct:{}", actor_address);
|
|
|
|
// TOOD: support http
|
2021-11-10 14:32:27 +00:00
|
|
|
let webfinger_url = format!("https://{}/.well-known/webfinger", instance_host);
|
2021-04-09 00:22:17 +00:00
|
|
|
let client = reqwest::Client::new();
|
|
|
|
let webfinger_data = client.get(&webfinger_url)
|
|
|
|
.query(&[("resource", webfinger_account_uri)])
|
|
|
|
.send().await?
|
2021-11-07 13:43:20 +00:00
|
|
|
.error_for_status()?
|
2021-04-09 00:22:17 +00:00
|
|
|
.text().await?;
|
|
|
|
let jrd: JsonResourceDescriptor = serde_json::from_str(&webfinger_data)?;
|
|
|
|
let link = jrd.links.iter()
|
|
|
|
.find(|link| link.rel == "self")
|
|
|
|
.ok_or(FetchError::OtherError("self link not found"))?;
|
|
|
|
let actor_url = link.href.as_ref()
|
|
|
|
.ok_or(FetchError::OtherError("account href not found"))?;
|
|
|
|
fetch_profile_by_actor_id(actor_url, media_dir).await
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn fetch_profile_by_actor_id(
|
|
|
|
actor_url: &str,
|
2021-11-13 17:37:31 +00:00
|
|
|
media_dir: &Path,
|
2021-04-09 00:22:17 +00:00
|
|
|
) -> Result<ProfileCreateData, FetchError> {
|
|
|
|
let actor_host = url::Url::parse(actor_url)?
|
|
|
|
.host_str()
|
|
|
|
.ok_or(FetchError::OtherError("invalid URL"))?
|
|
|
|
.to_owned();
|
|
|
|
let client = reqwest::Client::new();
|
|
|
|
let actor_json = client.get(actor_url)
|
|
|
|
.header(reqwest::header::ACCEPT, ACTIVITY_CONTENT_TYPE)
|
|
|
|
.send().await?
|
|
|
|
.text().await?;
|
|
|
|
let actor_value: Value = serde_json::from_str(&actor_json)?;
|
|
|
|
let actor: Actor = serde_json::from_value(actor_value.clone())?;
|
|
|
|
let (avatar, banner) = fetch_avatar_and_banner(&actor, media_dir).await?;
|
2021-09-17 12:36:24 +00:00
|
|
|
let extra_fields = actor.extra_fields();
|
2021-04-09 00:22:17 +00:00
|
|
|
let actor_address = format!(
|
|
|
|
"{}@{}",
|
|
|
|
actor.preferred_username,
|
|
|
|
actor_host,
|
|
|
|
);
|
|
|
|
let profile_data = ProfileCreateData {
|
|
|
|
username: actor.preferred_username,
|
|
|
|
display_name: Some(actor.name),
|
|
|
|
acct: actor_address,
|
|
|
|
bio: actor.summary,
|
2021-09-17 12:36:24 +00:00
|
|
|
avatar,
|
|
|
|
banner,
|
|
|
|
extra_fields,
|
2021-04-09 00:22:17 +00:00
|
|
|
actor: Some(actor_value),
|
|
|
|
};
|
|
|
|
Ok(profile_data)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub async fn fetch_attachment(
|
|
|
|
url: &str,
|
2021-11-13 17:37:31 +00:00
|
|
|
output_dir: &Path,
|
2021-04-09 00:22:17 +00:00
|
|
|
) -> Result<String, FetchError> {
|
|
|
|
let response = reqwest::get(url).await?;
|
|
|
|
let file_data = response.bytes().await?;
|
|
|
|
let file_name = save_file(file_data.to_vec(), output_dir)?;
|
|
|
|
Ok(file_name)
|
|
|
|
}
|
2021-10-09 23:59:45 +00:00
|
|
|
|
|
|
|
pub async fn fetch_object(
|
|
|
|
object_url: &str,
|
|
|
|
) -> Result<Object, FetchError> {
|
|
|
|
let client = reqwest::Client::new();
|
|
|
|
let object_json = client.get(object_url)
|
|
|
|
.header(reqwest::header::ACCEPT, ACTIVITY_CONTENT_TYPE)
|
|
|
|
.send().await?
|
|
|
|
.text().await?;
|
|
|
|
let object_value: Value = serde_json::from_str(&object_json)?;
|
|
|
|
let object: Object = serde_json::from_value(object_value)?;
|
|
|
|
Ok(object)
|
|
|
|
}
|