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