Skip to content
Open
Show file tree
Hide file tree
Changes from 16 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/apub/activities/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ serde_with.workspace = true
enum_delegate = "0.2.0"
either = { workspace = true }
lemmy_diesel_utils = { workspace = true }
sha2 = "0.10.9"

[dev-dependencies]

Expand Down
4 changes: 2 additions & 2 deletions crates/apub/activities/src/block/block_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
block::{SiteOrCommunity, generate_cc},
check_community_deleted_or_removed,
community::send_activity_in_community,
generate_activity_id,
generate_activity_id_with_object_id,
protocol::block::block_user::BlockUser,
send_lemmy_activity,
};
Expand Down Expand Up @@ -56,7 +56,7 @@ impl BlockUser {
kind: BlockType::Block,
remove_data,
summary: Some(reason),
id: generate_activity_id(BlockType::Block, context)?,
id: generate_activity_id_with_object_id(BlockType::Block, context)?,
end_time: expires,
audience: target.as_ref().right().map(|c| c.ap_id.clone().into()),
})
Expand Down
4 changes: 2 additions & 2 deletions crates/apub/activities/src/block/undo_block_user.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
activity_lists::AnnouncableActivities,
block::{SiteOrCommunity, generate_cc},
community::send_activity_in_community,
generate_activity_id,
generate_activity_id_with_object_id,
protocol::block::{block_user::BlockUser, undo_block_user::UndoBlockUser},
send_lemmy_activity,
};
Expand Down Expand Up @@ -47,7 +47,7 @@ impl UndoBlockUser {
let block = BlockUser::new(target, user, mod_, None, reason, None, context).await?;
let to = to(target)?;

let id = generate_activity_id(UndoType::Undo, context)?;
let id = generate_activity_id_with_object_id(UndoType::Undo, context)?;
let undo = UndoBlockUser {
actor: mod_.id().clone().into(),
to,
Expand Down
16 changes: 10 additions & 6 deletions crates/apub/activities/src/community/announce.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
activity_lists::AnnouncableActivities,
generate_activity_id,
generate_activity_id_with_object_id,
generate_announce_activity_id,
protocol::{
IdOrNestedObject,
Expand Down Expand Up @@ -82,15 +82,19 @@ impl AnnounceActivity {
pub fn new(
object: RawAnnouncableActivities,
community: &ApubCommunity,
object_id: Option<&Url>,
context: &Data<LemmyContext>,
) -> LemmyResult<AnnounceActivity> {
let inner_kind = object
.other
.get("type")
.and_then(serde_json::Value::as_str)
.unwrap_or("other");
let id =
generate_announce_activity_id(inner_kind, &context.settings().get_protocol_and_hostname())?;
let id = generate_announce_activity_id(
inner_kind,
&context.settings().get_protocol_and_hostname(),
object_id,
)?;
Ok(AnnounceActivity {
actor: community.id().clone().into(),
to: generate_to(community)?,
Expand All @@ -111,7 +115,7 @@ impl AnnounceActivity {
community: &ApubCommunity,
context: &Data<LemmyContext>,
) -> LemmyResult<()> {
let announce = AnnounceActivity::new(object.clone(), community, context)?;
let announce = AnnounceActivity::new(object.clone(), community, None, context)?;
let inboxes = ActivitySendTargets::to_local_community_followers(community.id);
Copy link
Copy Markdown
Author

@uOJackDu uOJackDu Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we sure we want the activity id for Announce activities to be the hash in the outbox, and a random UUID in the sent_activity table? For Create activities, both are the same.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same activity must always have the same ID.

send_lemmy_activity(context, announce, community, inboxes.clone(), false).await?;

Expand All @@ -122,14 +126,14 @@ impl AnnounceActivity {
// Hack: need to convert Page into a format which can be sent as activity, which requires
// adding actor field.
let announcable_page = RawAnnouncableActivities {
id: generate_activity_id(AnnounceType::Announce, context)?,
id: generate_activity_id_with_object_id(AnnounceType::Announce, context)?,
actor: c.actor.clone().into_inner(),
other: serde_json::to_value(c.object)?
.as_object()
.ok_or(UntranslatedError::Unreachable)?
.clone(),
};
let announce_compat = AnnounceActivity::new(announcable_page, community, context)?;
let announce_compat = AnnounceActivity::new(announcable_page, community, None, context)?;
send_lemmy_activity(context, announce_compat, community, inboxes, false).await?;
}
Ok(())
Expand Down
6 changes: 3 additions & 3 deletions crates/apub/activities/src/community/collection_add.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
activity_lists::AnnouncableActivities,
check_community_deleted_or_removed,
community::send_activity_in_community,
generate_activity_id,
generate_activity_id_with_object_id,
protocol::community::{collection_add::CollectionAdd, collection_remove::CollectionRemove},
};
use activitypub_federation::{
Expand Down Expand Up @@ -46,7 +46,7 @@ impl CollectionAdd {
actor: &ApubPerson,
context: &Data<LemmyContext>,
) -> LemmyResult<()> {
let id = generate_activity_id(AddType::Add, context)?;
let id = generate_activity_id_with_object_id(AddType::Add, context)?;
let add = CollectionAdd {
actor: actor.id().clone().into(),
to: generate_to(community)?,
Expand All @@ -69,7 +69,7 @@ impl CollectionAdd {
actor: &ApubPerson,
context: &Data<LemmyContext>,
) -> LemmyResult<()> {
let id = generate_activity_id(AddType::Add, context)?;
let id = generate_activity_id_with_object_id(AddType::Add, context)?;
let add = CollectionAdd {
actor: actor.id().clone().into(),
to: generate_to(community)?,
Expand Down
6 changes: 3 additions & 3 deletions crates/apub/activities/src/community/collection_remove.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::{
activity_lists::AnnouncableActivities,
check_community_deleted_or_removed,
community::send_activity_in_community,
generate_activity_id,
generate_activity_id_with_object_id,
protocol::community::collection_remove::CollectionRemove,
};
use activitypub_federation::{
Expand Down Expand Up @@ -43,7 +43,7 @@ impl CollectionRemove {
actor: &ApubPerson,
context: &Data<LemmyContext>,
) -> LemmyResult<()> {
let id = generate_activity_id(RemoveType::Remove, context)?;
let id = generate_activity_id_with_object_id(RemoveType::Remove, context)?;
let remove = CollectionRemove {
actor: actor.id().clone().into(),
to: generate_to(community)?,
Expand All @@ -66,7 +66,7 @@ impl CollectionRemove {
actor: &ApubPerson,
context: &Data<LemmyContext>,
) -> LemmyResult<()> {
let id = generate_activity_id(RemoveType::Remove, context)?;
let id = generate_activity_id_with_object_id(RemoveType::Remove, context)?;
let remove = CollectionRemove {
actor: actor.id().clone().into(),
to: generate_to(community)?,
Expand Down
6 changes: 3 additions & 3 deletions crates/apub/activities/src/community/lock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
activity_lists::AnnouncableActivities,
check_community_deleted_or_removed,
community::send_activity_in_community,
generate_activity_id,
generate_activity_id_with_object_id,
post_or_comment_community,
protocol::community::lock::{LockPageOrNote, LockType, UndoLockPageOrNote},
};
Expand Down Expand Up @@ -155,7 +155,7 @@ pub(crate) async fn send_lock(
context: Data<LemmyContext>,
) -> LemmyResult<()> {
let community: ApubCommunity = post_or_comment_community(&object, &context).await?.into();
let id = generate_activity_id(LockType::Lock, &context)?;
let id = generate_activity_id_with_object_id(LockType::Lock, &context)?;
let community_id = community.ap_id.inner().clone();
let ap_id = match object {
PostOrComment::Left(p) => p.ap_id.clone(),
Expand All @@ -175,7 +175,7 @@ pub(crate) async fn send_lock(
let activity = if locked {
AnnouncableActivities::Lock(lock)
} else {
let id = generate_activity_id(UndoType::Undo, &context)?;
let id = generate_activity_id_with_object_id(UndoType::Undo, &context)?;
let undo = UndoLockPageOrNote {
actor: lock.actor.clone(),
to: generate_to(&community)?,
Expand Down
6 changes: 3 additions & 3 deletions crates/apub/activities/src/community/report.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::{local_community, report_inboxes};
use crate::{
activity_lists::AnnouncableActivities,
check_community_deleted_or_removed,
generate_activity_id,
generate_activity_id_with_object_id,
protocol::community::{
announce::AnnounceActivity,
report::{Report, ReportObject},
Expand Down Expand Up @@ -57,7 +57,7 @@ impl Report {
context: &Data<LemmyContext>,
) -> LemmyResult<Self> {
let kind = FlagType::Flag;
let id = generate_activity_id(kind.clone(), context)?;
let id = generate_activity_id_with_object_id(kind.clone(), context)?;
Ok(Report {
actor: actor.id().clone().into(),
to: [receiver.id().clone().into()],
Expand Down Expand Up @@ -172,7 +172,7 @@ impl Activity for Report {
// forward to remote mods
let object_id = self.object.object_id(context).await?;
let announce = AnnouncableActivities::Report(self);
let announce = AnnounceActivity::new(announce.try_into()?, community, context)?;
let announce = AnnounceActivity::new(announce.try_into()?, community, None, context)?;
let inboxes = report_inboxes(object_id, &receiver, &actor, context).await?;
send_lemmy_activity(context, announce, community, inboxes.clone(), false).await?;
}
Expand Down
6 changes: 3 additions & 3 deletions crates/apub/activities/src/community/resolve_report.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{local_community, report_inboxes, verify_mod_or_admin_action};
use crate::{
activity_lists::AnnouncableActivities,
generate_activity_id,
generate_activity_id_with_object_id,
protocol::community::{
announce::AnnounceActivity,
report::Report,
Expand Down Expand Up @@ -47,7 +47,7 @@ impl ResolveReport {
context: Data<LemmyContext>,
) -> LemmyResult<()> {
let kind = ResolveType::Resolve;
let id = generate_activity_id(kind.clone(), &context)?;
let id = generate_activity_id_with_object_id(kind.clone(), &context)?;
let object = Report::new(&object_id, report_creator, receiver, None, &context)?;
let resolve = ResolveReport {
actor: actor.id().clone().into(),
Expand Down Expand Up @@ -106,7 +106,7 @@ impl Activity for ResolveReport {
// forward to remote mods
let object_id = self.object.object.object_id(context).await?;
let announce = AnnouncableActivities::ResolveReport(self);
let announce = AnnounceActivity::new(announce.try_into()?, community, context)?;
let announce = AnnounceActivity::new(announce.try_into()?, community, None, context)?;
let inboxes = report_inboxes(object_id, &receiver, &reporter, context).await?;
send_lemmy_activity(context, announce, community, inboxes.clone(), false).await?;
}
Expand Down
6 changes: 3 additions & 3 deletions crates/apub/activities/src/community/update.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
check_community_deleted_or_removed,
community::{AnnouncableActivities, send_activity_in_community},
generate_activity_id,
generate_activity_id_with_object_id,
protocol::community::update::Update,
send_lemmy_activity,
};
Expand Down Expand Up @@ -36,7 +36,7 @@ pub(crate) async fn send_update_community(
) -> LemmyResult<()> {
let community: ApubCommunity = community.into();
let actor: ApubPerson = actor.into();
let id = generate_activity_id(UpdateType::Update, &context)?;
let id = generate_activity_id_with_object_id(UpdateType::Update, &context)?;
let update = Update {
actor: actor.id().clone().into(),
to: generate_to(&community)?,
Expand Down Expand Up @@ -66,7 +66,7 @@ pub(crate) async fn send_update_multi_community(
) -> LemmyResult<()> {
let multi: ApubMultiCommunity = multi.into();
let actor: ApubPerson = actor.into();
let id = generate_activity_id(UpdateType::Update, &context)?;
let id = generate_activity_id_with_object_id(UpdateType::Update, &context)?;
let update = Update {
actor: actor.id().clone().into(),
to: vec![multi.ap_id.clone().into(), public()],
Expand Down
16 changes: 15 additions & 1 deletion crates/apub/activities/src/create_or_update/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,21 @@ impl CreateOrUpdateNote {
.await?
.into();

let id = generate_activity_id(kind.clone(), &context)?;
// get object_id for activity id generation
let ap_id = (*comment.ap_id.0).clone();
let object_id = match kind {
// for Create, use the comment's ap id
CreateOrUpdateType::Create => Some(&ap_id),
// for Update, use a timestamp to ensure each Update activity is unique
CreateOrUpdateType::Update => {
let timestamp = comment.updated_at.unwrap_or(comment.published_at); // use the latest timestamp
let mut seed_url = ap_id;
seed_url.set_fragment(Some(&timestamp.to_rfc3339()));
Comment on lines +65 to +67
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't need to be in this PR, but these things should be trait functions.

Actors have a ApubActor trait, which defines functions like generate_local_actor_url, read_from_name, etc.

There's no reason why that couldn't also be done for the other types, and activities on them also.

Some(&seed_url.clone())
}
};

let id = generate_activity_id(kind.clone(), object_id, &context)?;
let note = ApubComment(comment).into_json(&context).await?;

let create_or_update = CreateOrUpdateNote {
Expand Down
19 changes: 17 additions & 2 deletions crates/apub/activities/src/create_or_update/post.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,10 @@ impl CreateOrUpdatePage {
actor: &ApubPerson,
community: &ApubCommunity,
kind: CreateOrUpdateType,
object_id: Option<&Url>,
context: &Data<LemmyContext>,
) -> LemmyResult<CreateOrUpdatePage> {
let id = generate_activity_id(kind.clone(), context)?;
let id = generate_activity_id(kind.clone(), object_id, context)?;
Ok(CreateOrUpdatePage {
actor: actor.id().clone().into(),
to: generate_to(community)?,
Expand All @@ -70,8 +71,22 @@ impl CreateOrUpdatePage {
.await?
.into();

// get object_id for activity id generation
let ap_id = (*post.ap_id.0).clone();
let object_id = match kind {
// for Create, use the post's ap id
CreateOrUpdateType::Create => Some(&ap_id),
// for Update, use a timestamp to ensure each Update activity is unique
CreateOrUpdateType::Update => {
let timestamp = post.updated_at.unwrap_or(post.published_at); // use the latest timestamp
let mut seed_url = ap_id;
seed_url.set_fragment(Some(&timestamp.to_rfc3339()));
Some(&seed_url.clone())
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above

}
};
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Private message also needs the same logic. Best generate_activity_id() to take an optional timestamp param (or make a separate method), to avoid duplicate code. Theres nothing wrong with hashing the timestamp for create activities as well so it will be simpler.

And you dont need to change anything in this file, it only needs to be in CreateOrUpdatePage::new


let create_or_update =
CreateOrUpdatePage::new(post.into(), &person, &community, kind, &context).await?;
CreateOrUpdatePage::new(post.into(), &person, &community, kind, object_id, &context).await?;
let inboxes = tagged_user_inboxes(&create_or_update.object.tag, &context).await?;
let activity = AnnouncableActivities::CreateOrUpdatePost(create_or_update);
send_activity_in_community(activity, &person, &community, inboxes, false, &context).await?;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::{
generate_activity_id,
generate_activity_id_with_object_id,
protocol::{CreateOrUpdateType, create_or_update::private_message::CreateOrUpdatePrivateMessage},
send_lemmy_activity,
verify_person,
Expand All @@ -24,7 +24,7 @@ pub(crate) async fn send_create_or_update_pm(
let actor: ApubPerson = pm_view.creator.into();
let recipient: ApubPerson = pm_view.recipient.into();

let id = generate_activity_id(kind.clone(), &context)?;
let id = generate_activity_id_with_object_id(kind.clone(), &context)?;
let create_or_update = CreateOrUpdatePrivateMessage {
id: id.clone(),
actor: actor.id().clone().into(),
Expand Down
4 changes: 2 additions & 2 deletions crates/apub/activities/src/deletion/delete.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
MOD_ACTION_DEFAULT_REASON,
deletion::{DeletableObjects, receive_delete_action, verify_delete_activity},
generate_activity_id,
generate_activity_id_with_object_id,
protocol::{IdOrNestedObject, deletion::delete::Delete},
};
use activitypub_federation::{config::Data, kinds::activity::DeleteType, traits::Activity};
Expand Down Expand Up @@ -82,7 +82,7 @@ impl Delete {
with_replies: Option<bool>,
context: &Data<LemmyContext>,
) -> LemmyResult<Delete> {
let id = generate_activity_id(DeleteType::Delete, context)?;
let id = generate_activity_id_with_object_id(DeleteType::Delete, context)?;
let cc: Option<Url> = community.map(|c| c.ap_id.clone().into());
Ok(Delete {
actor: actor.ap_id.clone().into(),
Expand Down
4 changes: 2 additions & 2 deletions crates/apub/activities/src/deletion/undo_delete.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
deletion::{DeletableObjects, receive_delete_action, verify_delete_activity},
generate_activity_id,
generate_activity_id_with_object_id,
protocol::deletion::{delete::Delete, undo_delete::UndoDelete},
};
use activitypub_federation::{config::Data, kinds::activity::UndoType, traits::Activity};
Expand Down Expand Up @@ -72,7 +72,7 @@ impl UndoDelete {
context,
)?;

let id = generate_activity_id(UndoType::Undo, context)?;
let id = generate_activity_id_with_object_id(UndoType::Undo, context)?;
let cc: Option<Url> = community.map(|c| c.ap_id.clone().into());
Ok(UndoDelete {
actor: actor.ap_id.clone().into(),
Expand Down
4 changes: 2 additions & 2 deletions crates/apub/activities/src/following/accept.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::{
check_community_deleted_or_removed,
generate_activity_id,
generate_activity_id_with_object_id,
protocol::following::{accept::AcceptFollow, follow::Follow},
send_lemmy_activity,
};
Expand All @@ -27,7 +27,7 @@ impl AcceptFollow {
to: Some([person.id().clone().into()]),
object: follow,
kind: AcceptType::Accept,
id: generate_activity_id(AcceptType::Accept, context)?,
id: generate_activity_id_with_object_id(AcceptType::Accept, context)?,
};
let inbox = ActivitySendTargets::to_inbox(person.shared_inbox_or_inbox());
send_lemmy_activity(context, accept, &target, inbox, true).await
Expand Down
Loading