From d41d85548dc29204ace3e7d1f024086e293d0754 Mon Sep 17 00:00:00 2001 From: silverpill Date: Fri, 29 Oct 2021 19:21:26 +0000 Subject: [PATCH] Send and receive Like activities --- src/activitypub/activity.rs | 21 ++++++++++++++++ src/activitypub/receiver.rs | 28 +++++++++++++++++++-- src/activitypub/vocabulary.rs | 1 + src/mastodon_api/statuses/views.rs | 40 ++++++++++++++++++++++++++---- 4 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/activitypub/activity.rs b/src/activitypub/activity.rs index 4837ed0..7593a31 100644 --- a/src/activitypub/activity.rs +++ b/src/activitypub/activity.rs @@ -163,6 +163,27 @@ pub fn create_activity_note( activity } +pub fn create_activity_like( + instance_url: &str, + actor_profile: &DbActorProfile, + object_id: &str, +) -> Activity { + let object = Object { + context: Some(json!(AP_CONTEXT)), + id: object_id.to_string(), + object_type: NOTE.to_string(), + ..Default::default() + }; + let activity = create_activity( + instance_url, + &actor_profile.username, + LIKE, + None, + serde_json::to_value(object).unwrap(), + ); + activity +} + pub fn create_activity_follow( instance_url: &str, actor_profile: &DbActorProfile, diff --git a/src/activitypub/receiver.rs b/src/activitypub/receiver.rs index e977acd..268e7cc 100644 --- a/src/activitypub/receiver.rs +++ b/src/activitypub/receiver.rs @@ -22,6 +22,7 @@ use crate::models::profiles::queries::{ update_profile, }; use crate::models::profiles::types::{DbActorProfile, ProfileUpdateData}; +use crate::models::reactions::queries::create_reaction; use crate::models::relationships::queries::{ follow_request_accepted, follow_request_rejected, @@ -216,6 +217,23 @@ pub async fn receive_activity( create_post(db_client, &author.id, post_data).await?; } }, + (LIKE, _) => { + let author = get_or_fetch_profile_by_actor_id( + db_client, + &activity.actor, + &config.media_dir(), + ).await?; + let object_id = match activity.object.as_str() { + Some(object_id) => object_id.to_owned(), + None => { + let object: Object = serde_json::from_value(activity.object) + .map_err(|_| ValidationError("invalid object"))?; + object.id + }, + }; + let post_id = parse_object_id(&config.instance_url(), &object_id)?; + create_reaction(db_client, &author.id, &post_id).await?; + }, (FOLLOW, _) => { let source_profile = get_or_fetch_profile_by_actor_id( db_client, @@ -225,8 +243,14 @@ pub async fn receive_activity( let source_actor_value = source_profile.actor_json.ok_or(HttpError::InternalError)?; let source_actor: Actor = serde_json::from_value(source_actor_value) .map_err(|_| HttpError::InternalError)?; - let target_actor_id = activity.object.as_str() - .ok_or(ValidationError("invalid object"))?; + let target_actor_id = match activity.object.as_str() { + Some(object_id) => object_id.to_owned(), + None => { + let object: Object = serde_json::from_value(activity.object) + .map_err(|_| ValidationError("invalid object"))?; + object.id + }, + }; let target_username = parse_actor_id(&config.instance_url(), &target_actor_id)?; let target_profile = get_profile_by_acct(db_client, &target_username).await?; // Create and send 'Accept' activity diff --git a/src/activitypub/vocabulary.rs b/src/activitypub/vocabulary.rs index 13547b1..b827c41 100644 --- a/src/activitypub/vocabulary.rs +++ b/src/activitypub/vocabulary.rs @@ -2,6 +2,7 @@ pub const ACCEPT: &str = "Accept"; pub const CREATE: &str = "Create"; pub const FOLLOW: &str = "Follow"; +pub const LIKE: &str = "Like"; pub const REJECT: &str = "Reject"; pub const UNDO: &str = "Undo"; pub const UPDATE: &str = "Update"; diff --git a/src/mastodon_api/statuses/views.rs b/src/mastodon_api/statuses/views.rs index 24ff3ca..a71e53d 100644 --- a/src/mastodon_api/statuses/views.rs +++ b/src/mastodon_api/statuses/views.rs @@ -4,7 +4,10 @@ use actix_web_httpauth::extractors::bearer::BearerAuth; use serde::Serialize; use uuid::Uuid; -use crate::activitypub::activity::create_activity_note; +use crate::activitypub::activity::{ + create_activity_note, + create_activity_like, +}; use crate::activitypub::actor::Actor; use crate::activitypub::deliverer::deliver_activity; use crate::config::Config; @@ -137,12 +140,39 @@ async fn favourite( ) -> Result { let db_client = &mut **get_database_client(&db_pool).await?; let current_user = get_current_user(db_client, auth.token()).await?; - match create_reaction(db_client, ¤t_user.id, &status_id).await { - Err(DatabaseError::AlreadyExists(_)) => (), // post already favourited - other_result => other_result?, - } + let reaction_created = match create_reaction( + db_client, ¤t_user.id, &status_id, + ).await { + Ok(_) => true, + Err(DatabaseError::AlreadyExists(_)) => false, // post already favourited + Err(other_error) => return Err(other_error.into()), + }; let mut post = get_post_by_id(db_client, &status_id).await?; get_actions_for_post(db_client, ¤t_user.id, &mut post).await?; + + if reaction_created { + if let Some(actor_value) = &post.author.actor_json { + // Federate + let object_id = post.object_id.as_ref().ok_or(HttpError::InternalError)?; + let activity = create_activity_like( + &config.instance_url(), + ¤t_user.profile, + &object_id, + ); + let recipient: Actor = serde_json::from_value(actor_value.clone()) + .map_err(|_| HttpError::InternalError)?; + let config_clone = config.clone(); + actix_rt::spawn(async move { + deliver_activity( + &config_clone, + ¤t_user, + activity, + vec![recipient], + ).await; + }); + } + } + let status = Status::from_post(post, &config.instance_url()); Ok(HttpResponse::Ok().json(status)) }