A follow request sent again carries a new id

Lemmy keeps the ids of the activities it received and never answers one again, so resending the same Follow could not
heal a follow whose Accept it lost: the village's community follow stayed pending through two resends. Each resend is
now the same follow under its own id (<follow id>-again-<n>), and an Accept naming any of them answers the follow;
the Undo still embeds the follow, so servers match it by its actor and object. Sent this way, the stuck follow was
accepted at once.

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 10:45:18 +02:00
1 parent a5d493a8c9
commit 206506f5e9
5 files changed
+28 -7

No files matched your search

+9 -1
View File
@@ -298,7 +298,10 @@ namespace PrivaPub.Domain.Social
var inbox = following.TargetInboxURL ?? (await _remoteActors.GetActor(following.TargetActorURI, refresh: false, token))?.InboxURL;
if (follower != default && !string.IsNullOrEmpty(inbox))
{
await _delivery.Enqueue(follower, new[] { inbox }, FollowActivity(follower, following), token,
// under a new id each time: Lemmy keeps the ids it received and never answers one again
var again = FollowActivity(follower, following);
again["id"] = AgainId(following.FollowActivityURI, following.Resent + 1);
await _delivery.Enqueue(follower, new[] { inbox }, again, token,
again: "resend-" + now.ToString("yyyyMMddHHmm", System.Globalization.CultureInfo.InvariantCulture));
sent++;
}
@@ -308,6 +311,11 @@ namespace PrivaPub.Domain.Social
return sent;
}
// a follow sent again is the same follow under a new id, which an answer may name (AcceptHandler.FindFollowing)
public const string Again = "-again-";
public static string AgainId(string followActivityUri, int resend) => $"{followActivityUri}{Again}{resend}";
static JsonObject FollowActivity(LocalActor follower, Following following) => new()
{
["@context"] = ActivityPubRenderer.ActivityStreams,
@@ -53,7 +53,10 @@ namespace PrivaPub.Federation.Inbox.Handlers
{
if (follow is JsonObject && Value(follow, "type") is { } type && type != "Follow")
return default;
var followId = Id(follow);
// (a follow sent again names the first one under a new id: FollowService.AgainId)
var followId = Id(follow) is { } named && named.IndexOf(Domain.Social.FollowService.Again, StringComparison.Ordinal) is var at and > 0
? named[..at]
: Id(follow);
var byId = followId == default
? default
: await dbEntities.Followings.Match(f => f.FollowActivityURI == followId && f.TargetActorURI == target.ActorURI).ExecuteFirstAsync(token);