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