GoToSocial's interaction policies are honoured
A remote post's canReply, canLike and canAnnounce (with the older always and approvalRequired) are kept beside canQuote and judged for each persona: let in at once when the rule names the public, the persona, the author's followers while it follows the author, or the accounts the author follows while the author follows it; asked first when only the manual list names it; refused (422) otherwise. Asked first, a ReplyRequest, LikeRequest or AnnounceRequest with the interaction as its instrument goes to the author alone, and the interaction waits (privapub.approval: pending). The author's Accept brings an authorization, verified on the author's origin as naming the interaction and the post; the reply then goes out with replyAuthorization, the boost with announceAuthorization, the like with likeAuthorization. A Reject leaves the reply ours alone and takes a like or a boost back. As a third party, a reply a policy does not let in at once is kept only with an authorization that verifies. Clients see the rules as GoToSocial's interaction_policy. Checked live against GoToSocial 0.22.1: the scenario's nine new checks pass (64 in all), a reply and a like approved through its interaction requests and a boost refused. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
This commit is contained in:
1 parent
c65a44ba88
commit
5860b71223
21 files changed
+687
-26
No files matched your search
@@ -66,6 +66,7 @@
|
||||
{
|
||||
public string ObjectType { get; set; }
|
||||
public bool Locked { get; set; }//its community's moderators locked it: a reply is refused
|
||||
public string Approval { get; set; }//our reply or boost its target's author must approve: "pending", "rejected"
|
||||
public string Title { get; set; }
|
||||
public string Excerpt { get; set; }
|
||||
public string Cover { get; set; }
|
||||
@@ -316,6 +317,21 @@
|
||||
public QuoteEntity Quote { get; set; }
|
||||
public int QuotesCount { get; set; }
|
||||
public QuoteApprovalEntity QuoteApproval { get; set; }
|
||||
public InteractionPolicyEntity InteractionPolicy { get; set; }//GoToSocial's: who may like, reply to and boost a remote post
|
||||
}
|
||||
|
||||
// GoToSocial's interaction_policy: "public", "followers", "following", "author", "me", or an account's URI
|
||||
public class InteractionPolicyEntity
|
||||
{
|
||||
public InteractionRuleEntity CanFavourite { get; set; }
|
||||
public InteractionRuleEntity CanReply { get; set; }
|
||||
public InteractionRuleEntity CanReblog { get; set; }
|
||||
}
|
||||
|
||||
public class InteractionRuleEntity
|
||||
{
|
||||
public List<string> AutomaticApproval { get; set; } = new();
|
||||
public List<string> ManualApproval { get; set; } = new();
|
||||
}
|
||||
|
||||
public class MediaAttachment
|
||||
|
||||
@@ -288,6 +288,7 @@ namespace PrivaPub.Api.Mastodon.Mappers
|
||||
{
|
||||
status.QuotesCount = post.QuotesCount;
|
||||
status.QuoteApproval = post.IsFederatedCopy ? Approval(post, viewerId) : LocalApproval(post, viewerId, followsAuthor.Contains(post.GroupUserId));
|
||||
status.InteractionPolicy = Policies(post);
|
||||
if (post.QuoteState == QuoteState.None)
|
||||
return;
|
||||
var state = post.QuoteState.ToString().ToLowerInvariant();
|
||||
@@ -360,6 +361,23 @@ namespace PrivaPub.Api.Mastodon.Mappers
|
||||
return new QuoteApprovalEntity { Automatic = everyone, CurrentUser = current };
|
||||
}
|
||||
|
||||
// a remote post's canLike, canReply and canAnnounce, in GoToSocial's client terms; null when it states none
|
||||
static InteractionPolicyEntity Policies(PostEntity post)
|
||||
{
|
||||
if (!post.IsFederatedCopy || post.LikePolicy == default && post.ReplyPolicy == default && post.AnnouncePolicy == default)
|
||||
return default;
|
||||
var collections = post.ActorURI?.TrimEnd('/');
|
||||
string Named(string who) => Addressing.IsPublic(who) ? "public"
|
||||
: who == post.ActorURI ? "author"
|
||||
: who.EndsWith("/followers", StringComparison.Ordinal) && who.StartsWith(collections, StringComparison.Ordinal) ? "followers"
|
||||
: who.EndsWith("/following", StringComparison.Ordinal) && who.StartsWith(collections, StringComparison.Ordinal) ? "following"
|
||||
: who;
|
||||
InteractionRuleEntity Rule(InteractionRule rule) => rule == default
|
||||
? new InteractionRuleEntity { AutomaticApproval = new List<string> { "public" } }
|
||||
: new InteractionRuleEntity { AutomaticApproval = rule.Automatic.Select(Named).Distinct().ToList(), ManualApproval = rule.Manual.Select(Named).Distinct().ToList() };
|
||||
return new InteractionPolicyEntity { CanFavourite = Rule(post.LikePolicy), CanReply = Rule(post.ReplyPolicy), CanReblog = Rule(post.AnnouncePolicy) };
|
||||
}
|
||||
|
||||
static QuoteApprovalEntity Approval(PostEntity post, string viewerId)
|
||||
{
|
||||
if (!post.IsFederatedCopy || post.Visibility is not (PostVisibility.Public or PostVisibility.Unlisted))
|
||||
@@ -448,6 +466,7 @@ namespace PrivaPub.Api.Mastodon.Mappers
|
||||
{
|
||||
ObjectType = post.ObjectType,
|
||||
Locked = post.LockedAt.HasValue,
|
||||
Approval = post.Approval is ApprovalState.Pending or ApprovalState.Rejected ? post.Approval.ToString().ToLowerInvariant() : default,
|
||||
Title = post.IsFederatedCopy ? post.Title : default,
|
||||
Excerpt = post.Excerpt,
|
||||
Cover = Proxied(post.CoverURL),
|
||||
|
||||
Reference in new issue
Block a user