From b8d7f00d58c0c76c70436b2a070f19351d5ccbd5 Mon Sep 17 00:00:00 2001 From: Nutomic Date: Mon, 2 Aug 2021 20:33:40 +0000 Subject: [PATCH] Rewrite voting (#1685) * Merge like/dislike activity handlers into vote * Rewrite vote sending code * Remove old send_create, send_update functions --- crates/api/src/comment.rs | 37 +++-- crates/api/src/post.rs | 40 ++++-- crates/api_crud/src/comment/create.rs | 23 ++- crates/api_crud/src/post/create.rs | 20 ++- .../apub/src/activities/community/announce.rs | 13 +- crates/apub/src/activities/mod.rs | 1 - crates/apub/src/activities/send/comment.rs | 111 +-------------- crates/apub/src/activities/send/post.rs | 102 +------------- .../src/activities/send/private_message.rs | 16 --- crates/apub/src/activities/voting/dislike.rs | 54 ------- crates/apub/src/activities/voting/like.rs | 54 ------- crates/apub/src/activities/voting/mod.rs | 92 +++--------- .../src/activities/voting/undo_dislike.rs | 55 -------- .../apub/src/activities/voting/undo_like.rs | 55 -------- .../apub/src/activities/voting/undo_vote.rs | 122 ++++++++++++++++ crates/apub/src/activities/voting/vote.rs | 133 ++++++++++++++++++ crates/apub/src/http/inbox_enums.rs | 19 +-- crates/apub/src/lib.rs | 29 ++-- 18 files changed, 382 insertions(+), 594 deletions(-) delete mode 100644 crates/apub/src/activities/voting/dislike.rs delete mode 100644 crates/apub/src/activities/voting/like.rs delete mode 100644 crates/apub/src/activities/voting/undo_dislike.rs delete mode 100644 crates/apub/src/activities/voting/undo_like.rs create mode 100644 crates/apub/src/activities/voting/undo_vote.rs create mode 100644 crates/apub/src/activities/voting/vote.rs diff --git a/crates/api/src/comment.rs b/crates/api/src/comment.rs index ff1010fb1..f92e679f7 100644 --- a/crates/api/src/comment.rs +++ b/crates/api/src/comment.rs @@ -7,12 +7,19 @@ use lemmy_api_common::{ comment::*, get_local_user_view_from_jwt, }; -use lemmy_apub::ApubLikeableType; +use lemmy_apub::{ + activities::voting::{ + undo_vote::UndoVote, + vote::{Vote, VoteType}, + }, + PostOrComment, +}; use lemmy_db_queries::{source::comment::Comment_, Likeable, Saveable}; use lemmy_db_schema::{source::comment::*, LocalUserId}; use lemmy_db_views::{comment_view::CommentView, local_user_view::LocalUserView}; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; use lemmy_websocket::{messages::SendComment, LemmyContext, UserOperation}; +use std::convert::TryInto; #[async_trait::async_trait(?Send)] impl Perform for MarkCommentAsRead { @@ -170,6 +177,7 @@ impl Perform for CreateCommentLike { // Only add the like if the score isnt 0 let comment = orig_comment.comment; + let object = PostOrComment::Comment(Box::new(comment)); let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1); if do_add { let like_form2 = like_form.clone(); @@ -178,17 +186,24 @@ impl Perform for CreateCommentLike { return Err(ApiError::err("couldnt_like_comment").into()); } - if like_form.score == 1 { - comment.send_like(&local_user_view.person, context).await?; - } else if like_form.score == -1 { - comment - .send_dislike(&local_user_view.person, context) - .await?; - } + Vote::send( + &object, + &local_user_view.person, + orig_comment.community.id, + like_form.score.try_into()?, + context, + ) + .await?; } else { - comment - .send_undo_like(&local_user_view.person, context) - .await?; + // API doesn't distinguish between Undo/Like and Undo/Dislike + UndoVote::send( + &object, + &local_user_view.person, + orig_comment.community.id, + VoteType::Like, + context, + ) + .await?; } // Have to refetch the comment to get the current state diff --git a/crates/api/src/post.rs b/crates/api/src/post.rs index a3392f550..684f64798 100644 --- a/crates/api/src/post.rs +++ b/crates/api/src/post.rs @@ -10,14 +10,22 @@ use lemmy_api_common::{ post::*, }; use lemmy_apub::{ - activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType}, - ApubLikeableType, + activities::{ + post::create_or_update::CreateOrUpdatePost, + voting::{ + undo_vote::UndoVote, + vote::{Vote, VoteType}, + }, + CreateOrUpdateType, + }, + PostOrComment, }; use lemmy_db_queries::{source::post::Post_, Crud, Likeable, Saveable}; use lemmy_db_schema::source::{moderator::*, post::*}; use lemmy_db_views::post_view::PostView; use lemmy_utils::{ApiError, ConnectionId, LemmyError}; use lemmy_websocket::{messages::SendPost, LemmyContext, UserOperation}; +use std::convert::TryInto; #[async_trait::async_trait(?Send)] impl Perform for CreatePostLike { @@ -53,6 +61,9 @@ impl Perform for CreatePostLike { }) .await??; + let community_id = post.community_id; + let object = PostOrComment::Post(Box::new(post)); + // Only add the like if the score isnt 0 let do_add = like_form.score != 0 && (like_form.score == 1 || like_form.score == -1); if do_add { @@ -62,15 +73,24 @@ impl Perform for CreatePostLike { return Err(ApiError::err("couldnt_like_post").into()); } - if like_form.score == 1 { - post.send_like(&local_user_view.person, context).await?; - } else if like_form.score == -1 { - post.send_dislike(&local_user_view.person, context).await?; - } + Vote::send( + &object, + &local_user_view.person, + community_id, + like_form.score.try_into()?, + context, + ) + .await?; } else { - post - .send_undo_like(&local_user_view.person, context) - .await?; + // API doesn't distinguish between Undo/Like and Undo/Dislike + UndoVote::send( + &object, + &local_user_view.person, + community_id, + VoteType::Like, + context, + ) + .await?; } // Mark the post as read diff --git a/crates/api_crud/src/comment/create.rs b/crates/api_crud/src/comment/create.rs index 1b772cd84..5a6fec81c 100644 --- a/crates/api_crud/src/comment/create.rs +++ b/crates/api_crud/src/comment/create.rs @@ -9,10 +9,14 @@ use lemmy_api_common::{ send_local_notifs, }; use lemmy_apub::{ - activities::{comment::create_or_update::CreateOrUpdateComment, CreateOrUpdateType}, + activities::{ + comment::create_or_update::CreateOrUpdateComment, + voting::vote::{Vote, VoteType}, + CreateOrUpdateType, + }, generate_apub_endpoint, - ApubLikeableType, EndpointType, + PostOrComment, }; use lemmy_db_queries::{source::comment::Comment_, Crud, Likeable}; use lemmy_db_schema::source::comment::*; @@ -42,8 +46,9 @@ impl PerformCrud for CreateComment { // Check for a community ban let post_id = data.post_id; let post = get_post(post_id, context.pool()).await?; + let community_id = post.community_id; - check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?; + check_community_ban(local_user_view.person.id, community_id, context.pool()).await?; // Check if post is locked, no new comments if post.locked { @@ -122,9 +127,15 @@ impl PerformCrud for CreateComment { return Err(ApiError::err("couldnt_like_comment").into()); } - updated_comment - .send_like(&local_user_view.person, context) - .await?; + let object = PostOrComment::Comment(Box::new(updated_comment)); + Vote::send( + &object, + &local_user_view.person, + community_id, + VoteType::Like, + context, + ) + .await?; let person_id = local_user_view.person.id; let mut comment_view = blocking(context.pool(), move |conn| { diff --git a/crates/api_crud/src/post/create.rs b/crates/api_crud/src/post/create.rs index 813c62ce0..bbb720faf 100644 --- a/crates/api_crud/src/post/create.rs +++ b/crates/api_crud/src/post/create.rs @@ -8,10 +8,14 @@ use lemmy_api_common::{ post::*, }; use lemmy_apub::{ - activities::{post::create_or_update::CreateOrUpdatePost, CreateOrUpdateType}, + activities::{ + post::create_or_update::CreateOrUpdatePost, + voting::vote::{Vote, VoteType}, + CreateOrUpdateType, + }, generate_apub_endpoint, - ApubLikeableType, EndpointType, + PostOrComment, }; use lemmy_db_queries::{source::post::Post_, Crud, Likeable}; use lemmy_db_schema::source::post::*; @@ -112,9 +116,15 @@ impl PerformCrud for CreatePost { // Mark the post as read mark_post_as_read(person_id, post_id, context.pool()).await?; - updated_post - .send_like(&local_user_view.person, context) - .await?; + let object = PostOrComment::Post(Box::new(updated_post)); + Vote::send( + &object, + &local_user_view.person, + inserted_post.community_id, + VoteType::Like, + context, + ) + .await?; // Refetch the view let inserted_post_id = inserted_post.id; diff --git a/crates/apub/src/activities/community/announce.rs b/crates/apub/src/activities/community/announce.rs index b72cd1a5c..9cde8a43c 100644 --- a/crates/apub/src/activities/community/announce.rs +++ b/crates/apub/src/activities/community/announce.rs @@ -19,12 +19,7 @@ use crate::{ }, verify_activity, verify_community, - voting::{ - dislike::DislikePostOrComment, - like::LikePostOrComment, - undo_dislike::UndoDislikePostOrComment, - undo_like::UndoLikePostOrComment, - }, + voting::{undo_vote::UndoVote, vote::Vote}, }, activity_queue::send_activity_new, extensions::context::lemmy_context, @@ -46,10 +41,8 @@ use url::Url; pub enum AnnouncableActivities { CreateOrUpdateComment(CreateOrUpdateComment), CreateOrUpdatePost(Box), - LikePostOrComment(LikePostOrComment), - DislikePostOrComment(DislikePostOrComment), - UndoLikePostOrComment(UndoLikePostOrComment), - UndoDislikePostOrComment(UndoDislikePostOrComment), + Vote(Vote), + UndoVote(UndoVote), DeletePostCommentOrCommunity(DeletePostCommentOrCommunity), UndoDeletePostCommentOrCommunity(UndoDeletePostCommentOrCommunity), RemovePostCommentCommunityOrMod(RemovePostCommentCommunityOrMod), diff --git a/crates/apub/src/activities/mod.rs b/crates/apub/src/activities/mod.rs index f4c535ee6..afbad6c8b 100644 --- a/crates/apub/src/activities/mod.rs +++ b/crates/apub/src/activities/mod.rs @@ -31,7 +31,6 @@ pub mod send; pub mod voting; #[derive(Clone, Debug, ToString, Deserialize, Serialize)] -#[serde(rename_all = "camelCase")] pub enum CreateOrUpdateType { Create, Update, diff --git a/crates/apub/src/activities/send/comment.rs b/crates/apub/src/activities/send/comment.rs index 619d59b1b..8b446860a 100644 --- a/crates/apub/src/activities/send/comment.rs +++ b/crates/apub/src/activities/send/comment.rs @@ -3,15 +3,12 @@ use crate::{ activity_queue::send_to_community, extensions::context::lemmy_context, ActorType, - ApubLikeableType, ApubObjectType, }; use activitystreams::{ activity::{ - kind::{DeleteType, DislikeType, LikeType, RemoveType, UndoType}, + kind::{DeleteType, RemoveType, UndoType}, Delete, - Dislike, - Like, Remove, Undo, }, @@ -26,22 +23,6 @@ use lemmy_websocket::LemmyContext; #[async_trait::async_trait(?Send)] impl ApubObjectType for Comment { - async fn send_create( - &self, - _creator: &Person, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn send_update( - &self, - _creator: &Person, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> { let post_id = self.post_id; let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; @@ -170,93 +151,3 @@ impl ApubObjectType for Comment { Ok(()) } } - -#[async_trait::async_trait(?Send)] -impl ApubLikeableType for Comment { - async fn send_like(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> { - let post_id = self.post_id; - let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; - - let community_id = post.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut like = Like::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - like - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(LikeType::Like)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(like, creator, &community, None, context).await?; - Ok(()) - } - - async fn send_dislike(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> { - let post_id = self.post_id; - let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; - - let community_id = post.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut dislike = Dislike::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - dislike - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DislikeType::Dislike)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(dislike, creator, &community, None, context).await?; - Ok(()) - } - - async fn send_undo_like( - &self, - creator: &Person, - context: &LemmyContext, - ) -> Result<(), LemmyError> { - let post_id = self.post_id; - let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??; - - let community_id = post.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut like = Like::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - like - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DislikeType::Dislike)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - // Undo that fake activity - let mut undo = Undo::new( - creator.actor_id.to_owned().into_inner(), - like.into_any_base()?, - ); - undo - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(UndoType::Undo)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(undo, creator, &community, None, context).await?; - Ok(()) - } -} diff --git a/crates/apub/src/activities/send/post.rs b/crates/apub/src/activities/send/post.rs index 677e7845b..b9713661b 100644 --- a/crates/apub/src/activities/send/post.rs +++ b/crates/apub/src/activities/send/post.rs @@ -3,15 +3,12 @@ use crate::{ activity_queue::send_to_community, extensions::context::lemmy_context, ActorType, - ApubLikeableType, ApubObjectType, }; use activitystreams::{ activity::{ - kind::{DeleteType, DislikeType, LikeType, RemoveType, UndoType}, + kind::{DeleteType, RemoveType, UndoType}, Delete, - Dislike, - Like, Remove, Undo, }, @@ -26,22 +23,6 @@ use lemmy_websocket::LemmyContext; #[async_trait::async_trait(?Send)] impl ApubObjectType for Post { - async fn send_create( - &self, - _creator: &Person, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn send_update( - &self, - _creator: &Person, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> { let community_id = self.community_id; let community = blocking(context.pool(), move |conn| { @@ -156,84 +137,3 @@ impl ApubObjectType for Post { Ok(()) } } - -#[async_trait::async_trait(?Send)] -impl ApubLikeableType for Post { - async fn send_like(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> { - let community_id = self.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut like = Like::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - like - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(LikeType::Like)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(like, creator, &community, None, context).await?; - Ok(()) - } - - async fn send_dislike(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> { - let community_id = self.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut dislike = Dislike::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - dislike - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(DislikeType::Dislike)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(dislike, creator, &community, None, context).await?; - Ok(()) - } - - async fn send_undo_like( - &self, - creator: &Person, - context: &LemmyContext, - ) -> Result<(), LemmyError> { - let community_id = self.community_id; - let community = blocking(context.pool(), move |conn| { - Community::read(conn, community_id) - }) - .await??; - - let mut like = Like::new( - creator.actor_id.to_owned().into_inner(), - self.ap_id.to_owned().into_inner(), - ); - like - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(LikeType::Like)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - // Undo that fake activity - let mut undo = Undo::new( - creator.actor_id.to_owned().into_inner(), - like.into_any_base()?, - ); - undo - .set_many_contexts(lemmy_context()) - .set_id(generate_activity_id(UndoType::Undo)?) - .set_to(public()) - .set_many_ccs(vec![community.actor_id()]); - - send_to_community(undo, creator, &community, None, context).await?; - Ok(()) - } -} diff --git a/crates/apub/src/activities/send/private_message.rs b/crates/apub/src/activities/send/private_message.rs index 7461926c1..edcff3771 100644 --- a/crates/apub/src/activities/send/private_message.rs +++ b/crates/apub/src/activities/send/private_message.rs @@ -22,22 +22,6 @@ use lemmy_websocket::LemmyContext; #[async_trait::async_trait(?Send)] impl ApubObjectType for PrivateMessage { - async fn send_create( - &self, - _creator: &Person, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - - async fn send_update( - &self, - _creator: &Person, - _context: &LemmyContext, - ) -> Result<(), LemmyError> { - unimplemented!() - } - async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> { let recipient_id = self.recipient_id; let recipient = diff --git a/crates/apub/src/activities/voting/dislike.rs b/crates/apub/src/activities/voting/dislike.rs deleted file mode 100644 index 54b673008..000000000 --- a/crates/apub/src/activities/voting/dislike.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::activities::{ - verify_activity, - verify_person_in_community, - voting::receive_like_or_dislike, -}; -use activitystreams::activity::kind::DislikeType; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use url::Url; - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct DislikePostOrComment { - to: PublicUrl, - pub(in crate::activities) object: Url, - cc: [Url; 1], - #[serde(rename = "type")] - kind: DislikeType, - #[serde(flatten)] - common: ActivityCommonFields, -} - -#[async_trait::async_trait(?Send)] -impl ActivityHandler for DislikePostOrComment { - async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - Ok(()) - } - - async fn receive( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - receive_like_or_dislike( - -1, - &self.common.actor, - &self.object, - context, - request_counter, - ) - .await - } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } -} diff --git a/crates/apub/src/activities/voting/like.rs b/crates/apub/src/activities/voting/like.rs deleted file mode 100644 index 90f29c427..000000000 --- a/crates/apub/src/activities/voting/like.rs +++ /dev/null @@ -1,54 +0,0 @@ -use crate::activities::{ - verify_activity, - verify_person_in_community, - voting::receive_like_or_dislike, -}; -use activitystreams::activity::kind::LikeType; -use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use url::Url; - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct LikePostOrComment { - to: PublicUrl, - pub(in crate::activities::voting) object: Url, - cc: [Url; 1], - #[serde(rename = "type")] - kind: LikeType, - #[serde(flatten)] - common: ActivityCommonFields, -} - -#[async_trait::async_trait(?Send)] -impl ActivityHandler for LikePostOrComment { - async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - Ok(()) - } - - async fn receive( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - receive_like_or_dislike( - 1, - &self.common.actor, - &self.object, - context, - request_counter, - ) - .await - } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } -} diff --git a/crates/apub/src/activities/voting/mod.rs b/crates/apub/src/activities/voting/mod.rs index 739c43051..48d9446f8 100644 --- a/crates/apub/src/activities/voting/mod.rs +++ b/crates/apub/src/activities/voting/mod.rs @@ -1,62 +1,33 @@ -use crate::{ - activities::{ - comment::send_websocket_message as send_comment_message, - post::send_websocket_message as send_post_message, - }, - fetcher::{ - objects::get_or_fetch_and_insert_post_or_comment, - person::get_or_fetch_and_upsert_person, - }, - PostOrComment, +use crate::activities::{ + comment::send_websocket_message as send_comment_message, + post::send_websocket_message as send_post_message, + voting::vote::VoteType, }; use lemmy_api_common::blocking; use lemmy_db_queries::Likeable; use lemmy_db_schema::source::{ comment::{Comment, CommentLike, CommentLikeForm}, + person::Person, post::{Post, PostLike, PostLikeForm}, }; use lemmy_utils::LemmyError; use lemmy_websocket::{LemmyContext, UserOperation}; -use std::ops::Deref; -use url::Url; -pub mod dislike; -pub mod like; -pub mod undo_dislike; -pub mod undo_like; +pub mod undo_vote; +pub mod vote; -pub(in crate::activities::voting) async fn receive_like_or_dislike( - score: i16, - actor: &Url, - object: &Url, - context: &LemmyContext, - request_counter: &mut i32, -) -> Result<(), LemmyError> { - match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? { - PostOrComment::Post(p) => { - like_or_dislike_post(score, actor, p.deref(), context, request_counter).await - } - PostOrComment::Comment(c) => { - like_or_dislike_comment(score, actor, c.deref(), context, request_counter).await - } - } -} - -async fn like_or_dislike_comment( - score: i16, - actor: &Url, +async fn vote_comment( + vote_type: &VoteType, + actor: Person, comment: &Comment, context: &LemmyContext, - request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; - let comment_id = comment.id; let like_form = CommentLikeForm { comment_id, post_id: comment.post_id, person_id: actor.id, - score, + score: vote_type.into(), }; let person_id = actor.id; blocking(context.pool(), move |conn| { @@ -74,20 +45,17 @@ async fn like_or_dislike_comment( .await } -async fn like_or_dislike_post( - score: i16, - actor: &Url, +async fn vote_post( + vote_type: &VoteType, + actor: Person, post: &Post, context: &LemmyContext, - request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; - let post_id = post.id; let like_form = PostLikeForm { post_id: post.id, person_id: actor.id, - score, + score: vote_type.into(), }; let person_id = actor.id; blocking(context.pool(), move |conn| { @@ -99,30 +67,11 @@ async fn like_or_dislike_post( send_post_message(post.id, UserOperation::CreatePostLike, context).await } -pub(in crate::activities::voting) async fn receive_undo_like_or_dislike( - actor: &Url, - object: &Url, - context: &LemmyContext, - request_counter: &mut i32, -) -> Result<(), LemmyError> { - match get_or_fetch_and_insert_post_or_comment(object, context, request_counter).await? { - PostOrComment::Post(p) => { - undo_like_or_dislike_post(actor, p.deref(), context, request_counter).await - } - PostOrComment::Comment(c) => { - undo_like_or_dislike_comment(actor, c.deref(), context, request_counter).await - } - } -} - -async fn undo_like_or_dislike_comment( - actor: &Url, +async fn undo_vote_comment( + actor: Person, comment: &Comment, context: &LemmyContext, - request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; - let comment_id = comment.id; let person_id = actor.id; blocking(context.pool(), move |conn| { @@ -139,14 +88,11 @@ async fn undo_like_or_dislike_comment( .await } -async fn undo_like_or_dislike_post( - actor: &Url, +async fn undo_vote_post( + actor: Person, post: &Post, context: &LemmyContext, - request_counter: &mut i32, ) -> Result<(), LemmyError> { - let actor = get_or_fetch_and_upsert_person(actor, context, request_counter).await?; - let post_id = post.id; let person_id = actor.id; blocking(context.pool(), move |conn| { diff --git a/crates/apub/src/activities/voting/undo_dislike.rs b/crates/apub/src/activities/voting/undo_dislike.rs deleted file mode 100644 index 13e3e1f76..000000000 --- a/crates/apub/src/activities/voting/undo_dislike.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::activities::{ - verify_activity, - verify_person_in_community, - voting::{dislike::DislikePostOrComment, receive_undo_like_or_dislike}, -}; -use activitystreams::activity::kind::UndoType; -use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use url::Url; - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct UndoDislikePostOrComment { - to: PublicUrl, - object: DislikePostOrComment, - cc: [Url; 1], - #[serde(rename = "type")] - kind: UndoType, - #[serde(flatten)] - common: ActivityCommonFields, -} - -#[async_trait::async_trait(?Send)] -impl ActivityHandler for UndoDislikePostOrComment { - async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_urls_match(&self.common.actor, &self.object.common().actor)?; - self.object.verify(context, request_counter).await?; - Ok(()) - } - - async fn receive( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - receive_undo_like_or_dislike( - &self.common.actor, - &self.object.object, - context, - request_counter, - ) - .await - } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } -} diff --git a/crates/apub/src/activities/voting/undo_like.rs b/crates/apub/src/activities/voting/undo_like.rs deleted file mode 100644 index ab15da70b..000000000 --- a/crates/apub/src/activities/voting/undo_like.rs +++ /dev/null @@ -1,55 +0,0 @@ -use crate::activities::{ - verify_activity, - verify_person_in_community, - voting::{like::LikePostOrComment, receive_undo_like_or_dislike}, -}; -use activitystreams::activity::kind::UndoType; -use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler}; -use lemmy_utils::LemmyError; -use lemmy_websocket::LemmyContext; -use url::Url; - -#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] -#[serde(rename_all = "camelCase")] -pub struct UndoLikePostOrComment { - to: PublicUrl, - object: LikePostOrComment, - cc: [Url; 1], - #[serde(rename = "type")] - kind: UndoType, - #[serde(flatten)] - common: ActivityCommonFields, -} - -#[async_trait::async_trait(?Send)] -impl ActivityHandler for UndoLikePostOrComment { - async fn verify( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - verify_activity(self.common())?; - verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; - verify_urls_match(&self.common.actor, &self.object.common().actor)?; - self.object.verify(context, request_counter).await?; - Ok(()) - } - - async fn receive( - &self, - context: &LemmyContext, - request_counter: &mut i32, - ) -> Result<(), LemmyError> { - receive_undo_like_or_dislike( - &self.common.actor, - &self.object.object, - context, - request_counter, - ) - .await - } - - fn common(&self) -> &ActivityCommonFields { - &self.common - } -} diff --git a/crates/apub/src/activities/voting/undo_vote.rs b/crates/apub/src/activities/voting/undo_vote.rs new file mode 100644 index 000000000..8c3137e87 --- /dev/null +++ b/crates/apub/src/activities/voting/undo_vote.rs @@ -0,0 +1,122 @@ +use crate::{ + activities::{ + community::announce::AnnouncableActivities, + generate_activity_id, + verify_activity, + verify_person_in_community, + voting::{ + undo_vote_comment, + undo_vote_post, + vote::{Vote, VoteType}, + }, + }, + activity_queue::send_to_community_new, + extensions::context::lemmy_context, + fetcher::{ + objects::get_or_fetch_and_insert_post_or_comment, + person::get_or_fetch_and_upsert_person, + }, + ActorType, + PostOrComment, +}; +use activitystreams::activity::kind::UndoType; +use lemmy_api_common::blocking; +use lemmy_apub_lib::{values::PublicUrl, verify_urls_match, ActivityCommonFields, ActivityHandler}; +use lemmy_db_queries::Crud; +use lemmy_db_schema::{ + source::{community::Community, person::Person}, + CommunityId, +}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use std::ops::Deref; +use url::Url; + +#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)] +#[serde(rename_all = "camelCase")] +pub struct UndoVote { + to: PublicUrl, + object: Vote, + cc: [Url; 1], + #[serde(rename = "type")] + kind: UndoType, + #[serde(flatten)] + common: ActivityCommonFields, +} + +impl UndoVote { + pub async fn send( + object: &PostOrComment, + actor: &Person, + community_id: CommunityId, + kind: VoteType, + context: &LemmyContext, + ) -> Result<(), LemmyError> { + let community = blocking(context.pool(), move |conn| { + Community::read(conn, community_id) + }) + .await??; + let id = generate_activity_id(UndoType::Undo)?; + + let undo_vote = UndoVote { + to: PublicUrl::Public, + object: Vote { + to: PublicUrl::Public, + object: object.ap_id(), + cc: [community.actor_id()], + kind: kind.clone(), + common: ActivityCommonFields { + context: lemmy_context(), + id: generate_activity_id(kind)?, + actor: actor.actor_id(), + unparsed: Default::default(), + }, + }, + cc: [community.actor_id()], + kind: UndoType::Undo, + common: ActivityCommonFields { + context: lemmy_context(), + id: id.clone(), + actor: actor.actor_id(), + unparsed: Default::default(), + }, + }; + let activity = AnnouncableActivities::UndoVote(undo_vote); + send_to_community_new(activity, &id, actor, &community, vec![], context).await + } +} + +#[async_trait::async_trait(?Send)] +impl ActivityHandler for UndoVote { + async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_activity(self.common())?; + verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; + verify_urls_match(&self.common.actor, &self.object.common().actor)?; + self.object.verify(context, request_counter).await?; + Ok(()) + } + + async fn receive( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + let actor = + get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; + let object = + get_or_fetch_and_insert_post_or_comment(&self.object.object, context, request_counter) + .await?; + match object { + PostOrComment::Post(p) => undo_vote_post(actor, p.deref(), context).await, + PostOrComment::Comment(c) => undo_vote_comment(actor, c.deref(), context).await, + } + } + + fn common(&self) -> &ActivityCommonFields { + &self.common + } +} diff --git a/crates/apub/src/activities/voting/vote.rs b/crates/apub/src/activities/voting/vote.rs new file mode 100644 index 000000000..0f5a2b5c4 --- /dev/null +++ b/crates/apub/src/activities/voting/vote.rs @@ -0,0 +1,133 @@ +use crate::{ + activities::{ + community::announce::AnnouncableActivities, + generate_activity_id, + verify_activity, + verify_person_in_community, + voting::{vote_comment, vote_post}, + }, + activity_queue::send_to_community_new, + extensions::context::lemmy_context, + fetcher::{ + objects::get_or_fetch_and_insert_post_or_comment, + person::get_or_fetch_and_upsert_person, + }, + ActorType, + PostOrComment, +}; +use anyhow::anyhow; +use lemmy_api_common::blocking; +use lemmy_apub_lib::{values::PublicUrl, ActivityCommonFields, ActivityHandler}; +use lemmy_db_queries::Crud; +use lemmy_db_schema::{ + source::{community::Community, person::Person}, + CommunityId, +}; +use lemmy_utils::LemmyError; +use lemmy_websocket::LemmyContext; +use serde::{Deserialize, Serialize}; +use std::{convert::TryFrom, ops::Deref}; +use strum_macros::ToString; +use url::Url; + +#[derive(Clone, Debug, ToString, Deserialize, Serialize)] +pub enum VoteType { + Like, + Dislike, +} + +impl TryFrom for VoteType { + type Error = LemmyError; + + fn try_from(value: i16) -> Result { + match value { + 1 => Ok(VoteType::Like), + -1 => Ok(VoteType::Dislike), + _ => Err(anyhow!("invalid vote value").into()), + } + } +} + +impl From<&VoteType> for i16 { + fn from(value: &VoteType) -> i16 { + match value { + VoteType::Like => 1, + VoteType::Dislike => -1, + } + } +} + +#[derive(Clone, Debug, Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct Vote { + pub(in crate::activities::voting) to: PublicUrl, + pub(in crate::activities::voting) object: Url, + pub(in crate::activities::voting) cc: [Url; 1], + #[serde(rename = "type")] + pub(in crate::activities::voting) kind: VoteType, + #[serde(flatten)] + pub(in crate::activities::voting) common: ActivityCommonFields, +} + +impl Vote { + pub async fn send( + object: &PostOrComment, + actor: &Person, + community_id: CommunityId, + kind: VoteType, + context: &LemmyContext, + ) -> Result<(), LemmyError> { + let community = blocking(context.pool(), move |conn| { + Community::read(conn, community_id) + }) + .await??; + let id = generate_activity_id(kind.clone())?; + + let vote = Vote { + to: PublicUrl::Public, + object: object.ap_id(), + cc: [community.actor_id()], + kind, + common: ActivityCommonFields { + context: lemmy_context(), + id: id.clone(), + actor: actor.actor_id(), + unparsed: Default::default(), + }, + }; + let activity = AnnouncableActivities::Vote(vote); + send_to_community_new(activity, &id, actor, &community, vec![], context).await + } +} + +#[async_trait::async_trait(?Send)] +impl ActivityHandler for Vote { + async fn verify( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + verify_activity(self.common())?; + verify_person_in_community(&self.common.actor, &self.cc[0], context, request_counter).await?; + Ok(()) + } + + async fn receive( + &self, + context: &LemmyContext, + request_counter: &mut i32, + ) -> Result<(), LemmyError> { + let actor = + get_or_fetch_and_upsert_person(&self.common.actor, context, request_counter).await?; + let object = + get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?; + match object { + PostOrComment::Post(p) => vote_post(&self.kind, actor, p.deref(), context).await, + PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await, + } + } + + fn common(&self) -> &ActivityCommonFields { + &self.common + } +} diff --git a/crates/apub/src/http/inbox_enums.rs b/crates/apub/src/http/inbox_enums.rs index f8ee0cb21..034048777 100644 --- a/crates/apub/src/http/inbox_enums.rs +++ b/crates/apub/src/http/inbox_enums.rs @@ -19,12 +19,7 @@ use crate::activities::{ remove::RemovePostCommentCommunityOrMod, undo_remove::UndoRemovePostCommentOrCommunity, }, - voting::{ - dislike::DislikePostOrComment, - like::LikePostOrComment, - undo_dislike::UndoDislikePostOrComment, - undo_like::UndoLikePostOrComment, - }, + voting::{undo_vote::UndoVote, vote::Vote}, }; use lemmy_apub_lib::{ActivityCommonFields, ActivityHandler}; use lemmy_utils::LemmyError; @@ -48,10 +43,8 @@ pub enum GroupInboxActivities { UndoFollowCommunity(UndoFollowCommunity), CreateOrUpdateComment(CreateOrUpdateComment), CreateOrUpdatePost(Box), - LikePostOrComment(LikePostOrComment), - DislikePostOrComment(DislikePostOrComment), - UndoLikePostOrComment(UndoLikePostOrComment), - UndoDislikePostOrComment(UndoDislikePostOrComment), + Vote(Vote), + UndoVote(UndoVote), DeletePostCommentOrCommunity(DeletePostCommentOrCommunity), UndoDeletePostCommentOrCommunity(UndoDeletePostCommentOrCommunity), RemovePostCommentOrCommunity(RemovePostCommentCommunityOrMod), @@ -70,10 +63,8 @@ pub enum SharedInboxActivities { UndoFollowCommunity(UndoFollowCommunity), CreateOrUpdateComment(CreateOrUpdateComment), CreateOrUpdatePost(Box), - LikePostOrComment(LikePostOrComment), - DislikePostOrComment(DislikePostOrComment), - UndoDislikePostOrComment(UndoDislikePostOrComment), - UndoLikePostOrComment(UndoLikePostOrComment), + Vote(Vote), + UndoVote(UndoVote), DeletePostCommentOrCommunity(DeletePostCommentOrCommunity), UndoDeletePostCommentOrCommunity(UndoDeletePostCommentOrCommunity), RemovePostCommentOrCommunity(RemovePostCommentCommunityOrMod), diff --git a/crates/apub/src/lib.rs b/crates/apub/src/lib.rs index 16773aae5..191230579 100644 --- a/crates/apub/src/lib.rs +++ b/crates/apub/src/lib.rs @@ -132,10 +132,6 @@ pub fn check_is_apub_id_valid(apub_id: &Url, use_strict_allowlist: bool) -> Resu /// and actors in Lemmy. #[async_trait::async_trait(?Send)] pub trait ApubObjectType { - async fn send_create(&self, creator: &DbPerson, context: &LemmyContext) - -> Result<(), LemmyError>; - async fn send_update(&self, creator: &DbPerson, context: &LemmyContext) - -> Result<(), LemmyError>; async fn send_delete(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>; async fn send_undo_delete( @@ -151,21 +147,6 @@ pub trait ApubObjectType { ) -> Result<(), LemmyError>; } -#[async_trait::async_trait(?Send)] -pub trait ApubLikeableType { - async fn send_like(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>; - async fn send_dislike( - &self, - creator: &DbPerson, - context: &LemmyContext, - ) -> Result<(), LemmyError>; - async fn send_undo_like( - &self, - creator: &DbPerson, - context: &LemmyContext, - ) -> Result<(), LemmyError>; -} - /// Common methods provided by ActivityPub actors (community and person). Not all methods are /// implemented by all actors. pub trait ActorType { @@ -376,6 +357,16 @@ pub enum PostOrComment { Post(Box), } +impl PostOrComment { + pub(crate) fn ap_id(&self) -> Url { + match self { + PostOrComment::Post(p) => p.ap_id.clone(), + PostOrComment::Comment(c) => c.ap_id.clone(), + } + .into() + } +} + /// Tries to find a post or comment in the local database, without any network requests. /// This is used to handle deletions and removals, because in case we dont have the object, we can /// simply ignore the activity.