3 check_is_apub_id_valid,
4 community::do_announce,
5 extensions::signatures::verify,
7 get_or_fetch_and_upsert_actor,
8 get_or_fetch_and_upsert_community,
9 get_or_fetch_and_upsert_user,
12 announce::receive_announce,
13 create::receive_create,
14 delete::receive_delete,
15 dislike::receive_dislike,
17 remove::receive_remove,
19 update::receive_update,
25 use activitystreams::{
26 activity::{ActorAndObject, ActorAndObjectRef},
27 base::{AsBase, Extends},
31 use actix_web::{web, HttpRequest, HttpResponse};
33 use lemmy_db::user::User_;
34 use lemmy_utils::{location_info, LemmyError};
36 use serde::{Deserialize, Serialize};
40 #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
41 #[serde(rename_all = "PascalCase")]
53 // TODO: this isnt entirely correct, cause some of these activities are not ActorAndObject,
54 // but it might still work due to the anybase conversion
55 pub type AcceptedActivities = ActorAndObject<ValidTypes>;
57 /// Handler for all incoming activities to user inboxes.
58 pub async fn shared_inbox(
60 input: web::Json<AcceptedActivities>,
61 context: web::Data<LemmyContext>,
62 ) -> Result<HttpResponse, LemmyError> {
63 let activity = input.into_inner();
65 let json = serde_json::to_string(&activity)?;
66 debug!("Shared inbox received activity: {}", json);
68 // TODO: if we already received an activity with identical ID, then ignore this (same in other inboxes)
70 let sender = &activity
74 .context(location_info!())?;
75 let community = get_community_id_from_activity(&activity)?;
77 check_is_apub_id_valid(sender)?;
78 check_is_apub_id_valid(&community)?;
80 let actor = get_or_fetch_and_upsert_actor(sender, &context).await?;
81 verify(&request, actor.as_ref())?;
83 let any_base = activity.clone().into_any_base()?;
84 let kind = activity.kind().context(location_info!())?;
85 let res = match kind {
86 ValidTypes::Announce => receive_announce(any_base, &context).await,
87 ValidTypes::Create => receive_create(any_base, &context).await,
88 ValidTypes::Update => receive_update(any_base, &context).await,
89 ValidTypes::Like => receive_like(any_base, &context).await,
90 ValidTypes::Dislike => receive_dislike(any_base, &context).await,
91 ValidTypes::Remove => receive_remove(any_base, &context).await,
92 ValidTypes::Delete => receive_delete(any_base, &context).await,
93 ValidTypes::Undo => receive_undo(any_base, &context).await,
96 insert_activity(actor.user_id(), activity.clone(), false, context.pool()).await?;
100 pub(in crate::apub::inbox) fn receive_unhandled_activity<A>(
102 ) -> Result<HttpResponse, LemmyError>
106 debug!("received unhandled activity type: {:?}", activity);
107 Ok(HttpResponse::NotImplemented().finish())
110 pub(in crate::apub::inbox) async fn get_user_from_activity<T, A>(
112 context: &LemmyContext,
113 ) -> Result<User_, LemmyError>
115 T: AsBase<A> + ActorAndObjectRef,
117 let actor = activity.actor()?;
118 let user_uri = actor.as_single_xsd_any_uri().context(location_info!())?;
119 get_or_fetch_and_upsert_user(&user_uri, context).await
122 pub(in crate::apub::inbox) fn get_community_id_from_activity<T, A>(
124 ) -> Result<Url, LemmyError>
126 T: AsBase<A> + ActorAndObjectRef + AsObject<A>,
128 let cc = activity.cc().context(location_info!())?;
129 let cc = cc.as_many().context(location_info!())?;
132 .context(location_info!())?
134 .context(location_info!())?
139 pub(in crate::apub::inbox) async fn announce_if_community_is_local<T, Kind>(
142 context: &LemmyContext,
143 ) -> Result<(), LemmyError>
148 <T as Extends<Kind>>::Error: From<serde_json::Error> + Send + Sync + 'static,
150 let cc = activity.cc().context(location_info!())?;
151 let cc = cc.as_many().context(location_info!())?;
152 let community_followers_uri = cc
154 .context(location_info!())?
156 .context(location_info!())?;
157 // TODO: this is hacky but seems to be the only way to get the community ID
158 let community_uri = community_followers_uri
160 .replace("/followers", "");
161 let community = get_or_fetch_and_upsert_community(&Url::parse(&community_uri)?, context).await?;
164 do_announce(activity.into_any_base()?, &community, &user, context).await?;