4 activities::send_activity,
17 use activitystreams_ext::Ext1;
18 use activitystreams_new::{
19 activity::{Follow, Undo},
20 actor::{ApActor, Endpoints, Person},
22 object::{Image, Tombstone},
24 primitives::{XsdAnyUri, XsdDateTime},
26 use actix_web::{body::Body, client::Client, web, HttpResponse};
27 use failure::_core::str::FromStr;
30 user::{UserForm, User_},
32 use lemmy_utils::convert_datetime;
33 use serde::Deserialize;
35 #[derive(Deserialize)]
36 pub struct UserQuery {
40 #[async_trait::async_trait(?Send)]
41 impl ToApub for User_ {
42 type Response = PersonExt;
44 // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
45 async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
46 // TODO go through all these to_string and to_owned()
47 let mut person = Person::new();
49 .set_context(context())
50 .set_id(XsdAnyUri::from_str(&self.actor_id)?)
51 .set_name(self.name.to_owned())
52 .set_published(XsdDateTime::from(convert_datetime(self.published)));
54 if let Some(u) = self.updated {
55 person.set_updated(XsdDateTime::from(convert_datetime(u)));
58 if let Some(avatar_url) = &self.avatar {
59 let mut image = Image::new();
60 image.set_url(avatar_url.to_owned());
61 person.set_icon(image.into_any_base()?);
64 let mut ap_actor = ApActor::new(self.get_inbox_url().parse()?, person);
66 .set_outbox(self.get_outbox_url().parse()?)
67 .set_followers(self.get_followers_url().parse()?)
68 .set_following(self.get_following_url().parse()?)
69 .set_liked(self.get_liked_url().parse()?)
70 .set_endpoints(Endpoints {
71 shared_inbox: Some(self.get_shared_inbox_url().parse()?),
75 if let Some(i) = &self.preferred_username {
76 ap_actor.set_preferred_username(i.to_owned());
79 Ok(Ext1::new(ap_actor, self.get_public_key_ext()))
81 fn to_tombstone(&self) -> Result<Tombstone, LemmyError> {
86 #[async_trait::async_trait(?Send)]
87 impl ActorType for User_ {
88 fn actor_id(&self) -> String {
89 self.actor_id.to_owned()
92 fn public_key(&self) -> String {
93 self.public_key.to_owned().unwrap()
96 fn private_key(&self) -> String {
97 self.private_key.to_owned().unwrap()
100 /// As a given local user, send out a follow request to a remote community.
101 async fn send_follow(
103 follow_actor_id: &str,
106 ) -> Result<(), LemmyError> {
107 let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
108 let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
109 follow.set_context(context()).set_id(id.parse()?);
110 let to = format!("{}/inbox", follow_actor_id);
112 insert_activity(self.id, follow.clone(), true, pool).await?;
114 send_activity(client, &follow, self, vec![to]).await?;
118 async fn send_unfollow(
120 follow_actor_id: &str,
123 ) -> Result<(), LemmyError> {
124 let id = format!("{}/follow/{}", self.actor_id, uuid::Uuid::new_v4());
125 let mut follow = Follow::new(self.actor_id.to_owned(), follow_actor_id);
126 follow.set_context(context()).set_id(id.parse()?);
128 let to = format!("{}/inbox", follow_actor_id);
131 // Undo that fake activity
132 let undo_id = format!("{}/undo/follow/{}", self.actor_id, uuid::Uuid::new_v4());
133 let mut undo = Undo::new(self.actor_id.parse::<XsdAnyUri>()?, follow.into_any_base()?);
134 undo.set_context(context()).set_id(undo_id.parse()?);
136 insert_activity(self.id, undo.clone(), true, pool).await?;
138 send_activity(client, &undo, self, vec![to]).await?;
142 async fn send_delete(
147 ) -> Result<(), LemmyError> {
151 async fn send_undo_delete(
156 ) -> Result<(), LemmyError> {
160 async fn send_remove(
165 ) -> Result<(), LemmyError> {
169 async fn send_undo_remove(
174 ) -> Result<(), LemmyError> {
178 async fn send_accept_follow(
183 ) -> Result<(), LemmyError> {
187 async fn get_follower_inboxes(&self, _pool: &DbPool) -> Result<Vec<String>, LemmyError> {
192 #[async_trait::async_trait(?Send)]
193 impl FromApub for UserForm {
194 type ApubType = PersonExt;
195 /// Parse an ActivityPub person received from another instance into a Lemmy user.
196 async fn from_apub(person: &PersonExt, _: &Client, _: &DbPool) -> Result<Self, LemmyError> {
197 let avatar = match person.icon() {
198 Some(any_image) => Image::from_any_base(any_image.as_one().unwrap().clone())
203 .as_single_xsd_any_uri()
204 .map(|u| u.to_string()),
212 .as_single_xsd_string()
215 preferred_username: person.inner.preferred_username().map(|u| u.to_string()),
216 password_encrypted: "".to_string(),
223 .map(|u| u.as_ref().to_owned().naive_local()),
225 theme: "".to_string(),
226 default_sort_type: 0,
227 default_listing_type: 0,
228 lang: "".to_string(),
230 send_notifications_to_email: false,
231 matrix_user_id: None,
232 actor_id: person.id().unwrap().to_string(),
235 .map(|s| s.as_single_xsd_string().unwrap().into()),
238 public_key: Some(person.ext_one.public_key.to_owned().public_key_pem),
239 last_refreshed_at: Some(naive_now()),
244 /// Return the user json over HTTP.
245 pub async fn get_apub_user_http(
246 info: web::Path<UserQuery>,
248 ) -> Result<HttpResponse<Body>, LemmyError> {
249 let user_name = info.into_inner().user_name;
250 let user = blocking(&db, move |conn| {
251 Claims::find_by_email_or_username(conn, &user_name)
254 let u = user.to_apub(&db).await?;
255 Ok(create_apub_response(&u))