Files
SocialPub/PrivaPub/Federation/Inbox/Handlers/FeaturedHandler.cs
T
thepraandClaude Opus 5.5 c47b6e5533 Personas have walls their followers write on
Owner decision of 2026-10-06 (G-0009, FEP-400e). A persona's actor names its wall (…/graffiti, sm:wall) and, in
Smithereen's privacySettings, that its followers may write on it. A public post that is not a reply, sent with the wall
as its target by an account following the persona, is hosted: the persona is notified, it reaches the persona's and its
followers' homes, and the followers' servers and the author's are told with Add{Note}. The persona deletes it with
DELETE /api/v1/statuses/:id, which sends Remove{Note}; Smithereen deletes the post then. The wall lists the persona's
public posts that start a thread and what was written on it.

Elsewhere: an account's Add{Note} on its own wall shows the post to its followers here as on that wall (privapub.wall on
the status), and its Remove takes it away. An Add or Remove naming a collection of the account's own server PrivaPub
does not know has its document read again first, at most hourly: accounts kept before walls were read had none.

Checked live: Smithereen 40 checks (G-0009 closed); GoToSocial and Mastodon unchanged (121).

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

44 lines
1.8 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 bool _add;
public FeaturedHandler(IFeaturedPosts featured, bool add, IWalls walls = default, IRemoteActorService remoteActors = default)
{
_featured = featured;
_walls = walls;
_remoteActors = remoteActors;
_add = add;
}
public string Type => _add ? "Add" : "Remove";
public async Task Handle(JsonNode activity, ForeignAvatar actor, CancellationToken token)
{
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);
}
}
}