Replies a post's author approves (FEP-5624), checked live with PeerTube

PeerTube puts canReply on a video whose comments wait for approval, and answers each comment with ApproveReply. A
persona's reply to such a post now waits (privapub.approval: pending), its Create going to the author alone; the
author's ApproveReply, signed by the author and naming the post answered, lets it out to its audience with
replyApproval, and RejectReply leaves it ours. A null canReply (PeerTube's open comments) says nothing; an empty one
refuses. The PeerTube scenario holds a comment for review and approves it through PeerTube's API (28 checks).

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-06 16:16:34 +02:00
1 parent 5d5f1d6909
commit a3a66b6db2
17 files changed
+256 -25

No files matched your search

@@ -44,6 +44,7 @@ namespace PrivaPub.Federation.Objects
public InteractionRule ReplyPolicy { get; init; }
public InteractionRule LikePolicy { get; init; }
public InteractionRule AnnouncePolicy { get; init; }
public InteractionRule ReplyApprovals { get; init; }//FEP-5624's canReply: who may reply, each reply then approved or refused
public IReadOnlyList<CustomEmoji> Emojis { get; init; } = Array.Empty<CustomEmoji>();
public string CoverURL { get; init; }
public PostLink Link { get; init; }
@@ -122,6 +123,7 @@ namespace PrivaPub.Federation.Objects
ReplyPolicy = ObjectShapes.Policy(note, "canReply"),
LikePolicy = ObjectShapes.Policy(note, "canLike"),
AnnouncePolicy = ObjectShapes.Policy(note, "canAnnounce"),
ReplyApprovals = ObjectShapes.ReplyApprovals(note),
Emojis = ObjectShapes.Emojis(note["tag"]),
CoverURL = ObjectShapes.Cover(note),
Link = ObjectShapes.Link(note),
+13 -6
View File
@@ -102,16 +102,23 @@ namespace PrivaPub.Federation.Objects
// one rule of a post's interactionPolicy (GoToSocial, FEP-044f): who may, automatically or once asked; the old names
// (always, approvalRequired) too. A rule left out means anyone, automatically
static List<string> Who(JsonNode node) => node switch
{
JsonArray array => array.Select(Id).Where(id => id != default).Take(MaxAudience).ToList(),
JsonNode single when Id(single) is { } id => new List<string> { id },
_ => new List<string>()
};
// FEP-5624's canReply (PeerTube's, on a video whose comments wait for approval): who may reply, each reply then
// approved by the author with an ApproveReply, or refused with a RejectReply; null when the post states none (PeerTube
// sends null for a video whose comments are open, and an empty list where they are closed)
public static InteractionRule ReplyApprovals(JsonObject note) =>
note["canReply"] is { } canReply ? new InteractionRule { Manual = Who(canReply) } : default;
public static InteractionRule Policy(JsonObject note, string rule)
{
if (note["interactionPolicy"] is not JsonObject policy || policy[rule] is not JsonObject allowed)
return default;
static List<string> Who(JsonNode node) => node switch
{
JsonArray array => array.Select(Id).Where(id => id != default).Take(MaxAudience).ToList(),
JsonNode single when Id(single) is { } id => new List<string> { id },
_ => new List<string>()
};
return new InteractionRule
{
Automatic = Who(allowed["automaticApproval"] ?? allowed["always"]),