]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/voting/mod.rs
8bae05577ef36645e62444894967a8a87b86c355
[lemmy.git] / crates / apub / src / activities / voting / mod.rs
1 use crate::{
2   activities::community::send_activity_in_community,
3   activity_lists::AnnouncableActivities,
4   fetcher::post_or_comment::PostOrComment,
5   objects::{comment::ApubComment, person::ApubPerson, post::ApubPost},
6   protocol::activities::voting::{
7     undo_vote::UndoVote,
8     vote::{Vote, VoteType},
9   },
10   SendActivity,
11 };
12 use activitypub_federation::{config::Data, fetch::object_id::ObjectId};
13 use lemmy_api_common::{
14   comment::{CommentResponse, CreateCommentLike},
15   context::LemmyContext,
16   post::{CreatePostLike, PostResponse},
17   sensitive::Sensitive,
18   utils::local_user_view_from_jwt,
19 };
20 use lemmy_db_schema::{
21   newtypes::CommunityId,
22   source::{
23     comment::{CommentLike, CommentLikeForm},
24     community::Community,
25     person::Person,
26     post::{PostLike, PostLikeForm},
27   },
28   traits::{Crud, Likeable},
29 };
30 use lemmy_utils::error::LemmyError;
31
32 pub mod undo_vote;
33 pub mod vote;
34
35 #[async_trait::async_trait]
36 impl SendActivity for CreatePostLike {
37   type Response = PostResponse;
38
39   async fn send_activity(
40     request: &Self,
41     response: &Self::Response,
42     context: &Data<LemmyContext>,
43   ) -> Result<(), LemmyError> {
44     let object_id = ObjectId::from(response.post_view.post.ap_id.clone());
45     let community_id = response.post_view.community.id;
46     send_activity(
47       object_id,
48       community_id,
49       request.score,
50       &request.auth,
51       context,
52     )
53     .await
54   }
55 }
56
57 #[async_trait::async_trait]
58 impl SendActivity for CreateCommentLike {
59   type Response = CommentResponse;
60
61   async fn send_activity(
62     request: &Self,
63     response: &Self::Response,
64     context: &Data<LemmyContext>,
65   ) -> Result<(), LemmyError> {
66     let object_id = ObjectId::from(response.comment_view.comment.ap_id.clone());
67     let community_id = response.comment_view.community.id;
68     send_activity(
69       object_id,
70       community_id,
71       request.score,
72       &request.auth,
73       context,
74     )
75     .await
76   }
77 }
78
79 async fn send_activity(
80   object_id: ObjectId<PostOrComment>,
81   community_id: CommunityId,
82   score: i16,
83   jwt: &Sensitive<String>,
84   context: &Data<LemmyContext>,
85 ) -> Result<(), LemmyError> {
86   let community = Community::read(context.pool(), community_id).await?.into();
87   let local_user_view = local_user_view_from_jwt(jwt, context).await?;
88   let actor = Person::read(context.pool(), local_user_view.person.id)
89     .await?
90     .into();
91
92   // score of 1 means upvote, -1 downvote, 0 undo a previous vote
93   if score != 0 {
94     let vote = Vote::new(object_id, &actor, &community, score.try_into()?, context)?;
95     let activity = AnnouncableActivities::Vote(vote);
96     send_activity_in_community(activity, &actor, &community, vec![], false, context).await
97   } else {
98     // Lemmy API doesnt distinguish between Undo/Like and Undo/Dislike, so we hardcode it here.
99     let vote = Vote::new(object_id, &actor, &community, VoteType::Like, context)?;
100     let undo_vote = UndoVote::new(vote, &actor, &community, context)?;
101     let activity = AnnouncableActivities::UndoVote(undo_vote);
102     send_activity_in_community(activity, &actor, &community, vec![], false, context).await
103   }
104 }
105
106 #[tracing::instrument(skip_all)]
107 async fn vote_comment(
108   vote_type: &VoteType,
109   actor: ApubPerson,
110   comment: &ApubComment,
111   context: &Data<LemmyContext>,
112 ) -> Result<(), LemmyError> {
113   let comment_id = comment.id;
114   let like_form = CommentLikeForm {
115     comment_id,
116     post_id: comment.post_id,
117     person_id: actor.id,
118     score: vote_type.into(),
119   };
120   let person_id = actor.id;
121   CommentLike::remove(context.pool(), person_id, comment_id).await?;
122   CommentLike::like(context.pool(), &like_form).await?;
123   Ok(())
124 }
125
126 #[tracing::instrument(skip_all)]
127 async fn vote_post(
128   vote_type: &VoteType,
129   actor: ApubPerson,
130   post: &ApubPost,
131   context: &Data<LemmyContext>,
132 ) -> Result<(), LemmyError> {
133   let post_id = post.id;
134   let like_form = PostLikeForm {
135     post_id: post.id,
136     person_id: actor.id,
137     score: vote_type.into(),
138   };
139   let person_id = actor.id;
140   PostLike::remove(context.pool(), person_id, post_id).await?;
141   PostLike::like(context.pool(), &like_form).await?;
142   Ok(())
143 }
144
145 #[tracing::instrument(skip_all)]
146 async fn undo_vote_comment(
147   actor: ApubPerson,
148   comment: &ApubComment,
149   context: &Data<LemmyContext>,
150 ) -> Result<(), LemmyError> {
151   let comment_id = comment.id;
152   let person_id = actor.id;
153   CommentLike::remove(context.pool(), person_id, comment_id).await?;
154   Ok(())
155 }
156
157 #[tracing::instrument(skip_all)]
158 async fn undo_vote_post(
159   actor: ApubPerson,
160   post: &ApubPost,
161   context: &Data<LemmyContext>,
162 ) -> Result<(), LemmyError> {
163   let post_id = post.id;
164   let person_id = actor.id;
165   PostLike::remove(context.pool(), person_id, post_id).await?;
166   Ok(())
167 }