Pixelfed names one of our posts by the address of its page (/@name/<post id>, the post's url) in its Like, Announce and their Undo, so its likes were dropped as unknown objects. Before an activity is handled, such a reference to a post of ours, as its object or the object of the activity it undoes, is replaced by the post's id. Found by the pasture's new Pixelfed peer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LsXgEaXee4GCU1hwYgPJXw
43 lines
1.7 KiB
C#
43 lines
1.7 KiB
C#
using System.Text.Json.Nodes;
|
|
using System.Text.RegularExpressions;
|
|
|
|
using MongoDB.Entities;
|
|
|
|
using PostEntity = PrivaPub.Models.Post.Post;
|
|
|
|
namespace PrivaPub.Federation.Inbox
|
|
{
|
|
// Some servers name one of our posts by the address of its page instead of its id (Pixelfed likes /@name/<post id>, the
|
|
// post's `url`). Before an activity is handled, such a reference to a post of ours, as the activity's object or as the
|
|
// object of the activity it undoes, is replaced by the post's id, so that every handler finds the post the same way.
|
|
public static partial class LocalReferences
|
|
{
|
|
[GeneratedRegex("^/@[^/?#]+/([0-9a-f]{24})$")]
|
|
private static partial Regex PostPage();
|
|
|
|
public static async Task Canonicalise(JsonNode activity, string baseAddress, CancellationToken token)
|
|
{
|
|
if (activity is not JsonObject outer || string.IsNullOrEmpty(baseAddress))
|
|
return;
|
|
await Replace(outer, baseAddress, token);
|
|
if (outer["object"] is JsonObject inner)
|
|
await Replace(inner, baseAddress, token);
|
|
}
|
|
|
|
static async Task Replace(JsonObject node, string baseAddress, CancellationToken token)
|
|
{
|
|
if (node["object"] is not JsonValue value || !value.TryGetValue<string>(out var uri) || PostId(uri, baseAddress) is not { } id)
|
|
return;
|
|
var post = await DB.Default.Find<PostEntity>().Match(p => p.ID == id && !p.IsFederatedCopy).ExecuteFirstAsync(token);
|
|
if (post?.ObjectURI is { } canonical)
|
|
node["object"] = canonical;
|
|
}
|
|
|
|
// the id of the post whose page the address is, on our own site
|
|
public static string PostId(string uri, string baseAddress) =>
|
|
uri.StartsWith(baseAddress + "/@", StringComparison.OrdinalIgnoreCase) && PostPage().Match(uri[baseAddress.Length..]) is { Success: true } page
|
|
? page.Groups[1].Value
|
|
: default;
|
|
}
|
|
}
|