]> Untitled Git - lemmy.git/blob - crates/apub/src/activities/voting/vote.rs
Change public activities to field to array (#1739)
[lemmy.git] / crates / apub / src / activities / voting / vote.rs
1 use crate::{
2   activities::{
3     community::announce::AnnouncableActivities,
4     generate_activity_id,
5     verify_activity,
6     verify_person_in_community,
7     voting::{vote_comment, vote_post},
8   },
9   activity_queue::send_to_community_new,
10   extensions::context::lemmy_context,
11   fetcher::{
12     objects::get_or_fetch_and_insert_post_or_comment,
13     person::get_or_fetch_and_upsert_person,
14   },
15   ActorType,
16   PostOrComment,
17 };
18 use activitystreams::{base::AnyBase, primitives::OneOrMany, unparsed::Unparsed};
19 use anyhow::anyhow;
20 use lemmy_api_common::blocking;
21 use lemmy_apub_lib::{values::PublicUrl, ActivityFields, ActivityHandler};
22 use lemmy_db_queries::Crud;
23 use lemmy_db_schema::{
24   source::{community::Community, person::Person},
25   CommunityId,
26 };
27 use lemmy_utils::LemmyError;
28 use lemmy_websocket::LemmyContext;
29 use serde::{Deserialize, Serialize};
30 use std::{convert::TryFrom, ops::Deref};
31 use strum_macros::ToString;
32 use url::Url;
33
34 #[derive(Clone, Debug, ToString, Deserialize, Serialize)]
35 pub enum VoteType {
36   Like,
37   Dislike,
38 }
39
40 impl TryFrom<i16> for VoteType {
41   type Error = LemmyError;
42
43   fn try_from(value: i16) -> Result<Self, Self::Error> {
44     match value {
45       1 => Ok(VoteType::Like),
46       -1 => Ok(VoteType::Dislike),
47       _ => Err(anyhow!("invalid vote value").into()),
48     }
49   }
50 }
51
52 impl From<&VoteType> for i16 {
53   fn from(value: &VoteType) -> i16 {
54     match value {
55       VoteType::Like => 1,
56       VoteType::Dislike => -1,
57     }
58   }
59 }
60
61 #[derive(Clone, Debug, Deserialize, Serialize, ActivityFields)]
62 #[serde(rename_all = "camelCase")]
63 pub struct Vote {
64   actor: Url,
65   to: [PublicUrl; 1],
66   pub(in crate::activities::voting) object: Url,
67   cc: [Url; 1],
68   #[serde(rename = "type")]
69   pub(in crate::activities::voting) kind: VoteType,
70   id: Url,
71   #[serde(rename = "@context")]
72   context: OneOrMany<AnyBase>,
73   #[serde(flatten)]
74   unparsed: Unparsed,
75 }
76
77 impl Vote {
78   pub(in crate::activities::voting) fn new(
79     object: &PostOrComment,
80     actor: &Person,
81     community: &Community,
82     kind: VoteType,
83   ) -> Result<Vote, LemmyError> {
84     Ok(Vote {
85       actor: actor.actor_id(),
86       to: [PublicUrl::Public],
87       object: object.ap_id(),
88       cc: [community.actor_id()],
89       kind: kind.clone(),
90       id: generate_activity_id(kind)?,
91       context: lemmy_context(),
92       unparsed: Default::default(),
93     })
94   }
95
96   pub async fn send(
97     object: &PostOrComment,
98     actor: &Person,
99     community_id: CommunityId,
100     kind: VoteType,
101     context: &LemmyContext,
102   ) -> Result<(), LemmyError> {
103     let community = blocking(context.pool(), move |conn| {
104       Community::read(conn, community_id)
105     })
106     .await??;
107     let vote = Vote::new(object, actor, &community, kind)?;
108     let vote_id = vote.id.clone();
109
110     let activity = AnnouncableActivities::Vote(vote);
111     send_to_community_new(activity, &vote_id, actor, &community, vec![], context).await
112   }
113 }
114
115 #[async_trait::async_trait(?Send)]
116 impl ActivityHandler for Vote {
117   async fn verify(
118     &self,
119     context: &LemmyContext,
120     request_counter: &mut i32,
121   ) -> Result<(), LemmyError> {
122     verify_activity(self)?;
123     verify_person_in_community(&self.actor, &self.cc[0], context, request_counter).await?;
124     Ok(())
125   }
126
127   async fn receive(
128     self,
129     context: &LemmyContext,
130     request_counter: &mut i32,
131   ) -> Result<(), LemmyError> {
132     let actor = get_or_fetch_and_upsert_person(&self.actor, context, request_counter).await?;
133     let object =
134       get_or_fetch_and_insert_post_or_comment(&self.object, context, request_counter).await?;
135     match object {
136       PostOrComment::Post(p) => vote_post(&self.kind, actor, p.deref(), context).await,
137       PostOrComment::Comment(c) => vote_comment(&self.kind, actor, c.deref(), context).await,
138     }
139   }
140 }