]> Untitled Git - lemmy.git/blobdiff - server/src/apub/puller.rs
Merge branch 'federation_add_fed_columns' of https://yerbamate.dev/dessalines/lemmy...
[lemmy.git] / server / src / apub / puller.rs
index a9ac10861ad823d006763fc1746c50b68bf5d21b..6a81b8d0651365d340eb9367d81ef78b60a4acf0 100644 (file)
@@ -1,14 +1,11 @@
-use crate::api::community::{GetCommunityResponse, ListCommunitiesResponse};
+use crate::api::community::GetCommunityResponse;
 use crate::api::post::GetPostsResponse;
-use crate::apub::get_apub_protocol_string;
+use crate::apub::{get_apub_protocol_string, GroupExt};
 use crate::db::community_view::CommunityView;
 use crate::db::post_view::PostView;
-use crate::naive_now;
 use crate::routes::nodeinfo::{NodeInfo, NodeInfoWellKnown};
 use crate::settings::Settings;
-use activitystreams::actor::{properties::ApActorProperties, Group};
 use activitystreams::collection::{OrderedCollection, UnorderedCollection};
-use activitystreams::ext::Ext;
 use activitystreams::object::Page;
 use activitystreams::BaseBox;
 use failure::Error;
@@ -28,39 +25,44 @@ fn fetch_node_info(domain: &str) -> Result<NodeInfo, Error> {
 
 fn fetch_communities_from_instance(domain: &str) -> Result<Vec<CommunityView>, Error> {
   let node_info = fetch_node_info(domain)?;
-  if node_info.software.name != "lemmy" {
-    return Err(format_err!(
+
+  if let Some(community_list_url) = node_info.metadata.community_list_url {
+    let collection = fetch_remote_object::<UnorderedCollection>(&community_list_url)?;
+    let object_boxes = collection
+      .collection_props
+      .get_many_items_base_boxes()
+      .unwrap();
+    let communities: Result<Vec<CommunityView>, Error> = object_boxes
+      .map(|c| -> Result<CommunityView, Error> {
+        let group = c.to_owned().to_concrete::<GroupExt>()?;
+        CommunityView::from_group(&group, domain)
+      })
+      .collect();
+    Ok(communities?)
+  } else {
+    Err(format_err!(
       "{} is not a Lemmy instance, federation is not supported",
       domain
-    ));
+    ))
   }
+}
 
-  // TODO: follow pagination (seems like page count is missing?)
-  // TODO: see if there is any standard for discovering remote actors, so we dont have to rely on lemmy apis
-  let communities_uri = format!(
-    "http://{}/api/v1/communities/list?sort=Hot&local_only=true",
-    domain
-  );
-  let communities1 = fetch_remote_object::<ListCommunitiesResponse>(&communities_uri)?;
-  let mut communities2 = communities1.communities;
-  for c in &mut communities2 {
-    c.name = format_community_name(&c.name, domain);
-  }
-  Ok(communities2)
+/// Returns a tuple of (username, domain) from an identifier like "main@dev.lemmy.ml"
+fn split_identifier(identifier: &str) -> (String, String) {
+  let x: Vec<&str> = identifier.split('@').collect();
+  (x[0].replace("!", ""), x[1].to_string())
 }
 
 fn get_remote_community_uri(identifier: &str) -> String {
-  let x: Vec<&str> = identifier.split('@').collect();
-  let name = x[0].replace("!", "");
-  let instance = x[1];
-  format!("http://{}/federation/c/{}", instance, name)
+  let (name, domain) = split_identifier(identifier);
+  format!("http://{}/federation/c/{}", domain, name)
 }
 
-fn fetch_remote_object<Response>(uri: &str) -> Result<Response, Error>
+pub fn fetch_remote_object<Response>(uri: &str) -> Result<Response, Error>
 where
   Response: for<'de> Deserialize<'de>,
 {
-  if Settings::get().federation.tls_enabled && !uri.starts_with("https") {
+  if Settings::get().federation.tls_enabled && !uri.starts_with("https://") {
     return Err(format_err!("Activitypub uri is insecure: {}", uri));
   }
   // TODO: should cache responses here when we are in production
@@ -71,131 +73,32 @@ where
 }
 
 pub fn get_remote_community_posts(identifier: &str) -> Result<GetPostsResponse, Error> {
-  let community =
-    fetch_remote_object::<Ext<Group, ApActorProperties>>(&get_remote_community_uri(identifier))?;
+  let community = fetch_remote_object::<GroupExt>(&get_remote_community_uri(identifier))?;
   let outbox_uri = &community.extension.get_outbox().to_string();
   let outbox = fetch_remote_object::<OrderedCollection>(outbox_uri)?;
   let items = outbox.collection_props.get_many_items_base_boxes();
 
-  let posts: Vec<PostView> = items
+  let posts: Result<Vec<PostView>, Error> = items
     .unwrap()
     .map(|obox: &BaseBox| {
-      let page: Page = obox.clone().to_concrete::<Page>().unwrap();
-      PostView {
-        id: -1,
-        name: page.object_props.get_name_xsd_string().unwrap().to_string(),
-        url: page
-          .object_props
-          .get_url_xsd_any_uri()
-          .map(|u| u.to_string()),
-        body: page
-          .object_props
-          .get_content_xsd_string()
-          .map(|c| c.to_string()),
-        creator_id: -1,
-        community_id: -1,
-        removed: false,
-        locked: false,
-        published: page
-          .object_props
-          .get_published()
-          .unwrap()
-          .as_ref()
-          .naive_local()
-          .to_owned(),
-        updated: page
-          .object_props
-          .get_updated()
-          .map(|u| u.as_ref().to_owned().naive_local()),
-        deleted: false,
-        nsfw: false,
-        stickied: false,
-        embed_title: None,
-        embed_description: None,
-        embed_html: None,
-        thumbnail_url: None,
-        banned: false,
-        banned_from_community: false,
-        creator_name: "".to_string(),
-        creator_avatar: None,
-        community_name: "".to_string(),
-        community_removed: false,
-        community_deleted: false,
-        community_nsfw: false,
-        number_of_comments: -1,
-        score: -1,
-        upvotes: -1,
-        downvotes: -1,
-        hot_rank: -1,
-        newest_activity_time: naive_now(),
-        user_id: None,
-        my_vote: None,
-        subscribed: None,
-        read: None,
-        saved: None,
-      }
+      let page = obox.clone().to_concrete::<Page>().unwrap();
+      PostView::from_page(&page)
     })
     .collect();
-  Ok(GetPostsResponse { posts })
+  Ok(GetPostsResponse { posts: posts? })
 }
 
 pub fn get_remote_community(identifier: &str) -> Result<GetCommunityResponse, failure::Error> {
-  let community =
-    fetch_remote_object::<Ext<Group, ApActorProperties>>(&get_remote_community_uri(identifier))?;
-  let followers_uri = &community.extension.get_followers().unwrap().to_string();
-  let outbox_uri = &community.extension.get_outbox().to_string();
-  let outbox = fetch_remote_object::<OrderedCollection>(outbox_uri)?;
-  let followers = fetch_remote_object::<UnorderedCollection>(followers_uri)?;
+  let group = fetch_remote_object::<GroupExt>(&get_remote_community_uri(identifier))?;
   // TODO: this is only for testing until we can call that function from GetPosts
   // (once string ids are supported)
   //dbg!(get_remote_community_posts(identifier)?);
 
+  let (_, domain) = split_identifier(identifier);
   Ok(GetCommunityResponse {
     moderators: vec![],
     admins: vec![],
-    community: CommunityView {
-      // TODO: we need to merge id and name into a single thing (stuff like @user@instance.com)
-      id: 1337, //community.object_props.get_id()
-      name: identifier.to_string(),
-      title: community
-        .as_ref()
-        .get_name_xsd_string()
-        .unwrap()
-        .to_string(),
-      description: community
-        .as_ref()
-        .get_summary_xsd_string()
-        .map(|s| s.to_string()),
-      category_id: -1,
-      creator_id: -1, //community.object_props.get_attributed_to_xsd_any_uri()
-      removed: false,
-      published: community
-        .as_ref()
-        .get_published()
-        .unwrap()
-        .as_ref()
-        .naive_local()
-        .to_owned(),
-      updated: community
-        .as_ref()
-        .get_updated()
-        .map(|u| u.as_ref().to_owned().naive_local()),
-      deleted: false,
-      nsfw: false,
-      creator_name: "".to_string(),
-      creator_avatar: None,
-      category_name: "".to_string(),
-      number_of_subscribers: *followers
-        .collection_props
-        .get_total_items()
-        .unwrap()
-        .as_ref() as i64, // TODO: need to use the same type
-      number_of_posts: *outbox.collection_props.get_total_items().unwrap().as_ref() as i64,
-      number_of_comments: -1,
-      hot_rank: -1,
-      user_id: None,
-      subscribed: None,
-    },
+    community: CommunityView::from_group(&group, &domain)?,
     online: 0,
   })
 }