Files
SocialPub/PrivaPub/Federation/Inbox/ReplyRelay.cs
T

43 lines
2.3 KiB
C#
Raw Normal View History

using MongoDB.Entities;
using PrivaPub.Federation.Actors;
using PrivaPub.Federation.Outbox;
using PrivaPub.Infrastructure.Statistics;
using PrivaPub.Models.Federation;
using PrivaPub.Models.Post;
using PrivaPub.StaticServices;
using System.Text.Json.Nodes;
using PostEntity = PrivaPub.Models.Post.Post;
namespace PrivaPub.Federation.Inbox
{
// ActivityPub's inbox forwarding of replies (owner decision 2026-10-05): a public or unlisted reply to one of our
// personas' own posts reaches the persona's followers as its author sent it, and so do its edits and its deletion, as
// Mastodon forwards the replies to its accounts' posts. The author's server sends a reply to its own followers and to
// the persona, never to the persona's followers, who would otherwise see half of the thread. It goes as it came (an
// LD signature in it stays good), signed by the persona; never to the replier's own server, never once more when it
// reached us forwarded, and never for a followers-only reply or a DM, which were not for the persona's followers.
public static class ReplyRelay
{
public static async Task Pass(PostEntity reply, DbEntities db, ILocalActorService localActors, IDeliveryService delivery, CancellationToken token)
{
var raw = Arrival.Current?.Raw;
if (raw == default || delivery == default || localActors == default || reply is not { IsFederatedCopy: true }
|| reply.Visibility is not (PostVisibility.Public or PostVisibility.Unlisted) || string.IsNullOrEmpty(reply.AnsweringToPostId))
return;
var parent = await db.Posts.MatchID(reply.AnsweringToPostId).ExecuteFirstAsync(token);
if (parent is not { IsFederatedCopy: false, IsLocalOnly: false } || !string.IsNullOrEmpty(parent.GroupId)
|| parent.Visibility is not (PostVisibility.Public or PostVisibility.Unlisted or PostVisibility.FollowersOnly))
return;
var owner = await localActors.FindById(LocalActorKind.Person, parent.GroupUserId, token);
if (owner is not { IsFederated: true } || JsonNode.Parse(raw) is not JsonObject activity)
return;
var replier = Interactions.HostOf(reply.ActorURI);
var inboxes = (await delivery.FollowerInboxes(owner, token)).Where(i => Interactions.HostOf(i) != replier).ToList();
if (inboxes.Count > 0)
await delivery.Enqueue(owner, inboxes, activity, token);
}
}
}