Pins across servers, both ways

An account elsewhere that pins or unpins one of its posts (Add or Remove on its `featured`) now shows those pins on its
profile here (`pinned=true`), in its order and its public posts only; a community's announce of a moderator's Add does
the same for the community. The `featured` collection itself is read with the account's counts, at most once a day, so
pins made before PrivaPub ever saw an account show too. Any other target (Smithereen's wall, a community's moderators)
is dropped. A persona's pin and unpin go to the post's audience as Add and Remove on /trophies, as Mastodon sends them.

Checked live against Mastodon (scenarios/pins.sh, 8 checks). The town's checker learnt three peer rules from the
village: Misskey and Sharkey keep a forwarded reply only with its author's LD signature (only Mastodon signs), they
count no renote by a bot, and Mastodon never sees a Lemmy vote on a post in a community. The village of 2026-10-05
checks clean, 2454 of 2454.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
thepraandClaude Opus 5.5 committed 2026-10-05 22:48:57 +02:00
1 parent 5f150b7a59
commit 1c7d1ece9c
23 files changed
+510 -19

No files matched your search

+5 -1
View File
@@ -56,6 +56,7 @@ namespace PrivaPub.Tests.Support
Receiver = new InboxReceiver(Local, Remote, Queue, new NoBlocks(), NullLogger<InboxReceiver>.Instance, Ledger);
Follows = new FollowService(Db, Local, Remote, Delivery, new KeyLocalizer<GenericRes>(), NullLogger<FollowService>.Instance);
Relationships = new RelationshipService(Db, Follows, Delivery);
Featured = new FeaturedPosts(Db, Remote, RemotePosts);
Handlers = new IActivityHandler[]
{
new FollowHandler(Db, Local, Remote, Delivery),
@@ -74,7 +75,9 @@ namespace PrivaPub.Tests.Support
new FlagHandler(Db, Local),
new BlockHandler(Db, Local),
new LockHandler(Db),
new MoveHandler(Remote, Db, Local, Follows, Relationships)
new MoveHandler(Remote, Db, Local, Follows, Relationships),
new FeaturedHandler(Featured, add: true),
new FeaturedHandler(Featured, add: false)
};
((AnnounceHandler)Handlers.First(h => h is AnnounceHandler)).Relays = Handlers;
Processor = new InboxProcessor(Remote, Handlers, NullLogger<InboxProcessor>.Instance, Ledger, Local);
@@ -114,6 +117,7 @@ namespace PrivaPub.Tests.Support
public Participations Participations { get; }
public TimelineService Timelines { get; }
public RelationshipService Relationships { get; }
public FeaturedPosts Featured { get; }
public ReportService Reports { get; }
public async Task FollowedBy(LocalActor local, RemoteActor follower) =>
+1
View File
@@ -36,6 +36,7 @@ namespace PrivaPub.Tests.Support
["preferredUsername"] = Name,
["inbox"] = Id + "/inbox",
["followers"] = Id + "/followers",
["featured"] = Id + "/featured",
["publicKey"] = new JsonObject
{
["id"] = KeyId,