Implement FEP-e232 and allow to add quotes to posts

This commit is contained in:
silverpill 2022-10-01 11:08:56 +00:00
parent 5b7979b9d4
commit a685829472
6 changed files with 83 additions and 11 deletions

View file

@ -28,6 +28,10 @@ And these additional standards:
Activities are implemented in way that is compatible with Pleroma, Mastodon and other popular ActivityPub servers.
## Supported FEPs
- [FEP-e232](https://codeberg.org/fediverse/fep/src/branch/main/feps/fep-e232.md)
## Profile extensions
### Cryptocurrency addresses

View file

@ -35,6 +35,9 @@ pub struct Tag {
pub tag_type: String,
pub href: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub media_type: Option<String>,
}
#[derive(Default, Deserialize, Serialize)]

View file

@ -5,7 +5,7 @@ use tokio_postgres::GenericClient;
use crate::activitypub::{
activity::{create_activity, Activity, Attachment, Tag},
actors::types::Actor,
constants::{AP_CONTEXT, AP_PUBLIC},
constants::{AP_MEDIA_TYPE, AP_CONTEXT, AP_PUBLIC},
deliverer::OutgoingActivity,
identifiers::{
local_actor_id,
@ -13,7 +13,7 @@ use crate::activitypub::{
local_actor_subscribers,
local_object_id,
},
vocabulary::{CREATE, DOCUMENT, HASHTAG, MENTION, NOTE},
vocabulary::{CREATE, DOCUMENT, HASHTAG, LINK, MENTION, NOTE},
};
use crate::config::Instance;
use crate::errors::DatabaseError;
@ -103,6 +103,7 @@ pub fn build_note(
name: Some(tag_name),
tag_type: MENTION.to_string(),
href: Some(actor_id),
media_type: None,
};
tags.push(tag);
};
@ -112,6 +113,19 @@ pub fn build_note(
name: Some(format!("#{}", tag_name)),
tag_type: HASHTAG.to_string(),
href: Some(tag_page_url),
media_type: None,
};
tags.push(tag);
};
assert_eq!(post.links.len(), post.linked.len());
for linked in &post.linked {
// Build FEP-e232 object link
let link_href = linked.get_object_id(instance_url);
let tag = Tag {
name: Some(format!("RE: {}", link_href)),
tag_type: LINK.to_string(),
href: Some(link_href),
media_type: Some(AP_MEDIA_TYPE.to_string()),
};
tags.push(tag);
};

View file

@ -8,7 +8,7 @@ use uuid::Uuid;
use crate::activitypub::{
activity::{Attachment, Link, Object, Tag},
constants::AP_PUBLIC,
constants::{AP_MEDIA_TYPE, AP_PUBLIC, AS_MEDIA_TYPE},
fetcher::fetchers::fetch_file,
fetcher::helpers::{
get_or_import_profile_by_actor_id,
@ -17,7 +17,7 @@ use crate::activitypub::{
},
identifiers::{parse_local_actor_id, parse_local_object_id},
receiver::{parse_array, parse_property_value},
vocabulary::{DOCUMENT, HASHTAG, IMAGE, MENTION, NOTE},
vocabulary::{DOCUMENT, HASHTAG, IMAGE, LINK, MENTION, NOTE},
};
use crate::config::Instance;
use crate::errors::{ConversionError, DatabaseError, ValidationError};
@ -305,18 +305,33 @@ pub async fn handle_note(
} else {
log::warn!("failed to parse mention {}", tag_name);
};
} 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;
};
if let Some(href) = tag.href {
let linked_id = get_internal_post_id(
db_client,
&instance.url(),
&href,
redirects,
).await?;
links.push(linked_id);
};
};
};
};
if let Some(ref object_id) = object.quote_url {
log::warn!("link to object found: {}", object_id);
let quoted_id = get_internal_post_id(
let linked_id = get_internal_post_id(
db_client,
&instance.url(),
object_id,
redirects,
).await?;
links.push(quoted_id);
links.push(linked_id);
};
let in_reply_to_id = match object.in_reply_to {

View file

@ -146,6 +146,7 @@ pub struct StatusData {
// Not supported by Mastodon
pub mentions: Option<Vec<Uuid>>,
pub links: Option<Vec<Uuid>>,
}
impl TryFrom<StatusData> for PostCreateData {
@ -169,7 +170,7 @@ impl TryFrom<StatusData> for PostCreateData {
attachments: value.media_ids.unwrap_or(vec![]),
mentions: value.mentions.unwrap_or(vec![]),
tags: vec![],
links: vec![],
links: value.links.unwrap_or(vec![]),
object_id: None,
created_at: Utc::now(),
};

View file

@ -81,8 +81,6 @@ async fn create_status(
.into_iter().map(|profile| profile.id);
post_data.mentions.extend(subscribers);
};
post_data.mentions.sort();
post_data.mentions.dedup();
// Hashtags
post_data.tags = find_hashtags(&post_data.content);
post_data.content = replace_hashtags(
@ -90,6 +88,39 @@ async fn create_status(
&post_data.content,
&post_data.tags,
);
// Links
let linked = match &post_data.links[..] {
[] => vec![],
[linked_id] => {
if post_data.in_reply_to_id.is_some() {
return Err(ValidationError("can't add links to reply").into());
};
if post_data.visibility != Visibility::Public {
return Err(ValidationError("can't add links to non-public posts").into());
};
let linked = match get_post_by_id(db_client, linked_id).await {
Ok(post) => post,
Err(DatabaseError::NotFound(_)) => {
return Err(ValidationError("referenced post does't exist").into());
},
Err(other_error) => return Err(other_error.into()),
};
if linked.repost_of_id.is_some() {
return Err(ValidationError("can't reference repost").into());
};
if linked.visibility != Visibility::Public {
return Err(ValidationError("can't reference non-public post").into());
};
// Append inline quote and add author to mentions
post_data.content += &format!(
r#"<p class="inline-quote">RE: <a href="{0}">{0}</a></p>"#,
linked.get_object_id(&instance.url()),
);
post_data.mentions.push(linked.author.id);
vec![linked]
},
_ => return Err(ValidationError("too many links").into()),
};
// Reply validation
let maybe_in_reply_to = if let Some(in_reply_to_id) = post_data.in_reply_to_id.as_ref() {
let in_reply_to = match get_post_by_id(db_client, in_reply_to_id).await {
@ -118,14 +149,18 @@ async fn create_status(
} else {
None
};
// Remove duplicate mentions
post_data.mentions.sort();
post_data.mentions.dedup();
// Create post
let mut post = create_post(db_client, &current_user.id, post_data).await?;
post.in_reply_to = maybe_in_reply_to.map(|mut in_reply_to| {
in_reply_to.reply_count += 1;
Box::new(in_reply_to)
});
post.linked = linked;
// Federate
prepare_create_note(db_client, config.instance(), &current_user, &post).await?
prepare_create_note(db_client, instance.clone(), &current_user, &post).await?
.spawn_deliver();
let status = Status::from_post(post, &instance.url());