]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/voting/vote.rs
Federate votes as private (#2501)
[lemmy.git] / crates / apub / src / activities / voting / vote.rs
1 use crate::{
2   activities::{
3     community::{announce::GetCommunity, send_activity_in_community},
4     generate_activity_id,
5     verify_person_in_community,
6     voting::{vote_comment, vote_post},
7   },
8   activity_lists::AnnouncableActivities,
9   local_instance,
10   objects::{community::ApubCommunity, person::ApubPerson},
11   protocol::activities::voting::vote::{Vote, VoteType},
12   ActorType,
13   PostOrComment,
14 };
15 use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::ActivityHandler};
16 use anyhow::anyhow;
17 use lemmy_api_common::utils::blocking;
18 use lemmy_db_schema::{
19   newtypes::CommunityId,
20   source::{community::Community, post::Post, site::Site},
21   traits::Crud,
22 };
23 use lemmy_utils::error::LemmyError;
24 use lemmy_websocket::LemmyContext;
25 use url::Url;
26
27 /// Vote has as:Public value in cc field, unlike other activities. This indicates to other software
28 /// (like GNU social, or presumably Mastodon), that the like actor should not be disclosed.
29 impl Vote {
30   pub(in crate::activities::voting) fn new(
31     object: &PostOrComment,
32     actor: &ApubPerson,
33     kind: VoteType,
34     context: &LemmyContext,
35   ) -> Result<Vote, LemmyError> {
36     Ok(Vote {
37       actor: ObjectId::new(actor.actor_id()),
38       object: ObjectId::new(object.ap_id()),
39       kind: kind.clone(),
40       id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?,
41       unparsed: Default::default(),
42     })
43   }
44
45   #[tracing::instrument(skip_all)]
46   pub async fn send(
47     object: &PostOrComment,
48     actor: &ApubPerson,
49     community_id: CommunityId,
50     kind: VoteType,
51     context: &LemmyContext,
52   ) -> Result<(), LemmyError> {
53     let community = blocking(context.pool(), move |conn| {
54       Community::read(conn, community_id)
55     })
56     .await??
57     .into();
58     let vote = Vote::new(object, actor, kind, context)?;
59
60     let activity = AnnouncableActivities::Vote(vote);
61     send_activity_in_community(activity, actor, &community, vec![], context).await
62   }
63 }
64
65 #[async_trait::async_trait(?Send)]
66 impl ActivityHandler for Vote {
67   type DataType = LemmyContext;
68   type Error = LemmyError;
69
70   fn id(&self) -> &Url {
71     &self.id
72   }
73
74   fn actor(&self) -> &Url {
75     self.actor.inner()
76   }
77
78   #[tracing::instrument(skip_all)]
79   async fn verify(
80     &self,
81     context: &Data<LemmyContext>,
82     request_counter: &mut i32,
83   ) -> Result<(), LemmyError> {
84     let community = self.get_community(context, request_counter).await?;
85     verify_person_in_community(&self.actor, &community, context, request_counter).await?;
86     let site = blocking(context.pool(), Site::read_local).await??;
87     if self.kind == VoteType::Dislike && !site.enable_downvotes {
88       return Err(anyhow!("Downvotes disabled").into());
89     }
90     Ok(())
91   }
92
93   #[tracing::instrument(skip_all)]
94   async fn receive(
95     self,
96     context: &Data<LemmyContext>,
97     request_counter: &mut i32,
98   ) -> Result<(), LemmyError> {
99     let actor = self
100       .actor
101       .dereference(context, local_instance(context), request_counter)
102       .await?;
103     let object = self
104       .object
105       .dereference(context, local_instance(context), request_counter)
106       .await?;
107     match object {
108       PostOrComment::Post(p) => vote_post(&self.kind, actor, &p, context).await,
109       PostOrComment::Comment(c) => vote_comment(&self.kind, actor, &c, context).await,
110     }
111   }
112 }
113
114 #[async_trait::async_trait(?Send)]
115 impl GetCommunity for Vote {
116   #[tracing::instrument(skip_all)]
117   async fn get_community(
118     &self,
119     context: &LemmyContext,
120     request_counter: &mut i32,
121   ) -> Result<ApubCommunity, LemmyError> {
122     let object = self
123       .object
124       .dereference(context, local_instance(context), request_counter)
125       .await?;
126     let cid = match object {
127       PostOrComment::Post(p) => p.community_id,
128       PostOrComment::Comment(c) => {
129         blocking(context.pool(), move |conn| Post::read(conn, c.post_id))
130           .await??
131           .community_id
132       }
133     };
134     let community = blocking(context.pool(), move |conn| Community::read(conn, cid)).await??;
135     Ok(community.into())
136   }
137 }