mirror of
https://github.com/LemmyNet/lemmy.git
synced 2024-10-01 01:36:12 -04:00
When announcing incoming activities, keep extra fields (#2550)
This commit is contained in:
parent
e3bb43542c
commit
a0fed24cee
@ -9,7 +9,7 @@
|
||||
],
|
||||
"target": "http://enterprise.lemmy.ml/c/main",
|
||||
"type": "Block",
|
||||
"remove_data": "true",
|
||||
"removeData": true,
|
||||
"summary": "spam post",
|
||||
"expires": "2021-11-01T12:23:50.151874+00:00",
|
||||
"id": "http://enterprise.lemmy.ml/activities/block/5d42fffb-0903-4625-86d4-0b39bb344fc2"
|
||||
|
@ -14,7 +14,7 @@
|
||||
],
|
||||
"target": "http://enterprise.lemmy.ml/c/main",
|
||||
"type": "Block",
|
||||
"remove_data": "true",
|
||||
"removeData": true,
|
||||
"summary": "spam post",
|
||||
"expires": "2021-11-01T12:23:50.151874+00:00",
|
||||
"id": "http://enterprise.lemmy.ml/activities/block/726f43ab-bd0e-4ab3-89c8-627e976f553c"
|
||||
|
@ -1,61 +1,81 @@
|
||||
{
|
||||
"type": "OrderedCollection",
|
||||
"id": "https://ds9.lemmy.ml/c/testcom/outbox",
|
||||
"totalItems": 2,
|
||||
"orderedItems": [
|
||||
"type":"OrderedCollection",
|
||||
"id":"https://ds9.lemmy.ml/c/testcom/outbox",
|
||||
"totalItems":2,
|
||||
"orderedItems":[
|
||||
{
|
||||
"actor": "https://ds9.lemmy.ml/c/testcom",
|
||||
"to": [
|
||||
"actor":"https://ds9.lemmy.ml/c/testcom",
|
||||
"to":[
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"object": {
|
||||
"type": "Page",
|
||||
"id": "https://ds9.lemmy.ml/post/2328",
|
||||
"attributedTo": "https://ds9.lemmy.ml/u/nutomic",
|
||||
"to": [
|
||||
"https://ds9.lemmy.ml/c/testcom",
|
||||
"object":{
|
||||
"actor":"https://ds9.lemmy.ml/u/nutomic",
|
||||
"to":[
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [],
|
||||
"name": "another outbox test",
|
||||
"mediaType": "text/html",
|
||||
"commentsEnabled": true,
|
||||
"sensitive": false,
|
||||
"stickied": false,
|
||||
"published": "2021-11-18T17:19:45.895163+00:00"
|
||||
"cc":[
|
||||
"https://ds9.lemmy.ml/c/testcom"
|
||||
],
|
||||
"type":"Create",
|
||||
"id":"http://ds9.lemmy.ml/activities/create/eee6a57a-622f-464d-b560-73ae1fcd3ddf",
|
||||
"object":{
|
||||
"type":"Page",
|
||||
"id":"https://ds9.lemmy.ml/post/2328",
|
||||
"attributedTo":"https://ds9.lemmy.ml/u/nutomic",
|
||||
"to":[
|
||||
"https://ds9.lemmy.ml/c/testcom",
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"name":"another outbox test",
|
||||
"mediaType":"text/html",
|
||||
"commentsEnabled":true,
|
||||
"sensitive":false,
|
||||
"stickied":false,
|
||||
"published":"2021-11-18T17:19:45.895163+00:00"
|
||||
}
|
||||
},
|
||||
"cc": [
|
||||
"cc":[
|
||||
"https://ds9.lemmy.ml/c/testcom/followers"
|
||||
],
|
||||
"type": "Announce",
|
||||
"id": "https://ds9.lemmy.ml/activities/announce/b204fe9f-b13d-4af2-9d22-239ac2d892e6"
|
||||
"type":"Announce",
|
||||
"id":"https://ds9.lemmy.ml/activities/announce/b204fe9f-b13d-4af2-9d22-239ac2d892e6"
|
||||
},
|
||||
{
|
||||
"actor": "https://ds9.lemmy.ml/c/testcom",
|
||||
"to": [
|
||||
"actor":"https://ds9.lemmy.ml/c/testcom",
|
||||
"to":[
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"object": {
|
||||
"type": "Page",
|
||||
"id": "https://ds9.lemmy.ml/post/2327",
|
||||
"attributedTo": "https://ds9.lemmy.ml/u/nutomic",
|
||||
"to": [
|
||||
"https://ds9.lemmy.ml/c/testcom",
|
||||
"object":{
|
||||
"actor":"https://ds9.lemmy.ml/u/nutomic",
|
||||
"to":[
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"cc": [],
|
||||
"name": "outbox test",
|
||||
"mediaType": "text/html",
|
||||
"commentsEnabled": true,
|
||||
"sensitive": false,
|
||||
"stickied": false,
|
||||
"published": "2021-11-18T17:19:05.763109+00:00"
|
||||
"cc":[
|
||||
"https://ds9.lemmy.ml/c/testcom"
|
||||
],
|
||||
"type":"Create",
|
||||
"id":"http://ds9.lemmy.ml/activities/create/eee6a57a-622f-464d-b560-73ae1fcd3ddf",
|
||||
"object":{
|
||||
"type":"Page",
|
||||
"id":"https://ds9.lemmy.ml/post/2327",
|
||||
"attributedTo":"https://ds9.lemmy.ml/u/nutomic",
|
||||
"to":[
|
||||
"https://ds9.lemmy.ml/c/testcom",
|
||||
"https://www.w3.org/ns/activitystreams#Public"
|
||||
],
|
||||
"name":"outbox test",
|
||||
"mediaType":"text/html",
|
||||
"commentsEnabled":true,
|
||||
"sensitive":false,
|
||||
"stickied":false,
|
||||
"published":"2021-11-18T17:19:05.763109+00:00"
|
||||
}
|
||||
},
|
||||
"cc": [
|
||||
"cc":[
|
||||
"https://ds9.lemmy.ml/c/testcom/followers"
|
||||
],
|
||||
"type": "Announce",
|
||||
"id": "https://ds9.lemmy.ml/activities/announce/c6c960ce-c8d8-4231-925e-3ba367468f18"
|
||||
"type":"Announce",
|
||||
"id":"https://ds9.lemmy.ml/activities/announce/c6c960ce-c8d8-4231-925e-3ba367468f18"
|
||||
}
|
||||
]
|
||||
}
|
@ -65,7 +65,6 @@ impl BlockUser {
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?,
|
||||
expires: expires.map(convert_datetime),
|
||||
unparsed: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -53,7 +53,6 @@ impl UndoBlockUser {
|
||||
cc: generate_cc(target, context.pool()).await?,
|
||||
kind: UndoType::Undo,
|
||||
id: id.clone(),
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
|
||||
let mut inboxes = vec![user.shared_inbox_or_inbox()];
|
||||
|
@ -55,7 +55,6 @@ impl AddMod {
|
||||
cc: vec![community.actor_id()],
|
||||
kind: AddType::Add,
|
||||
id: id.clone(),
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
|
||||
let activity = AnnouncableActivities::AddMod(add);
|
||||
|
@ -1,10 +1,16 @@
|
||||
use crate::{
|
||||
activities::{generate_activity_id, send_lemmy_activity, verify_is_public},
|
||||
activities::{
|
||||
generate_activity_id,
|
||||
send_lemmy_activity,
|
||||
verify_is_public,
|
||||
verify_person_in_community,
|
||||
},
|
||||
activity_lists::AnnouncableActivities,
|
||||
insert_activity,
|
||||
objects::community::ApubCommunity,
|
||||
protocol::{
|
||||
activities::{community::announce::AnnounceActivity, CreateOrUpdateType},
|
||||
activities::community::announce::{AnnounceActivity, RawAnnouncableActivities},
|
||||
Id,
|
||||
IdOrNestedObject,
|
||||
},
|
||||
ActorType,
|
||||
@ -13,9 +19,59 @@ use activitypub_federation::{core::object_id::ObjectId, data::Data, traits::Acti
|
||||
use activitystreams_kinds::{activity::AnnounceType, public};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde_json::Value;
|
||||
use tracing::debug;
|
||||
use url::Url;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for RawAnnouncableActivities {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn id(&self) -> &Url {
|
||||
&self.id
|
||||
}
|
||||
|
||||
fn actor(&self) -> &Url {
|
||||
&self.actor
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn verify(
|
||||
&self,
|
||||
_data: &Data<Self::DataType>,
|
||||
_request_counter: &mut i32,
|
||||
) -> Result<(), Self::Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
async fn receive(
|
||||
self,
|
||||
data: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), Self::Error> {
|
||||
let activity: AnnouncableActivities = self.clone().try_into()?;
|
||||
// This is only for sending, not receiving so we reject it.
|
||||
if let AnnouncableActivities::Page(_) = activity {
|
||||
return Err(LemmyError::from_message("Cant receive page"));
|
||||
}
|
||||
let community = activity.get_community(data, &mut 0).await?;
|
||||
let actor_id = ObjectId::new(activity.actor().clone());
|
||||
|
||||
// verify and receive activity
|
||||
activity.verify(data, request_counter).await?;
|
||||
activity.receive(data, request_counter).await?;
|
||||
|
||||
// send to community followers
|
||||
if community.local {
|
||||
verify_person_in_community(&actor_id, &community, data, &mut 0).await?;
|
||||
AnnounceActivity::send(self, &community, data).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
pub(crate) trait GetCommunity {
|
||||
async fn get_community(
|
||||
@ -27,7 +83,7 @@ pub(crate) trait GetCommunity {
|
||||
|
||||
impl AnnounceActivity {
|
||||
pub(crate) fn new(
|
||||
object: AnnouncableActivities,
|
||||
object: RawAnnouncableActivities,
|
||||
community: &ApubCommunity,
|
||||
context: &LemmyContext,
|
||||
) -> Result<AnnounceActivity, LemmyError> {
|
||||
@ -41,13 +97,12 @@ impl AnnounceActivity {
|
||||
&AnnounceType::Announce,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?,
|
||||
unparsed: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
#[tracing::instrument(skip_all)]
|
||||
pub async fn send(
|
||||
object: AnnouncableActivities,
|
||||
object: RawAnnouncableActivities,
|
||||
community: &ApubCommunity,
|
||||
context: &LemmyContext,
|
||||
) -> Result<(), LemmyError> {
|
||||
@ -57,13 +112,18 @@ impl AnnounceActivity {
|
||||
|
||||
// Pleroma and Mastodon can't handle activities like Announce/Create/Page. So for
|
||||
// compatibility, we also send Announce/Page so that they can follow Lemmy communities.
|
||||
use AnnouncableActivities::*;
|
||||
let object = match object {
|
||||
CreateOrUpdatePost(c) if c.kind == CreateOrUpdateType::Create => Page(c.object),
|
||||
_ => return Ok(()),
|
||||
};
|
||||
let announce_compat = AnnounceActivity::new(object, community, context)?;
|
||||
send_lemmy_activity(context, announce_compat, community, inboxes, false).await?;
|
||||
let object_parsed = object.try_into()?;
|
||||
if let AnnouncableActivities::CreateOrUpdatePost(c) = object_parsed {
|
||||
// Hack: need to convert Page into a format which can be sent as activity, which requires
|
||||
// adding actor field.
|
||||
let announcable_page = RawAnnouncableActivities {
|
||||
id: c.object.id.clone().into_inner(),
|
||||
actor: c.actor.clone().into_inner(),
|
||||
other: serde_json::to_value(c.object)?.as_object().unwrap().clone(),
|
||||
};
|
||||
let announce_compat = AnnounceActivity::new(announcable_page, community, context)?;
|
||||
send_lemmy_activity(context, announce_compat, community, inboxes, false).await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -97,27 +157,53 @@ impl ActivityHandler for AnnounceActivity {
|
||||
context: &Data<LemmyContext>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
let object = self.object.object(context, request_counter).await?;
|
||||
let object: AnnouncableActivities = self
|
||||
.object
|
||||
.object(context, request_counter)
|
||||
.await?
|
||||
.try_into()?;
|
||||
// This is only for sending, not receiving so we reject it.
|
||||
if let AnnouncableActivities::Page(_) = object {
|
||||
return Err(LemmyError::from_message("Cant receive page"));
|
||||
}
|
||||
|
||||
// we have to verify this here in order to avoid fetching the object twice over http
|
||||
object.verify(context, request_counter).await?;
|
||||
|
||||
// TODO: this can probably be implemented in a cleaner way
|
||||
match object {
|
||||
// Dont insert these into activities table, as they are not activities.
|
||||
AnnouncableActivities::Page(_) => {}
|
||||
_ => {
|
||||
let object_value = serde_json::to_value(&object)?;
|
||||
let insert =
|
||||
insert_activity(object.id(), object_value, false, true, context.pool()).await?;
|
||||
if !insert {
|
||||
debug!(
|
||||
"Received duplicate activity in announce {}",
|
||||
object.id().to_string()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
let object_value = serde_json::to_value(&object)?;
|
||||
let insert = insert_activity(object.id(), object_value, false, true, context.pool()).await?;
|
||||
if !insert {
|
||||
debug!(
|
||||
"Received duplicate activity in announce {}",
|
||||
object.id().to_string()
|
||||
);
|
||||
return Ok(());
|
||||
}
|
||||
object.receive(context, request_counter).await
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for RawAnnouncableActivities {
|
||||
fn object_id(&self) -> &Url {
|
||||
ActivityHandler::id(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<RawAnnouncableActivities> for AnnouncableActivities {
|
||||
type Error = serde_json::error::Error;
|
||||
|
||||
fn try_from(value: RawAnnouncableActivities) -> Result<Self, Self::Error> {
|
||||
let mut map = value.other.clone();
|
||||
map.insert("id".to_string(), Value::String(value.id.to_string()));
|
||||
map.insert("actor".to_string(), Value::String(value.actor.to_string()));
|
||||
serde_json::from_value(Value::Object(map))
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<AnnouncableActivities> for RawAnnouncableActivities {
|
||||
type Error = serde_json::error::Error;
|
||||
|
||||
fn try_from(value: AnnouncableActivities) -> Result<Self, Self::Error> {
|
||||
serde_json::from_value(serde_json::to_value(value)?)
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ where
|
||||
send_lemmy_activity(context, activity.clone(), actor, inboxes, false).await?;
|
||||
|
||||
if community.local {
|
||||
AnnounceActivity::send(activity, community, context).await?;
|
||||
AnnounceActivity::send(activity.try_into()?, community, context).await?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -55,7 +55,6 @@ impl RemoveMod {
|
||||
id: id.clone(),
|
||||
cc: vec![community.actor_id()],
|
||||
kind: RemoveType::Remove,
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
|
||||
let activity = AnnouncableActivities::RemoveMod(remove);
|
||||
|
@ -47,7 +47,6 @@ impl Report {
|
||||
summary: reason,
|
||||
kind,
|
||||
id: id.clone(),
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
|
||||
let inbox = vec![community.shared_inbox_or_inbox()];
|
||||
|
@ -41,7 +41,6 @@ impl UpdateCommunity {
|
||||
cc: vec![community.actor_id()],
|
||||
kind: UpdateType::Update,
|
||||
id: id.clone(),
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
|
||||
let activity = AnnouncableActivities::UpdateCommunity(update);
|
||||
|
@ -63,7 +63,6 @@ impl CreateOrUpdateComment {
|
||||
object: note,
|
||||
kind,
|
||||
id: id.clone(),
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
|
||||
let tagged_users: Vec<ObjectId<ApubPerson>> = create_or_update
|
||||
|
@ -49,7 +49,6 @@ impl CreateOrUpdatePost {
|
||||
cc: vec![community.actor_id()],
|
||||
kind,
|
||||
id: id.clone(),
|
||||
unparsed: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@ -65,7 +64,8 @@ impl CreateOrUpdatePost {
|
||||
|
||||
let create_or_update = CreateOrUpdatePost::new(post, actor, &community, kind, context).await?;
|
||||
let activity = AnnouncableActivities::CreateOrUpdatePost(Box::new(create_or_update));
|
||||
send_activity_in_community(activity, actor, &community, vec![], context).await
|
||||
send_activity_in_community(activity, actor, &community, vec![], context).await?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -39,7 +39,6 @@ impl CreateOrUpdatePrivateMessage {
|
||||
to: [ObjectId::new(recipient.actor_id())],
|
||||
object: private_message.into_apub(context).await?,
|
||||
kind,
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
let inbox = vec![recipient.shared_inbox_or_inbox()];
|
||||
send_lemmy_activity(context, create_or_update, actor, inbox, true).await
|
||||
|
@ -117,7 +117,6 @@ impl Delete {
|
||||
kind: DeleteType::Delete,
|
||||
summary,
|
||||
id,
|
||||
unparsed: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -117,7 +117,6 @@ impl UndoDelete {
|
||||
cc: cc.into_iter().collect(),
|
||||
kind: UndoType::Undo,
|
||||
id,
|
||||
unparsed: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -40,7 +40,6 @@ impl AcceptFollowCommunity {
|
||||
AcceptType::Accept,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?,
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
let inbox = vec![person.shared_inbox_or_inbox()];
|
||||
send_lemmy_activity(context, accept, &community, inbox, true).await
|
||||
|
@ -38,7 +38,6 @@ impl FollowCommunity {
|
||||
FollowType::Follow,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?,
|
||||
unparsed: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -36,7 +36,6 @@ impl UndoFollowCommunity {
|
||||
UndoType::Undo,
|
||||
&context.settings().get_protocol_and_hostname(),
|
||||
)?,
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
let inbox = vec![community.shared_inbox_or_inbox()];
|
||||
send_lemmy_activity(context, undo, actor, inbox, true).await
|
||||
|
@ -51,7 +51,6 @@ impl UndoVote {
|
||||
object,
|
||||
kind: UndoType::Undo,
|
||||
id: id.clone(),
|
||||
unparsed: Default::default(),
|
||||
};
|
||||
let activity = AnnouncableActivities::UndoVote(undo_vote);
|
||||
send_activity_in_community(activity, actor, &community, vec![], context).await
|
||||
|
@ -37,7 +37,6 @@ impl Vote {
|
||||
object: ObjectId::new(object.ap_id()),
|
||||
kind: kind.clone(),
|
||||
id: generate_activity_id(kind, &context.settings().get_protocol_and_hostname())?,
|
||||
unparsed: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -1,12 +1,12 @@
|
||||
use crate::{
|
||||
activities::{community::announce::GetCommunity, verify_person_in_community},
|
||||
activities::community::announce::GetCommunity,
|
||||
objects::community::ApubCommunity,
|
||||
protocol::{
|
||||
activities::{
|
||||
block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
|
||||
community::{
|
||||
add_mod::AddMod,
|
||||
announce::AnnounceActivity,
|
||||
announce::{AnnounceActivity, RawAnnouncableActivities},
|
||||
remove_mod::RemoveMod,
|
||||
report::Report,
|
||||
update::UpdateCommunity,
|
||||
@ -25,15 +25,9 @@ use crate::{
|
||||
voting::{undo_vote::UndoVote, vote::Vote},
|
||||
},
|
||||
objects::page::Page,
|
||||
Id,
|
||||
},
|
||||
};
|
||||
use activitypub_federation::{
|
||||
core::object_id::ObjectId,
|
||||
data::Data,
|
||||
deser::context::WithContext,
|
||||
traits::{activity_handler, ActivityHandler},
|
||||
};
|
||||
use activitypub_federation::{deser::context::WithContext, traits::activity_handler};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use lemmy_websocket::LemmyContext;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -43,19 +37,19 @@ use url::Url;
|
||||
#[serde(untagged)]
|
||||
#[activity_handler(LemmyContext, LemmyError)]
|
||||
pub enum SharedInboxActivities {
|
||||
GroupInboxActivities(Box<WithContext<GroupInboxActivities>>),
|
||||
// Note, pm activities need to be at the end, otherwise comments will end up here. We can probably
|
||||
// avoid this problem by replacing createpm.object with our own struct, instead of NoteExt.
|
||||
PersonInboxActivities(Box<WithContext<PersonInboxActivities>>),
|
||||
GroupInboxActivities(Box<WithContext<GroupInboxActivities>>),
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[activity_handler(LemmyContext, LemmyError)]
|
||||
pub enum GroupInboxActivities {
|
||||
FollowCommunity(FollowCommunity),
|
||||
UndoFollowCommunity(UndoFollowCommunity),
|
||||
AnnouncableActivities(Box<AnnouncableActivities>),
|
||||
Report(Report),
|
||||
// This is a catch-all and needs to be last
|
||||
AnnouncableActivities(RawAnnouncableActivities),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
@ -63,14 +57,23 @@ pub enum GroupInboxActivities {
|
||||
#[activity_handler(LemmyContext, LemmyError)]
|
||||
pub enum PersonInboxActivities {
|
||||
AcceptFollowCommunity(AcceptFollowCommunity),
|
||||
/// Some activities can also be sent from user to user, eg a comment with mentions
|
||||
AnnouncableActivities(AnnouncableActivities),
|
||||
CreateOrUpdatePrivateMessage(CreateOrUpdatePrivateMessage),
|
||||
Delete(Delete),
|
||||
UndoDelete(UndoDelete),
|
||||
AnnounceActivity(AnnounceActivity),
|
||||
}
|
||||
|
||||
/// This is necessary for user inbox, which can also receive some "announcable" activities,
|
||||
/// eg a comment mention. This needs to be a separate enum so that announcables received in shared
|
||||
/// inbox can fall through to be parsed as GroupInboxActivities::AnnouncableActivities.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[activity_handler(LemmyContext, LemmyError)]
|
||||
pub enum PersonInboxActivitiesWithAnnouncable {
|
||||
PersonInboxActivities(PersonInboxActivities),
|
||||
AnnouncableActivities(AnnouncableActivities),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
#[serde(untagged)]
|
||||
#[activity_handler(LemmyContext, LemmyError)]
|
||||
@ -127,77 +130,6 @@ impl GetCommunity for AnnouncableActivities {
|
||||
}
|
||||
}
|
||||
|
||||
impl Id for AnnouncableActivities {
|
||||
fn object_id(&self) -> &Url {
|
||||
ActivityHandler::id(self)
|
||||
}
|
||||
}
|
||||
|
||||
// Need to implement this manually to announce matching activities
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl ActivityHandler for GroupInboxActivities {
|
||||
type DataType = LemmyContext;
|
||||
type Error = LemmyError;
|
||||
|
||||
fn id(&self) -> &Url {
|
||||
match self {
|
||||
GroupInboxActivities::FollowCommunity(a) => a.id(),
|
||||
GroupInboxActivities::UndoFollowCommunity(a) => a.id(),
|
||||
GroupInboxActivities::AnnouncableActivities(a) => a.object_id(),
|
||||
GroupInboxActivities::Report(a) => a.id(),
|
||||
}
|
||||
}
|
||||
|
||||
fn actor(&self) -> &Url {
|
||||
match self {
|
||||
GroupInboxActivities::FollowCommunity(a) => a.actor(),
|
||||
GroupInboxActivities::UndoFollowCommunity(a) => a.actor(),
|
||||
GroupInboxActivities::AnnouncableActivities(a) => a.actor(),
|
||||
GroupInboxActivities::Report(a) => a.actor(),
|
||||
}
|
||||
}
|
||||
|
||||
async fn verify(
|
||||
&self,
|
||||
data: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
match self {
|
||||
GroupInboxActivities::FollowCommunity(a) => a.verify(data, request_counter).await,
|
||||
GroupInboxActivities::UndoFollowCommunity(a) => a.verify(data, request_counter).await,
|
||||
GroupInboxActivities::AnnouncableActivities(a) => a.verify(data, request_counter).await,
|
||||
GroupInboxActivities::Report(a) => a.verify(data, request_counter).await,
|
||||
}
|
||||
}
|
||||
|
||||
async fn receive(
|
||||
self,
|
||||
data: &Data<Self::DataType>,
|
||||
request_counter: &mut i32,
|
||||
) -> Result<(), LemmyError> {
|
||||
match self {
|
||||
GroupInboxActivities::FollowCommunity(a) => a.receive(data, request_counter).await,
|
||||
GroupInboxActivities::UndoFollowCommunity(a) => a.receive(data, request_counter).await,
|
||||
GroupInboxActivities::AnnouncableActivities(activity) => {
|
||||
activity.clone().receive(data, request_counter).await?;
|
||||
|
||||
// Ignore failures in get_community(). those happen because Delete/PrivateMessage is not in a
|
||||
// community, but looks identical to Delete/Post or Delete/Comment which are in a community.
|
||||
let community = activity.get_community(data, &mut 0).await;
|
||||
if let Ok(community) = community {
|
||||
if community.local {
|
||||
let actor_id = ObjectId::new(activity.actor().clone());
|
||||
verify_person_in_community(&actor_id, &community, data, &mut 0).await?;
|
||||
AnnounceActivity::send(*activity, &community, data).await?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
GroupInboxActivities::Report(a) => a.receive(data, request_counter).await,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{
|
||||
|
@ -4,7 +4,11 @@ use crate::{
|
||||
generate_outbox_url,
|
||||
objects::post::ApubPost,
|
||||
protocol::{
|
||||
activities::community::announce::AnnounceActivity,
|
||||
activities::{
|
||||
community::announce::AnnounceActivity,
|
||||
create_or_update::post::CreateOrUpdatePost,
|
||||
CreateOrUpdateType,
|
||||
},
|
||||
collections::group_outbox::GroupOutbox,
|
||||
},
|
||||
};
|
||||
@ -16,7 +20,10 @@ use activitypub_federation::{
|
||||
use activitystreams_kinds::collection::OrderedCollectionType;
|
||||
use chrono::NaiveDateTime;
|
||||
use futures::future::join_all;
|
||||
use lemmy_db_schema::source::post::Post;
|
||||
use lemmy_db_schema::{
|
||||
source::{person::Person, post::Post},
|
||||
traits::Crud,
|
||||
};
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use url::Url;
|
||||
|
||||
@ -61,9 +68,12 @@ impl ApubObject for ApubCommunityOutbox {
|
||||
async fn into_apub(self, data: &Self::DataType) -> Result<Self::ApubType, LemmyError> {
|
||||
let mut ordered_items = vec![];
|
||||
for post in self.0 {
|
||||
let page = post.into_apub(&data.1).await?;
|
||||
let announcable = AnnouncableActivities::Page(page);
|
||||
let announce = AnnounceActivity::new(announcable, &data.0, &data.1)?;
|
||||
let person = Person::read(data.1.pool(), post.creator_id).await?.into();
|
||||
let create =
|
||||
CreateOrUpdatePost::new(post, &person, &data.0, CreateOrUpdateType::Create, &data.1)
|
||||
.await?;
|
||||
let announcable = AnnouncableActivities::CreateOrUpdatePost(Box::new(create));
|
||||
let announce = AnnounceActivity::new(announcable.try_into()?, &data.0, &data.1)?;
|
||||
ordered_items.push(announce);
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
use crate::{
|
||||
activity_lists::PersonInboxActivities,
|
||||
activity_lists::PersonInboxActivitiesWithAnnouncable,
|
||||
fetcher::user_or_community::UserOrCommunity,
|
||||
generate_outbox_url,
|
||||
http::{create_apub_response, create_apub_tombstone_response, receive_lemmy_activity},
|
||||
@ -45,7 +45,7 @@ pub async fn person_inbox(
|
||||
payload: String,
|
||||
context: web::Data<LemmyContext>,
|
||||
) -> Result<HttpResponse, LemmyError> {
|
||||
receive_lemmy_activity::<WithContext<PersonInboxActivities>, UserOrCommunity>(
|
||||
receive_lemmy_activity::<WithContext<PersonInboxActivitiesWithAnnouncable>, UserOrCommunity>(
|
||||
request, payload, context,
|
||||
)
|
||||
.await
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{activities::block::SiteOrCommunity, objects::person::ApubPerson, protocol::Unparsed};
|
||||
use crate::{activities::block::SiteOrCommunity, objects::person::ApubPerson};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::BlockType;
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
@ -25,6 +25,4 @@ pub struct BlockUser {
|
||||
/// block reason, written to mod log
|
||||
pub(crate) summary: Option<String>,
|
||||
pub(crate) expires: Option<DateTime<FixedOffset>>,
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::block::block_user::BlockUser, Unparsed},
|
||||
};
|
||||
use crate::{objects::person::ApubPerson, protocol::activities::block::block_user::BlockUser};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -19,7 +16,4 @@ pub struct UndoBlockUser {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: UndoType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{objects::person::ApubPerson, protocol::Unparsed};
|
||||
use crate::objects::person::ApubPerson;
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::AddType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -17,7 +17,4 @@ pub struct AddMod {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: AddType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
use crate::{
|
||||
activity_lists::AnnouncableActivities,
|
||||
objects::community::ApubCommunity,
|
||||
protocol::{IdOrNestedObject, Unparsed},
|
||||
};
|
||||
use crate::{objects::community::ApubCommunity, protocol::IdOrNestedObject};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::AnnounceType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::{Map, Value};
|
||||
use url::Url;
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
@ -14,13 +11,20 @@ pub struct AnnounceActivity {
|
||||
pub(crate) actor: ObjectId<ApubCommunity>,
|
||||
#[serde(deserialize_with = "deserialize_one_or_many")]
|
||||
pub(crate) to: Vec<Url>,
|
||||
pub(crate) object: IdOrNestedObject<AnnouncableActivities>,
|
||||
pub(crate) object: IdOrNestedObject<RawAnnouncableActivities>,
|
||||
#[serde(deserialize_with = "deserialize_one_or_many")]
|
||||
pub(crate) cc: Vec<Url>,
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: AnnounceType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
||||
/// Use this to receive community inbox activities, and then announce them if valid. This
|
||||
/// ensures that all json fields are kept, even if Lemmy doesnt understand them.
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
pub struct RawAnnouncableActivities {
|
||||
pub(crate) id: Url,
|
||||
pub(crate) actor: Url,
|
||||
#[serde(flatten)]
|
||||
pub(crate) other: Map<String, Value>,
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{objects::person::ApubPerson, protocol::Unparsed};
|
||||
use crate::objects::person::ApubPerson;
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::RemoveType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -17,7 +17,4 @@ pub struct RemoveMod {
|
||||
pub(crate) kind: RemoveType,
|
||||
pub(crate) target: Url,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::{
|
||||
fetcher::post_or_comment::PostOrComment,
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::Unparsed,
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one};
|
||||
use activitystreams_kinds::activity::FlagType;
|
||||
@ -19,7 +18,4 @@ pub struct Report {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: FlagType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{objects::group::Group, Unparsed},
|
||||
};
|
||||
use crate::{objects::person::ApubPerson, protocol::objects::group::Group};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::UpdateType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -22,7 +19,4 @@ pub struct UpdateCommunity {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: UpdateType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::{
|
||||
mentions::MentionOrValue,
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::CreateOrUpdateType, objects::note::Note, Unparsed},
|
||||
protocol::{activities::CreateOrUpdateType, objects::note::Note},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -21,7 +21,4 @@ pub struct CreateOrUpdateComment {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: CreateOrUpdateType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::CreateOrUpdateType, objects::page::Page, Unparsed},
|
||||
protocol::{activities::CreateOrUpdateType, objects::page::Page},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -18,7 +18,4 @@ pub struct CreateOrUpdatePost {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: CreateOrUpdateType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage, Unparsed},
|
||||
protocol::{activities::CreateOrUpdateType, objects::chat_message::ChatMessage},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one};
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -16,7 +16,4 @@ pub struct CreateOrUpdatePrivateMessage {
|
||||
pub(crate) object: ChatMessage,
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: CreateOrUpdateType,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{objects::tombstone::Tombstone, IdOrNestedObject, Unparsed},
|
||||
protocol::{objects::tombstone::Tombstone, IdOrNestedObject},
|
||||
};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::DeleteType;
|
||||
@ -27,6 +27,4 @@ pub struct Delete {
|
||||
/// If summary is present, this is a mod action (Remove in Lemmy terms). Otherwise, its a user
|
||||
/// deleting their own content.
|
||||
pub(crate) summary: Option<String>,
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::deletion::delete::Delete, Unparsed},
|
||||
};
|
||||
use crate::{objects::person::ApubPerson, protocol::activities::deletion::delete::Delete};
|
||||
use activitypub_federation::{core::object_id::ObjectId, deser::helpers::deserialize_one_or_many};
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -21,6 +18,4 @@ pub struct UndoDelete {
|
||||
#[serde(deserialize_with = "deserialize_one_or_many", default)]
|
||||
#[serde(skip_serializing_if = "Vec::is_empty")]
|
||||
pub(crate) cc: Vec<Url>,
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
objects::community::ApubCommunity,
|
||||
protocol::{activities::following::follow::FollowCommunity, Unparsed},
|
||||
protocol::activities::following::follow::FollowCommunity,
|
||||
};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::activity::AcceptType;
|
||||
@ -15,7 +15,4 @@ pub struct AcceptFollowCommunity {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: AcceptType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
objects::{community::ApubCommunity, person::ApubPerson},
|
||||
protocol::Unparsed,
|
||||
};
|
||||
use crate::objects::{community::ApubCommunity, person::ApubPerson};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::activity::FollowType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -15,7 +12,4 @@ pub struct FollowCommunity {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: FollowType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::following::follow::FollowCommunity, Unparsed},
|
||||
protocol::activities::following::follow::FollowCommunity,
|
||||
};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
@ -15,7 +15,4 @@ pub struct UndoFollowCommunity {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: UndoType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,7 +1,4 @@
|
||||
use crate::{
|
||||
objects::person::ApubPerson,
|
||||
protocol::{activities::voting::vote::Vote, Unparsed},
|
||||
};
|
||||
use crate::{objects::person::ApubPerson, protocol::activities::voting::vote::Vote};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use activitystreams_kinds::activity::UndoType;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -15,7 +12,4 @@ pub struct UndoVote {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: UndoType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
@ -1,8 +1,4 @@
|
||||
use crate::{
|
||||
fetcher::post_or_comment::PostOrComment,
|
||||
objects::person::ApubPerson,
|
||||
protocol::Unparsed,
|
||||
};
|
||||
use crate::{fetcher::post_or_comment::PostOrComment, objects::person::ApubPerson};
|
||||
use activitypub_federation::core::object_id::ObjectId;
|
||||
use lemmy_utils::error::LemmyError;
|
||||
use serde::{Deserialize, Serialize};
|
||||
@ -18,9 +14,6 @@ pub struct Vote {
|
||||
#[serde(rename = "type")]
|
||||
pub(crate) kind: VoteType,
|
||||
pub(crate) id: Url,
|
||||
|
||||
#[serde(flatten)]
|
||||
pub(crate) unparsed: Unparsed,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Display, Deserialize, Serialize, PartialEq, Eq)]
|
||||
|
Loading…
Reference in New Issue
Block a user