2022-05-02 00:24:44 +00:00
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::path::Path;
|
|
|
|
|
2022-09-29 15:26:12 +00:00
|
|
|
use chrono::Utc;
|
2022-07-28 14:09:57 +00:00
|
|
|
use serde_json::{Value as JsonValue};
|
2022-05-02 00:24:44 +00:00
|
|
|
use tokio_postgres::GenericClient;
|
|
|
|
use uuid::Uuid;
|
|
|
|
|
2022-07-16 01:49:27 +00:00
|
|
|
use crate::activitypub::{
|
2022-12-09 21:16:53 +00:00
|
|
|
activity::{Attachment, Link, Object, Tag},
|
2022-10-01 11:08:56 +00:00
|
|
|
constants::{AP_MEDIA_TYPE, AP_PUBLIC, AS_MEDIA_TYPE},
|
2022-07-16 01:49:27 +00:00
|
|
|
fetcher::fetchers::fetch_file,
|
|
|
|
fetcher::helpers::{
|
2023-01-11 02:16:03 +00:00
|
|
|
get_or_import_profile_by_actor_address,
|
2022-07-16 01:49:27 +00:00
|
|
|
get_or_import_profile_by_actor_id,
|
2022-12-07 18:46:00 +00:00
|
|
|
import_post,
|
2022-07-16 01:49:27 +00:00
|
|
|
},
|
2022-10-15 11:54:20 +00:00
|
|
|
identifiers::parse_local_actor_id,
|
2022-10-23 22:18:01 +00:00
|
|
|
receiver::{parse_array, parse_property_value, HandlerError},
|
2022-11-21 23:24:40 +00:00
|
|
|
vocabulary::*,
|
2022-05-02 00:24:44 +00:00
|
|
|
};
|
2022-12-07 18:46:00 +00:00
|
|
|
use crate::config::{Config, Instance};
|
2023-01-11 19:49:12 +00:00
|
|
|
use crate::database::DatabaseError;
|
2022-12-03 22:09:42 +00:00
|
|
|
use crate::errors::{ConversionError, ValidationError};
|
2022-05-02 00:24:44 +00:00
|
|
|
use crate::models::attachments::queries::create_attachment;
|
2022-10-25 19:03:38 +00:00
|
|
|
use crate::models::posts::{
|
|
|
|
hashtags::normalize_hashtag,
|
|
|
|
helpers::get_post_by_object_id,
|
|
|
|
mentions::mention_to_address,
|
|
|
|
queries::create_post,
|
|
|
|
types::{Post, PostCreateData, Visibility},
|
2023-01-06 16:52:28 +00:00
|
|
|
validators::{
|
|
|
|
content_allowed_classes,
|
|
|
|
ATTACHMENTS_MAX_NUM,
|
|
|
|
CONTENT_MAX_SIZE,
|
|
|
|
},
|
2022-10-25 19:03:38 +00:00
|
|
|
};
|
2022-02-13 18:55:37 +00:00
|
|
|
use crate::models::profiles::types::DbActorProfile;
|
2022-05-02 00:24:44 +00:00
|
|
|
use crate::models::users::queries::get_user_by_name;
|
|
|
|
use crate::utils::html::clean_html;
|
2022-12-07 18:46:00 +00:00
|
|
|
use super::HandlerResult;
|
2022-05-02 00:24:44 +00:00
|
|
|
|
|
|
|
fn get_note_author_id(object: &Object) -> Result<String, ValidationError> {
|
|
|
|
let attributed_to = object.attributed_to.as_ref()
|
|
|
|
.ok_or(ValidationError("unattributed note"))?;
|
|
|
|
let author_id = parse_array(attributed_to)
|
|
|
|
.map_err(|_| ValidationError("invalid attributedTo property"))?
|
|
|
|
.get(0)
|
|
|
|
.ok_or(ValidationError("invalid attributedTo property"))?
|
|
|
|
.to_string();
|
|
|
|
Ok(author_id)
|
|
|
|
}
|
|
|
|
|
2022-07-28 14:09:57 +00:00
|
|
|
fn parse_object_url(value: &JsonValue) -> Result<String, ConversionError> {
|
|
|
|
let object_url = match value {
|
|
|
|
JsonValue::String(string) => string.to_owned(),
|
|
|
|
other_value => {
|
|
|
|
let links: Vec<Link> = parse_property_value(other_value)?;
|
|
|
|
if let Some(link) = links.get(0) {
|
|
|
|
link.href.clone()
|
|
|
|
} else {
|
|
|
|
return Err(ConversionError);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
};
|
|
|
|
Ok(object_url)
|
|
|
|
}
|
|
|
|
|
2022-05-11 12:50:36 +00:00
|
|
|
pub fn get_note_content(object: &Object) -> Result<String, ValidationError> {
|
2022-10-13 12:44:45 +00:00
|
|
|
let mut content = if let Some(ref content) = object.content {
|
|
|
|
if object.media_type == Some("text/markdown".to_string()) {
|
|
|
|
format!("<p>{}</p>", content)
|
|
|
|
} else {
|
|
|
|
// HTML
|
|
|
|
content.to_string()
|
|
|
|
}
|
|
|
|
} else {
|
2022-05-12 15:38:40 +00:00
|
|
|
// Lemmy pages and PeerTube videos have "name" property
|
2022-10-13 12:44:45 +00:00
|
|
|
object.name.as_deref().unwrap_or("").to_string()
|
|
|
|
};
|
2022-07-28 14:09:57 +00:00
|
|
|
if object.object_type != NOTE {
|
2022-12-06 19:48:58 +00:00
|
|
|
// Append link to object
|
|
|
|
let object_url = if let Some(ref value) = object.url {
|
|
|
|
parse_object_url(value)
|
|
|
|
.map_err(|_| ValidationError("invalid object URL"))?
|
|
|
|
} else {
|
|
|
|
object.id.clone()
|
2022-07-28 14:09:57 +00:00
|
|
|
};
|
2022-12-06 19:48:58 +00:00
|
|
|
content += &format!(
|
|
|
|
r#"<p><a href="{0}">{0}</a></p>"#,
|
|
|
|
object_url,
|
|
|
|
);
|
2022-07-28 14:09:57 +00:00
|
|
|
};
|
2022-05-02 00:24:44 +00:00
|
|
|
if content.len() > CONTENT_MAX_SIZE {
|
|
|
|
return Err(ValidationError("content is too long"));
|
|
|
|
};
|
2022-11-29 16:46:00 +00:00
|
|
|
let content_safe = clean_html(&content, content_allowed_classes());
|
2022-05-02 00:24:44 +00:00
|
|
|
Ok(content_safe)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_note_visibility(
|
2022-02-13 18:55:37 +00:00
|
|
|
author: &DbActorProfile,
|
|
|
|
primary_audience: Vec<String>,
|
|
|
|
secondary_audience: Vec<String>,
|
|
|
|
) -> Visibility {
|
2023-01-12 23:58:14 +00:00
|
|
|
let audience = [primary_audience, secondary_audience].concat();
|
|
|
|
// Some servers (e.g. Takahe) use "as" namespace
|
|
|
|
const PUBLIC_VARIANTS: [&str; 2] = [AP_PUBLIC, "as:Public"];
|
|
|
|
if audience.iter().any(|item| PUBLIC_VARIANTS.contains(&item.as_str())) {
|
2022-06-14 23:41:01 +00:00
|
|
|
return Visibility::Public;
|
|
|
|
};
|
|
|
|
let maybe_followers = author.actor_json.as_ref()
|
|
|
|
.and_then(|actor| actor.followers.as_ref());
|
|
|
|
if let Some(followers) = maybe_followers {
|
2023-01-12 23:58:14 +00:00
|
|
|
if audience.contains(followers) {
|
2022-06-14 23:41:01 +00:00
|
|
|
return Visibility::Followers;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
let maybe_subscribers = author.actor_json.as_ref()
|
|
|
|
.and_then(|actor| actor.subscribers.as_ref());
|
|
|
|
if let Some(subscribers) = maybe_subscribers {
|
2023-01-12 23:58:14 +00:00
|
|
|
if audience.contains(subscribers) {
|
2022-06-14 23:41:01 +00:00
|
|
|
return Visibility::Subscribers;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
Visibility::Direct
|
2022-02-13 18:55:37 +00:00
|
|
|
}
|
|
|
|
|
2022-05-02 00:24:44 +00:00
|
|
|
pub async fn handle_note(
|
|
|
|
db_client: &mut impl GenericClient,
|
|
|
|
instance: &Instance,
|
|
|
|
media_dir: &Path,
|
|
|
|
object: Object,
|
|
|
|
redirects: &HashMap<String, String>,
|
2022-10-23 22:18:01 +00:00
|
|
|
) -> Result<Post, HandlerError> {
|
2022-11-21 23:24:40 +00:00
|
|
|
match object.object_type.as_str() {
|
|
|
|
NOTE => (),
|
2022-12-06 19:48:58 +00:00
|
|
|
ARTICLE | EVENT | QUESTION | PAGE | VIDEO => {
|
2022-11-21 23:24:40 +00:00
|
|
|
log::info!("processing object of type {}", object.object_type);
|
|
|
|
},
|
|
|
|
other_type => {
|
|
|
|
log::warn!("discarding object of type {}", other_type);
|
|
|
|
return Err(ValidationError("unsupported type").into());
|
|
|
|
},
|
2022-05-02 00:24:44 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
let author_id = get_note_author_id(&object)?;
|
|
|
|
let author = get_or_import_profile_by_actor_id(
|
|
|
|
db_client,
|
|
|
|
instance,
|
|
|
|
media_dir,
|
|
|
|
&author_id,
|
2022-07-09 21:24:37 +00:00
|
|
|
).await.map_err(|err| {
|
|
|
|
log::warn!("failed to import {} ({})", author_id, err);
|
|
|
|
err
|
|
|
|
})?;
|
2022-05-02 00:24:44 +00:00
|
|
|
let content = get_note_content(&object)?;
|
2022-09-29 15:26:12 +00:00
|
|
|
let created_at = object.published.unwrap_or(Utc::now());
|
2022-05-02 00:24:44 +00:00
|
|
|
|
|
|
|
let mut attachments: Vec<Uuid> = Vec::new();
|
|
|
|
if let Some(value) = object.attachment {
|
|
|
|
let list: Vec<Attachment> = parse_property_value(&value)
|
|
|
|
.map_err(|_| ValidationError("invalid attachment property"))?;
|
|
|
|
let mut downloaded = vec![];
|
|
|
|
for attachment in list {
|
2022-12-10 18:02:08 +00:00
|
|
|
match attachment.attachment_type.as_str() {
|
|
|
|
DOCUMENT | IMAGE | VIDEO => (),
|
|
|
|
_ => {
|
|
|
|
log::warn!(
|
|
|
|
"skipping attachment of type {}",
|
|
|
|
attachment.attachment_type,
|
|
|
|
);
|
|
|
|
continue;
|
|
|
|
},
|
2022-05-02 00:24:44 +00:00
|
|
|
};
|
2023-01-14 19:27:47 +00:00
|
|
|
if attachment.media_type.as_deref() == Some("text/html; charset=UTF-8") {
|
|
|
|
// Don't fetch HTML pages attached by GNU Social
|
|
|
|
continue;
|
|
|
|
};
|
2022-05-02 00:24:44 +00:00
|
|
|
let attachment_url = attachment.url
|
|
|
|
.ok_or(ValidationError("attachment URL is missing"))?;
|
2023-01-14 00:46:49 +00:00
|
|
|
let (file_name, maybe_media_type) = fetch_file(
|
2022-10-19 18:39:47 +00:00
|
|
|
instance,
|
|
|
|
&attachment_url,
|
2023-01-04 20:04:10 +00:00
|
|
|
attachment.media_type.as_deref(),
|
2022-10-19 18:39:47 +00:00
|
|
|
media_dir,
|
|
|
|
).await
|
2022-09-18 23:57:39 +00:00
|
|
|
.map_err(|err| {
|
|
|
|
log::warn!("{}", err);
|
|
|
|
ValidationError("failed to fetch attachment")
|
|
|
|
})?;
|
2022-05-02 00:24:44 +00:00
|
|
|
log::info!("downloaded attachment {}", attachment_url);
|
2023-01-14 00:46:49 +00:00
|
|
|
downloaded.push((file_name, maybe_media_type));
|
2022-09-27 23:39:10 +00:00
|
|
|
// Stop downloading if limit is reached
|
|
|
|
if downloaded.len() >= ATTACHMENTS_MAX_NUM {
|
|
|
|
log::warn!("too many attachments");
|
|
|
|
break;
|
|
|
|
};
|
2022-05-02 00:24:44 +00:00
|
|
|
};
|
2023-01-14 00:46:49 +00:00
|
|
|
for (file_name, maybe_media_type) in downloaded {
|
2022-05-02 00:24:44 +00:00
|
|
|
let db_attachment = create_attachment(
|
|
|
|
db_client,
|
|
|
|
&author.id,
|
|
|
|
file_name,
|
2023-01-14 00:46:49 +00:00
|
|
|
maybe_media_type,
|
2022-05-02 00:24:44 +00:00
|
|
|
).await?;
|
|
|
|
attachments.push(db_attachment.id);
|
|
|
|
};
|
|
|
|
};
|
2022-09-29 15:26:12 +00:00
|
|
|
if content.is_empty() && attachments.is_empty() {
|
|
|
|
return Err(ValidationError("post is empty").into());
|
|
|
|
};
|
|
|
|
|
2022-05-02 00:24:44 +00:00
|
|
|
let mut mentions: Vec<Uuid> = Vec::new();
|
2023-01-02 21:46:24 +00:00
|
|
|
let mut hashtags = vec![];
|
2022-08-21 22:51:40 +00:00
|
|
|
let mut links = vec![];
|
2022-09-17 16:35:23 +00:00
|
|
|
if let Some(value) = object.tag {
|
2023-01-02 21:46:24 +00:00
|
|
|
let list: Vec<JsonValue> = parse_property_value(&value)
|
2022-09-17 16:35:23 +00:00
|
|
|
.map_err(|_| ValidationError("invalid tag property"))?;
|
2023-01-02 21:46:24 +00:00
|
|
|
for tag_value in list {
|
|
|
|
let tag: Tag = serde_json::from_value(tag_value.clone())
|
|
|
|
.map_err(|_| ValidationError("invalid tag"))?;
|
2022-05-02 00:24:44 +00:00
|
|
|
if tag.tag_type == HASHTAG {
|
|
|
|
if let Some(tag_name) = tag.name {
|
|
|
|
// Ignore invalid tags
|
2022-08-21 21:33:44 +00:00
|
|
|
if let Ok(tag_name) = normalize_hashtag(&tag_name) {
|
2023-01-02 21:46:24 +00:00
|
|
|
if !hashtags.contains(&tag_name) {
|
|
|
|
hashtags.push(tag_name);
|
2022-07-10 20:57:09 +00:00
|
|
|
};
|
2022-05-02 00:24:44 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
} else if tag.tag_type == MENTION {
|
|
|
|
// Try to find profile by actor ID.
|
|
|
|
if let Some(href) = tag.href {
|
2022-07-16 01:49:27 +00:00
|
|
|
if let Ok(username) = parse_local_actor_id(&instance.url(), &href) {
|
2022-05-02 00:24:44 +00:00
|
|
|
let user = get_user_by_name(db_client, &username).await?;
|
|
|
|
if !mentions.contains(&user.id) {
|
|
|
|
mentions.push(user.id);
|
|
|
|
};
|
|
|
|
continue;
|
|
|
|
};
|
2022-07-09 20:29:42 +00:00
|
|
|
// NOTE: `href` attribute is usually actor ID
|
2022-05-02 00:24:44 +00:00
|
|
|
// but also can be actor URL (profile link).
|
|
|
|
match get_or_import_profile_by_actor_id(
|
|
|
|
db_client,
|
|
|
|
instance,
|
|
|
|
media_dir,
|
|
|
|
&href,
|
|
|
|
).await {
|
|
|
|
Ok(profile) => {
|
|
|
|
if !mentions.contains(&profile.id) {
|
|
|
|
mentions.push(profile.id);
|
|
|
|
};
|
|
|
|
continue;
|
|
|
|
},
|
|
|
|
Err(error) => {
|
2022-07-10 12:41:01 +00:00
|
|
|
log::warn!(
|
|
|
|
"failed to find mentioned profile by ID {}: {}",
|
|
|
|
href,
|
|
|
|
error,
|
|
|
|
);
|
2022-05-02 00:24:44 +00:00
|
|
|
},
|
|
|
|
};
|
|
|
|
};
|
|
|
|
// Try to find profile by actor address
|
|
|
|
let tag_name = match tag.name {
|
|
|
|
Some(name) => name,
|
|
|
|
None => {
|
|
|
|
log::warn!("failed to parse mention");
|
|
|
|
continue;
|
|
|
|
},
|
|
|
|
};
|
2022-10-03 21:21:20 +00:00
|
|
|
if let Ok(actor_address) = mention_to_address(&tag_name) {
|
2023-01-11 02:16:03 +00:00
|
|
|
let profile = match get_or_import_profile_by_actor_address(
|
2022-05-02 00:24:44 +00:00
|
|
|
db_client,
|
2023-01-11 02:16:03 +00:00
|
|
|
instance,
|
|
|
|
media_dir,
|
|
|
|
&actor_address,
|
2022-05-02 00:24:44 +00:00
|
|
|
).await {
|
|
|
|
Ok(profile) => profile,
|
2023-01-11 19:49:12 +00:00
|
|
|
Err(error @ (
|
|
|
|
HandlerError::FetchError(_) |
|
|
|
|
HandlerError::DatabaseError(DatabaseError::NotFound(_))
|
|
|
|
)) => {
|
2023-01-11 02:16:03 +00:00
|
|
|
// Ignore mention if fetcher fails
|
2023-01-11 19:49:12 +00:00
|
|
|
// Ignore mention if local address is not valid
|
2023-01-11 02:16:03 +00:00
|
|
|
log::warn!(
|
|
|
|
"failed to find mentioned profile {}: {}",
|
|
|
|
actor_address,
|
|
|
|
error,
|
|
|
|
);
|
|
|
|
continue;
|
2022-05-02 00:24:44 +00:00
|
|
|
},
|
2023-01-11 02:16:03 +00:00
|
|
|
Err(other_error) => return Err(other_error),
|
2022-05-02 00:24:44 +00:00
|
|
|
};
|
|
|
|
if !mentions.contains(&profile.id) {
|
|
|
|
mentions.push(profile.id);
|
|
|
|
};
|
|
|
|
} else {
|
|
|
|
log::warn!("failed to parse mention {}", tag_name);
|
|
|
|
};
|
2022-10-01 11:08:56 +00:00
|
|
|
} else if tag.tag_type == LINK {
|
|
|
|
if tag.media_type != Some(AP_MEDIA_TYPE.to_string()) &&
|
|
|
|
tag.media_type != Some(AS_MEDIA_TYPE.to_string())
|
|
|
|
{
|
|
|
|
// Unknown media type
|
|
|
|
continue;
|
|
|
|
};
|
2022-10-15 11:54:20 +00:00
|
|
|
if let Some(ref href) = tag.href {
|
|
|
|
let href = redirects.get(href).unwrap_or(href);
|
|
|
|
let linked = get_post_by_object_id(
|
2022-10-01 11:08:56 +00:00
|
|
|
db_client,
|
|
|
|
&instance.url(),
|
2022-10-15 11:54:20 +00:00
|
|
|
href,
|
2022-10-01 11:08:56 +00:00
|
|
|
).await?;
|
2022-10-15 11:54:20 +00:00
|
|
|
if !links.contains(&linked.id) {
|
|
|
|
links.push(linked.id);
|
|
|
|
};
|
2022-10-01 11:08:56 +00:00
|
|
|
};
|
2023-01-02 21:46:24 +00:00
|
|
|
} else if tag.tag_type == EMOJI {
|
|
|
|
log::info!("found emoji tag: {}", tag_value);
|
|
|
|
} else {
|
|
|
|
log::warn!(
|
|
|
|
"skipping tag of type {}",
|
|
|
|
tag.tag_type,
|
|
|
|
);
|
2022-05-02 00:24:44 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
};
|
2022-08-21 22:51:40 +00:00
|
|
|
if let Some(ref object_id) = object.quote_url {
|
2022-10-15 11:54:20 +00:00
|
|
|
let object_id = redirects.get(object_id).unwrap_or(object_id);
|
|
|
|
let linked = get_post_by_object_id(
|
2022-08-21 22:51:40 +00:00
|
|
|
db_client,
|
|
|
|
&instance.url(),
|
|
|
|
object_id,
|
|
|
|
).await?;
|
2022-10-15 11:54:20 +00:00
|
|
|
if !links.contains(&linked.id) {
|
|
|
|
links.push(linked.id);
|
|
|
|
};
|
2022-08-21 22:51:40 +00:00
|
|
|
};
|
|
|
|
|
2022-05-02 00:24:44 +00:00
|
|
|
let in_reply_to_id = match object.in_reply_to {
|
2022-08-21 22:51:40 +00:00
|
|
|
Some(ref object_id) => {
|
2022-10-15 11:54:20 +00:00
|
|
|
let object_id = redirects.get(object_id).unwrap_or(object_id);
|
|
|
|
let in_reply_to = get_post_by_object_id(
|
2022-08-21 22:51:40 +00:00
|
|
|
db_client,
|
|
|
|
&instance.url(),
|
|
|
|
object_id,
|
|
|
|
).await?;
|
2022-10-15 11:54:20 +00:00
|
|
|
Some(in_reply_to.id)
|
2022-05-02 00:24:44 +00:00
|
|
|
},
|
|
|
|
None => None,
|
|
|
|
};
|
|
|
|
let primary_audience = match object.to {
|
|
|
|
Some(value) => {
|
|
|
|
parse_array(&value)
|
|
|
|
.map_err(|_| ValidationError("invalid 'to' property value"))?
|
|
|
|
},
|
|
|
|
None => vec![],
|
|
|
|
};
|
|
|
|
let secondary_audience = match object.cc {
|
|
|
|
Some(value) => {
|
|
|
|
parse_array(&value)
|
|
|
|
.map_err(|_| ValidationError("invalid 'cc' property value"))?
|
|
|
|
},
|
|
|
|
None => vec![],
|
|
|
|
};
|
|
|
|
let visibility = get_note_visibility(
|
|
|
|
&author,
|
|
|
|
primary_audience,
|
|
|
|
secondary_audience,
|
|
|
|
);
|
|
|
|
if visibility != Visibility::Public {
|
|
|
|
log::warn!(
|
|
|
|
"processing note with visibility {:?} attributed to {}",
|
|
|
|
visibility,
|
|
|
|
author.username,
|
|
|
|
);
|
|
|
|
};
|
|
|
|
let post_data = PostCreateData {
|
|
|
|
content: content,
|
|
|
|
in_reply_to_id,
|
|
|
|
repost_of_id: None,
|
|
|
|
visibility,
|
|
|
|
attachments: attachments,
|
|
|
|
mentions: mentions,
|
2023-01-02 21:46:24 +00:00
|
|
|
tags: hashtags,
|
2022-08-21 22:51:40 +00:00
|
|
|
links: links,
|
2022-05-02 00:24:44 +00:00
|
|
|
object_id: Some(object.id),
|
2022-09-29 15:26:12 +00:00
|
|
|
created_at,
|
2022-05-02 00:24:44 +00:00
|
|
|
};
|
|
|
|
let post = create_post(db_client, &author.id, post_data).await?;
|
|
|
|
Ok(post)
|
|
|
|
}
|
|
|
|
|
2022-12-07 18:46:00 +00:00
|
|
|
pub async fn handle_create(
|
|
|
|
config: &Config,
|
|
|
|
db_client: &mut impl GenericClient,
|
2022-12-09 21:16:53 +00:00
|
|
|
activity: JsonValue,
|
2022-12-08 18:01:47 +00:00
|
|
|
is_authenticated: bool,
|
2022-12-07 18:46:00 +00:00
|
|
|
) -> HandlerResult {
|
2022-12-09 21:16:53 +00:00
|
|
|
let object: Object = serde_json::from_value(activity["object"].to_owned())
|
2022-12-07 18:46:00 +00:00
|
|
|
.map_err(|_| ValidationError("invalid object"))?;
|
|
|
|
let object_id = object.id.clone();
|
2022-12-08 18:01:47 +00:00
|
|
|
let object_received = if is_authenticated {
|
2022-12-07 18:46:00 +00:00
|
|
|
Some(object)
|
|
|
|
} else {
|
2022-12-08 18:01:47 +00:00
|
|
|
// Fetch object, don't trust the sender.
|
2022-12-07 18:46:00 +00:00
|
|
|
// Most likely it's a forwarded reply.
|
|
|
|
None
|
|
|
|
};
|
|
|
|
import_post(config, db_client, object_id, object_received).await?;
|
|
|
|
Ok(Some(NOTE))
|
|
|
|
}
|
|
|
|
|
2022-02-13 18:55:37 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2022-07-28 14:09:57 +00:00
|
|
|
use serde_json::json;
|
|
|
|
use crate::activitypub::{
|
|
|
|
activity::Object,
|
|
|
|
actors::types::Actor,
|
|
|
|
vocabulary::NOTE,
|
|
|
|
};
|
2022-02-13 18:55:37 +00:00
|
|
|
use super::*;
|
|
|
|
|
2023-01-13 01:13:29 +00:00
|
|
|
#[test]
|
|
|
|
fn test_get_note_author_id() {
|
|
|
|
let object = Object {
|
|
|
|
object_type: NOTE.to_string(),
|
|
|
|
attributed_to: Some(json!(["https://example.org/1"])),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
let author_id = get_note_author_id(&object).unwrap();
|
|
|
|
assert_eq!(author_id, "https://example.org/1");
|
|
|
|
}
|
|
|
|
|
2022-05-02 00:24:44 +00:00
|
|
|
#[test]
|
|
|
|
fn test_get_note_content() {
|
|
|
|
let object = Object {
|
|
|
|
content: Some("test".to_string()),
|
|
|
|
object_type: NOTE.to_string(),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
let content = get_note_content(&object).unwrap();
|
|
|
|
assert_eq!(content, "test");
|
|
|
|
}
|
|
|
|
|
2022-07-28 14:09:57 +00:00
|
|
|
#[test]
|
|
|
|
fn test_get_note_content_from_video() {
|
|
|
|
let object = Object {
|
|
|
|
name: Some("test-name".to_string()),
|
|
|
|
content: Some("test-content".to_string()),
|
|
|
|
object_type: "Video".to_string(),
|
|
|
|
url: Some(json!([{
|
|
|
|
"type": "Link",
|
|
|
|
"mediaType": "text/html",
|
|
|
|
"href": "https://example.org/xyz",
|
|
|
|
}])),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
let content = get_note_content(&object).unwrap();
|
|
|
|
assert_eq!(
|
|
|
|
content,
|
2022-10-13 12:44:45 +00:00
|
|
|
r#"test-content<p><a href="https://example.org/xyz" rel="noopener">https://example.org/xyz</a></p>"#,
|
2022-07-28 14:09:57 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2022-02-13 18:55:37 +00:00
|
|
|
#[test]
|
|
|
|
fn test_get_note_visibility_public() {
|
|
|
|
let author = DbActorProfile::default();
|
|
|
|
let primary_audience = vec![AP_PUBLIC.to_string()];
|
|
|
|
let secondary_audience = vec![];
|
|
|
|
let visibility = get_note_visibility(
|
|
|
|
&author,
|
|
|
|
primary_audience,
|
|
|
|
secondary_audience,
|
|
|
|
);
|
|
|
|
assert_eq!(visibility, Visibility::Public);
|
|
|
|
}
|
|
|
|
|
2022-02-13 17:55:35 +00:00
|
|
|
#[test]
|
|
|
|
fn test_get_note_visibility_followers() {
|
|
|
|
let author_followers = "https://example.com/users/author/followers";
|
|
|
|
let author = DbActorProfile {
|
|
|
|
actor_json: Some(Actor {
|
|
|
|
followers: Some(author_followers.to_string()),
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
let primary_audience = vec![author_followers.to_string()];
|
|
|
|
let secondary_audience = vec![];
|
|
|
|
let visibility = get_note_visibility(
|
|
|
|
&author,
|
|
|
|
primary_audience,
|
|
|
|
secondary_audience,
|
|
|
|
);
|
|
|
|
assert_eq!(visibility, Visibility::Followers);
|
|
|
|
}
|
|
|
|
|
2022-06-14 23:41:01 +00:00
|
|
|
#[test]
|
|
|
|
fn test_get_note_visibility_subscribers() {
|
|
|
|
let author_followers = "https://example.com/users/author/followers";
|
|
|
|
let author_subscribers = "https://example.com/users/author/subscribers";
|
|
|
|
let author = DbActorProfile {
|
|
|
|
actor_json: Some(Actor {
|
|
|
|
followers: Some(author_followers.to_string()),
|
|
|
|
subscribers: Some(author_subscribers.to_string()),
|
|
|
|
..Default::default()
|
|
|
|
}),
|
|
|
|
..Default::default()
|
|
|
|
};
|
|
|
|
let primary_audience = vec![author_subscribers.to_string()];
|
|
|
|
let secondary_audience = vec![];
|
|
|
|
let visibility = get_note_visibility(
|
|
|
|
&author,
|
|
|
|
primary_audience,
|
|
|
|
secondary_audience,
|
|
|
|
);
|
|
|
|
assert_eq!(visibility, Visibility::Subscribers);
|
|
|
|
}
|
|
|
|
|
2022-02-13 18:55:37 +00:00
|
|
|
#[test]
|
|
|
|
fn test_get_note_visibility_direct() {
|
|
|
|
let author = DbActorProfile::default();
|
|
|
|
let primary_audience = vec!["https://example.com/users/1".to_string()];
|
|
|
|
let secondary_audience = vec![];
|
|
|
|
let visibility = get_note_visibility(
|
|
|
|
&author,
|
|
|
|
primary_audience,
|
|
|
|
secondary_audience,
|
|
|
|
);
|
|
|
|
assert_eq!(visibility, Visibility::Direct);
|
|
|
|
}
|
|
|
|
}
|