use lemmy_utils::{location_info, LemmyError};
use lemmy_websocket::LemmyContext;
+/// Takes an announce and passes the inner activity to the appropriate handler.
pub async fn receive_announce(
context: &LemmyContext,
activity: AnyBase,
mod undo_post;
pub mod update;
+/// Return HTTP 501 for unsupported activities in inbox.
fn receive_unhandled_activity<A>(activity: A) -> Result<HttpResponse, LemmyError>
where
A: Debug,
Ok(HttpResponse::NotImplemented().finish())
}
+/// Reads the destination community from the activity's `cc` field. If this refers to a local
+/// community, the activity is announced to all community followers.
async fn announce_if_community_is_local<T, Kind>(
activity: T,
user: &User_,
{
let cc = activity.cc().context(location_info!())?;
let cc = cc.as_many().context(location_info!())?;
- let community_followers_uri = cc
+ let community_uri = cc
.first()
.context(location_info!())?
.as_xsd_any_uri()
.context(location_info!())?;
- // TODO: this is hacky but seems to be the only way to get the community ID
- let community_uri = community_followers_uri
- .to_string()
- .replace("/followers", "");
- let community = get_or_fetch_and_upsert_community(&Url::parse(&community_uri)?, context).await?;
+ let community = get_or_fetch_and_upsert_community(&community_uri, context).await?;
if community.local {
community
Ok(())
}
+/// Reads the actor field of an activity and returns the corresponding `User_`.
pub(crate) async fn get_actor_as_user<T, A>(
activity: &T,
context: &LemmyContext,
Post(Post),
}
+/// Tries to find a community, post or comment in the local database, without any network requests.
+/// This is used to handle deletions and removals, because in case we dont have the object, we can
+/// simply ignore the activity.
pub(crate) async fn find_by_id(
context: &LemmyContext,
apub_id: Url,
return Err(NotFound.into());
}
+/// Ensure that the ID of an incoming activity comes from the same domain as the actor. Optionally
+/// also checks the ID of the inner object.
+///
+/// The reason that this starts with the actor ID is that it was already confirmed as correct by the
+/// HTTP signature.
pub(crate) fn verify_activity_domains_valid<T, Kind>(
activity: &T,
actor_id: Url,
#[async_trait::async_trait(?Send)]
impl ApubObjectType for Comment {
- /// Send out information about a newly created comment, to the followers of the community.
+ /// Send out information about a newly created comment, to the followers of the community and
+ /// mentioned users.
async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
let note = self.to_apub(context.pool()).await?;
// Set the mention tags
.set_many_tags(maa.get_tags()?);
- send_to_community(&creator, &community, create.clone(), context).await?;
+ send_to_community(create.clone(), &creator, &community, context).await?;
send_comment_mentions(&creator, maa.inboxes, create, context).await?;
Ok(())
}
- /// Send out information about an edited post, to the followers of the community.
+ /// Send out information about an edited post, to the followers of the community and mentioned
+ /// users.
async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
let note = self.to_apub(context.pool()).await?;
// Set the mention tags
.set_many_tags(maa.get_tags()?);
- send_to_community(&creator, &community, update.clone(), context).await?;
+ send_to_community(update.clone(), &creator, &community, context).await?;
send_comment_mentions(&creator, maa.inboxes, update, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&creator, &community, delete, context).await?;
+ send_to_community(delete, &creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&creator, &community, undo, context).await?;
+ send_to_community(undo, &creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&mod_, &community, remove, context).await?;
+ send_to_community(remove, &mod_, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&mod_, &community, undo, context).await?;
+ send_to_community(undo, &mod_, &community, context).await?;
Ok(())
}
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&creator, &community, like, context).await?;
+ send_to_community(like, &creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&creator, &community, dislike, context).await?;
+ send_to_community(dislike, &creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&creator, &community, undo, context).await?;
+ send_to_community(undo, &creator, &community, context).await?;
Ok(())
}
}
Ok(())
}
+ /// If the creator of a community deletes the community, send this to all followers.
async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
let group = self.to_apub(context.pool()).await?;
Ok(())
}
+ /// If the creator of a community reverts the deletion of a community, send this to all followers.
async fn send_undo_delete(
&self,
creator: &User_,
Ok(())
}
+ /// If an admin removes a community, send this to all followers.
async fn send_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
let mut remove = Remove::new(mod_.actor_id.to_owned(), self.actor_id()?);
remove
Ok(())
}
+ /// If an admin reverts the removal of a community, send this to all followers.
async fn send_undo_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
let mut remove = Remove::new(mod_.actor_id.to_owned(), self.actor_id()?);
remove
Ok(())
}
+ /// Wraps an activity sent to the community in an announce, and then sends the announce to all
+ /// community followers.
async fn send_announce(
&self,
activity: AnyBase,
pub mod private_message;
pub mod user;
+/// Generate a unique ID for an activity, in the format:
+/// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`
fn generate_activity_id<T>(kind: T) -> Result<Url, ParseError>
where
T: ToString,
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(creator, &community, create, context).await?;
+ send_to_community(create, creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(creator, &community, update, context).await?;
+ send_to_community(update, creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(creator, &community, delete, context).await?;
+ send_to_community(delete, creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(creator, &community, undo, context).await?;
+ send_to_community(undo, creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(mod_, &community, remove, context).await?;
+ send_to_community(remove, mod_, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(mod_, &community, undo, context).await?;
+ send_to_community(undo, mod_, &community, context).await?;
Ok(())
}
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&creator, &community, like, context).await?;
+ send_to_community(like, &creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&creator, &community, dislike, context).await?;
+ send_to_community(dislike, &creator, &community, context).await?;
Ok(())
}
.set_to(public())
.set_many_ccs(vec![community.actor_id()?]);
- send_to_community(&creator, &community, undo, context).await?;
+ send_to_community(undo, &creator, &community, context).await?;
Ok(())
}
}
Ok(())
}
- /// Send out information about an edited post, to the followers of the community.
+ /// Send out information about an edited private message, to the followers of the community.
async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
let note = self.to_apub(context.pool()).await?;
use std::{collections::BTreeMap, future::Future, pin::Pin};
use url::Url;
+/// Sends a local activity to a single, remote actor.
+///
+/// * `activity` the apub activity to be sent
+/// * `creator` the local actor which created the activity
+/// * `inbox` the inbox url where the activity should be delivered to
pub async fn send_activity_single_dest<T, Kind>(
activity: T,
creator: &dyn ActorType,
Ok(())
}
+/// From a local community, send activity to all remote followers.
+///
+/// * `activity` the apub activity to send
+/// * `community` the sending community
+/// * `sender_shared_inbox` in case of an announce, this should be the shared inbox of the inner
+/// activities creator, as receiving a known activity will cause an error
pub async fn send_to_community_followers<T, Kind>(
activity: T,
community: &Community,
Ok(())
}
+/// Sends an activity from a local user to a remote community.
+///
+/// * `activity` the activity to send
+/// * `creator` the creator of the activity
+/// * `community` the destination community
+///
pub async fn send_to_community<T, Kind>(
+ activity: T,
creator: &User_,
community: &Community,
- activity: T,
context: &LemmyContext,
) -> Result<(), LemmyError>
where
Ok(())
}
+/// Sends notification to any users mentioned in a comment
+///
+/// * `creator` user who created the comment
+/// * `mentions` list of inboxes of users which are mentioned in the comment
+/// * `activity` either a `Create/Note` or `Update/Note`
pub async fn send_comment_mentions<T, Kind>(
creator: &User_,
mentions: Vec<Url>,
Ok(())
}
-/// Asynchronously sends the given `activity` from `actor` to every inbox URL in `to`.
+/// Create new `SendActivityTasks`, which will deliver the given activity to inboxes, as well as
+/// handling signing and retrying failed deliveres.
///
/// The caller of this function needs to remove any blocked domains from `to`,
/// using `check_is_apub_id_valid()`.
private_key: String,
}
+/// Signs the activity with the sending actor's key, and delivers to the given inbox. Also retries
+/// if the delivery failed.
impl ActixJob for SendActivityTask {
type State = MyState;
type Future = Pin<Box<dyn Future<Output = Result<(), Error>>>>;
use lemmy_utils::LemmyError;
use serde::{Deserialize, Serialize};
+/// Activitystreams extension to allow (de)serializing additional Community fields `category` and
+/// `sensitive` (called 'nsfw' in Lemmy).
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct GroupExtension {
use activitystreams_ext::UnparsedExtension;
use serde::{Deserialize, Serialize};
+/// Activitystreams extension to allow (de)serializing additional Post fields
+/// `comemnts_enabled` (called 'locked' in Lemmy),
+/// `sensitive` (called 'nsfw') and `stickied`.
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct PageExtension {
static ref HTTP_SIG_CONFIG: Config = Config::new();
}
-/// Signs request headers with the given keypair.
+/// Creates an HTTP post request to `inbox_url`, with the given `client` and `headers`, and
+/// `activity` as request body. The request is signed with `private_key` and then sent.
pub async fn sign_and_send(
client: &Client,
headers: BTreeMap<String, String>,
- url: &Url,
+ inbox_url: &Url,
activity: String,
actor_id: &Url,
private_key: String,
);
}
let response = client
- .post(&url.to_string())
+ .post(&inbox_url.to_string())
.headers(header_map)
.signature_with_digest(
HTTP_SIG_CONFIG.clone(),
Ok(response)
}
+/// Verifies the HTTP signature on an incoming inbox request.
pub fn verify_signature(request: &HttpRequest, actor: &dyn ActorType) -> Result<(), LemmyError> {
let public_key = actor.public_key().context(location_info!())?;
let verified = CONFIG2
}
}
-// The following is taken from here:
-// https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
+/// Extension for actor public key, which is needed on user and community for HTTP signatures.
+///
+/// Taken from: https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
+#[derive(Clone, Debug, Default, Deserialize, Serialize)]
+#[serde(rename_all = "camelCase")]
+pub struct PublicKeyExtension {
+ pub public_key: PublicKey,
+}
#[derive(Clone, Debug, Default, Deserialize, Serialize)]
#[serde(rename_all = "camelCase")]
pub public_key_pem: String,
}
-#[derive(Clone, Debug, Default, Deserialize, Serialize)]
-#[serde(rename_all = "camelCase")]
-pub struct PublicKeyExtension {
- pub public_key: PublicKey,
-}
-
impl PublicKey {
pub fn to_ext(&self) -> PublicKeyExtension {
PublicKeyExtension {
/// Attempt to parse the query as URL, and fetch an ActivityPub object from it.
///
-/// Some working examples for use with the docker/federation/ setup:
-/// http://lemmy_alpha:8540/c/main, or !main@lemmy_alpha:8540
-/// http://lemmy_alpha:8540/u/lemmy_alpha, or @lemmy_alpha@lemmy_alpha:8540
-/// http://lemmy_alpha:8540/post/3
-/// http://lemmy_alpha:8540/comment/2
+/// Some working examples for use with the `docker/federation/` setup:
+/// http://lemmy_alpha:8541/c/main, or !main@lemmy_alpha:8541
+/// http://lemmy_beta:8551/u/lemmy_alpha, or @lemmy_beta@lemmy_beta:8551
+/// http://lemmy_gamma:8561/post/3
+/// http://lemmy_delta:8571/comment/2
pub async fn search_by_apub_id(
query: &str,
context: &LemmyContext,
Ok(response)
}
+/// Get a remote actor from its apub ID (either a user or a community). Thin wrapper around
+/// `get_or_fetch_and_upsert_user()` and `get_or_fetch_and_upsert_community()`.
+///
+/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
+/// Otherwise it is fetched from the remote instance, stored and returned.
pub(crate) async fn get_or_fetch_and_upsert_actor(
apub_id: &Url,
context: &LemmyContext,
) -> Result<Box<dyn ActorType>, LemmyError> {
- let user = get_or_fetch_and_upsert_user(apub_id, context).await;
- let actor: Box<dyn ActorType> = match user {
- Ok(u) => Box::new(u),
- Err(_) => Box::new(get_or_fetch_and_upsert_community(apub_id, context).await?),
+ let community = get_or_fetch_and_upsert_community(apub_id, context).await;
+ let actor: Box<dyn ActorType> = match community {
+ Ok(c) => Box::new(c),
+ Err(_) => Box::new(get_or_fetch_and_upsert_user(apub_id, context).await?),
};
Ok(actor)
}
-/// Check if a remote user exists, create if not found, if its too old update it.Fetch a user, insert/update it in the database and return the user.
+/// Get a user from its apub ID.
+///
+/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
+/// Otherwise it is fetched from the remote instance, stored and returned.
pub(crate) async fn get_or_fetch_and_upsert_user(
apub_id: &Url,
context: &LemmyContext,
}
/// Determines when a remote actor should be refetched from its instance. In release builds, this is
-/// ACTOR_REFETCH_INTERVAL_SECONDS after the last refetch, in debug builds always.
+/// `ACTOR_REFETCH_INTERVAL_SECONDS` after the last refetch, in debug builds
+/// `ACTOR_REFETCH_INTERVAL_SECONDS_DEBUG`.
///
/// TODO it won't pick up new avatars, summaries etc until a day after.
/// Actors need an "update" activity pushed to other servers to fix this.
last_refreshed.lt(&(naive_now() - update_interval))
}
-/// Check if a remote community exists, create if not found, if its too old update it.Fetch a community, insert/update it in the database and return the community.
+/// Get a community from its apub ID.
+///
+/// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
+/// Otherwise it is fetched from the remote instance, stored and returned.
pub(crate) async fn get_or_fetch_and_upsert_community(
apub_id: &Url,
context: &LemmyContext,
}
}
+/// Request a community by apub ID from a remote instance, including moderators. If `community_id`,
+/// is set, this is an update for a community which is already known locally. If not, we don't know
+/// the community yet and also pull the outbox, to get some initial posts.
async fn fetch_remote_community(
apub_id: &Url,
context: &LemmyContext,
Ok(community)
}
+/// Gets a post by its apub ID. If it exists locally, it is returned directly. Otherwise it is
+/// pulled from its apub ID, inserted and returned.
+///
+/// The parent community is also pulled if necessary. Comments are not pulled.
pub(crate) async fn get_or_fetch_and_insert_post(
post_ap_id: &Url,
context: &LemmyContext,
}
}
+/// Gets a comment by its apub ID. If it exists locally, it is returned directly. Otherwise it is
+/// pulled from its apub ID, inserted and returned.
+///
+/// The parent community, post and comment are also pulled if necessary.
pub(crate) async fn get_or_fetch_and_insert_comment(
comment_ap_id: &Url,
context: &LemmyContext,
comment_id: String,
}
-/// Return the post json over HTTP.
+/// Return the ActivityPub json representation of a local comment over HTTP.
pub async fn get_apub_comment(
info: Path<CommentQuery>,
context: web::Data<LemmyContext>,
community_name: String,
}
-/// Return the community json over HTTP.
+/// Return the ActivityPub json representation of a local community over HTTP.
pub async fn get_apub_community_http(
info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>,
Ok(create_apub_response(&collection))
}
+/// Returns the community outbox, which is populated by a maximum of 20 posts (but no other
+/// activites like votes or comments).
pub async fn get_apub_community_outbox(
info: web::Path<CommunityQuery>,
context: web::Data<LemmyContext>,
post_id: String,
}
-/// Return the post json over HTTP.
+/// Return the ActivityPub json representation of a local post over HTTP.
pub async fn get_apub_post(
info: web::Path<PostQuery>,
context: web::Data<LemmyContext>,
user_name: String,
}
-/// Return the user json over HTTP.
+/// Return the ActivityPub json representation of a local user over HTTP.
pub async fn get_apub_user_http(
info: web::Path<UserQuery>,
context: web::Data<LemmyContext>,
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
+/// Allowed activities for community inbox.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub enum ValidTypes {
res
}
-/// Handle a follow request from a remote user, adding it to the local database and returning an
+/// Handle a follow request from a remote user, adding the user as follower and returning an
/// Accept activity.
async fn handle_follow(
activity: AnyBase,
Ok(HttpResponse::Ok().finish())
}
+/// Handle `Undo/Follow` from a user, removing the user from followers list.
async fn handle_undo_follow(
activity: AnyBase,
user: User_,
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
+/// Allowed activity types for shared inbox.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub enum ValidTypes {
// but it might still work due to the anybase conversion
pub type AcceptedActivities = ActorAndObject<ValidTypes>;
-/// Handler for all incoming receive to user inboxes.
+/// Handler for all incoming requests to shared inbox.
pub async fn shared_inbox(
request: HttpRequest,
input: web::Json<AcceptedActivities>,
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
+/// Allowed activities for user inbox.
#[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
#[serde(rename_all = "PascalCase")]
pub enum ValidTypes {
pub type AcceptedActivities = ActorAndObject<ValidTypes>;
-/// Handler for all incoming receive to user inboxes.
+/// Handler for all incoming activities to user inboxes.
pub async fn user_inbox(
request: HttpRequest,
input: web::Json<AcceptedActivities>,
use serde::Serialize;
use url::{ParseError, Url};
+/// Activitystreams type for community
type GroupExt = Ext2<ApActor<Group>, GroupExtension, PublicKeyExtension>;
+/// Activitystreams type for user
type PersonExt = Ext1<ApActor<Person>, PublicKeyExtension>;
+/// Activitystreams type for post
type PageExt = Ext1<Page, PageExtension>;
pub static APUB_JSON_CONTENT_TYPE: &str = "application/activity+json";
-// Checks if the ID has a valid format, correct scheme, and is in the allowed instance list.
+/// Checks if the ID is allowed for sending or receiving.
+///
+/// In particular, it checks for:
+/// - federation being enabled (if its disabled, only local URLs are allowed)
+/// - the correct scheme (either http or https)
+/// - URL being in the allowlist (if it is active)
+/// - URL not being in the blocklist (if it is active)
+///
+/// Note that only one of allowlist and blacklist can be enabled, not both.
fn check_is_apub_id_valid(apub_id: &Url) -> Result<(), LemmyError> {
let settings = Settings::get();
let domain = apub_id.domain().context(location_info!())?.to_string();
}
}
+/// Trait for converting an object or actor into the respective ActivityPub type.
#[async_trait::async_trait(?Send)]
pub trait ToApub {
- type Response;
- async fn to_apub(&self, pool: &DbPool) -> Result<Self::Response, LemmyError>;
+ type ApubType;
+ async fn to_apub(&self, pool: &DbPool) -> Result<Self::ApubType, LemmyError>;
fn to_tombstone(&self) -> Result<Tombstone, LemmyError>;
}
///
/// * `apub` The object to read from
/// * `context` LemmyContext which holds DB pool, HTTP client etc
- /// * `expected_domain` If present, ensure that the apub object comes from the same domain as
- /// this URL
- ///
+ /// * `expected_domain` If present, ensure that the domains of this and of the apub object ID are
+ /// identical
async fn from_apub(
apub: &Self::ApubType,
context: &LemmyContext,
Self: Sized;
}
+/// Common functions for ActivityPub objects, which are implemented by most (but not all) objects
+/// and actors in Lemmy.
#[async_trait::async_trait(?Send)]
pub trait ApubObjectType {
async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
-> Result<(), LemmyError>;
}
+/// Common methods provided by ActivityPub actors (community and user). Not all methods are
+/// implemented by all actors.
#[async_trait::async_trait(?Send)]
pub trait ActorType {
fn actor_id_str(&self) -> String;
/// numeric id in the database, used for insert_activity
fn user_id(&self) -> i32;
- // These two have default impls, since currently a community can't follow anything,
- // and a user can't be followed (yet)
- #[allow(unused_variables)]
async fn send_follow(
&self,
follow_actor_id: &Url,
context: &LemmyContext,
) -> Result<(), LemmyError>;
- #[allow(unused_variables)]
async fn send_accept_follow(
&self,
follow: Follow,
}
}
+/// Store a sent or received activity in the database, for logging purposes. These records are not
+/// persistent.
pub async fn insert_activity<T>(
user_id: i32,
data: T,
#[async_trait::async_trait(?Send)]
impl ToApub for Comment {
- type Response = Note;
+ type ApubType = Note;
async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
let mut comment = Note::new();
impl FromApub for CommentForm {
type ApubType = Note;
- /// Parse an ActivityPub note received from another instance into a Lemmy comment
+ /// Converts a `Note` to `CommentForm`.
+ ///
+ /// If the parent community, post and comment(s) are not known locally, these are also fetched.
async fn from_apub(
note: &Note,
context: &LemmyContext,
#[async_trait::async_trait(?Send)]
impl ToApub for Community {
- type Response = GroupExt;
+ type ApubType = GroupExt;
- // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
async fn to_apub(&self, pool: &DbPool) -> Result<GroupExt, LemmyError> {
// The attributed to, is an ordered vector with the creator actor_ids first,
// then the rest of the moderators
impl FromApub for CommunityForm {
type ApubType = GroupExt;
- /// Parse an ActivityPub group received from another instance into a Lemmy community.
async fn from_apub(
group: &GroupExt,
context: &LemmyContext,
#[async_trait::async_trait(?Send)]
impl ToApub for Post {
- type Response = PageExt;
+ type ApubType = PageExt;
// Turn a Lemmy post into an ActivityPub page that can be sent out over the network.
async fn to_apub(&self, pool: &DbPool) -> Result<PageExt, LemmyError> {
impl FromApub for PostForm {
type ApubType = PageExt;
- /// Parse an ActivityPub page received from another instance into a Lemmy post.
+ /// Converts a `PageExt` to `PostForm`.
+ ///
+ /// If the post's community or creator are not known locally, these are also fetched.
async fn from_apub(
page: &PageExt,
context: &LemmyContext,
#[async_trait::async_trait(?Send)]
impl ToApub for PrivateMessage {
- type Response = Note;
+ type ApubType = Note;
async fn to_apub(&self, pool: &DbPool) -> Result<Note, LemmyError> {
let mut private_message = Note::new();
impl FromApub for PrivateMessageForm {
type ApubType = Note;
- /// Parse an ActivityPub note received from another instance into a Lemmy Private message
async fn from_apub(
note: &Note,
context: &LemmyContext,
#[async_trait::async_trait(?Send)]
impl ToApub for User_ {
- type Response = PersonExt;
+ type ApubType = PersonExt;
- // Turn a Lemmy Community into an ActivityPub group that can be sent out over the network.
async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
- // TODO go through all these to_string and to_owned()
let mut person = Person::new();
person
.set_context(activitystreams::context())
#[async_trait::async_trait(?Send)]
impl FromApub for UserForm {
type ApubType = PersonExt;
- /// Parse an ActivityPub person received from another instance into a Lemmy user.
+
async fn from_apub(
person: &PersonExt,
_context: &LemmyContext,