Files
SocialPub/PrivaPub/Federation/Inbox/Handlers/FeaturedHandler.cs
T
thepraandClaude Opus 5.5 d06f684f0d A thread owner's Add of a reply to its context is the reply passed on
FEP-171b, received (Hubzilla, Streams, Forte): an Add whose target is a collection of the actor's own server, and whose
object is someone's Create, Update or Delete of their own object, is queued as that activity forwarded by the owner, so
it is believed on its FEP-8b32 proof or as its origin has it, and shares its job with the plain forward Hubzilla also
sends. Checked live against Hubzilla (unchanged, 20 checks).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
2026-10-06 10:03:44 +02:00

49 lines
2.0 KiB
C#

using PrivaPub.Federation.Actors;
using PrivaPub.Models.User;
using System.Text.Json.Nodes;
namespace PrivaPub.Federation.Inbox.Handlers
{
// An account pins one of its posts (Add to its `featured` collection) or unpins it (Remove), as Mastodon and Akkoma
// send it; inside a community's announce, the community features a post made in it. On its wall (Smithereen's, FEP-400e)
// it tells of a post someone wrote there, or takes it off. Any other target (a community's moderators) is not ours to
// keep.
public class FeaturedHandler : IActivityHandler
{
readonly IFeaturedPosts _featured;
readonly IWalls _walls;
readonly IRemoteActorService _remoteActors;
readonly Infrastructure.Jobs.IJobQueue _queue;
readonly bool _add;
public FeaturedHandler(IFeaturedPosts featured, bool add, IWalls walls = default, IRemoteActorService remoteActors = default,
Infrastructure.Jobs.IJobQueue queue = default)
{
_featured = featured;
_walls = walls;
_remoteActors = remoteActors;
_queue = queue;
_add = add;
}
public string Type => _add ? "Add" : "Remove";
public async Task Handle(JsonNode activity, ForeignAvatar actor, CancellationToken token)
{
if (_add && _queue != default && await ConversationContainers.Unwrap(activity, actor, _queue, token))
return;
var target = Objects.ActivityJson.Id(activity["target"]);
// a collection of its own we do not know of: its document read again, at most hourly, in case it was kept before
// we read that collection (a wall)
if (_remoteActors != default && target != default && target != actor.FeaturedURL && target != actor.WallURL
&& Objects.Origin.Same(target, actor.ActorURI) && actor.UpdatedAt < DateTime.UtcNow.AddHours(-1))
actor = await _remoteActors.GetActor(actor.ActorURI, refresh: true, token) ?? actor;
if (_walls != default && !string.IsNullOrEmpty(actor.WallURL) && target == actor.WallURL)
await _walls.Receive(activity, actor, _add, token);
else
await _featured.Receive(activity, actor, _add, token);
}
}
}