diff --git a/DysonNetwork.Sphere/ActivityPub/ActivityPubActivityHandler.cs b/DysonNetwork.Sphere/ActivityPub/ActivityPubActivityHandler.cs index 41dfa9dd..3452a8a8 100644 --- a/DysonNetwork.Sphere/ActivityPub/ActivityPubActivityHandler.cs +++ b/DysonNetwork.Sphere/ActivityPub/ActivityPubActivityHandler.cs @@ -26,7 +26,7 @@ public class ActivityPubActivityHandler( { if (!Uri.TryCreate(objectUri, UriKind.Absolute, out var uri)) return await db.Posts.FirstOrDefaultAsync(c => c.FediverseUri == objectUri); - + var domain = uri.Host; // Remote post @@ -320,6 +320,37 @@ public class ActivityPubActivityHandler( Visibility = PostVisibility.Public, Metadata = BuildMetadataFromActivity(objectDict) }; + + var inReplyTo = GetStringValue(objectDict, "inReplyTo"); + if (!string.IsNullOrEmpty(inReplyTo)) + { + var replyingTo = await db.Posts + .Where(c => c.FediverseUri == inReplyTo) + .FirstOrDefaultAsync(); + if (replyingTo == null) + { + logger.LogWarning("Incoming post replied to {Uri}, but that post was not found in our database, skipping...", inReplyTo); + return true; + } + + content.RepliedPostId = replyingTo.Id; + } + + var quoteUri = GetStringValue(objectDict, "quoteUri"); + if (!string.IsNullOrEmpty(quoteUri)) + { + var forwardedItem = await db.Posts + .FirstOrDefaultAsync(c => c.FediverseUri == quoteUri); + if (forwardedItem == null) + { + logger.LogWarning("Incoming post quoted {Uri}, but the post not found in our database...", quoteUri); + content.ForwardedGone = true; + } + else + { + content.ForwardedPostId = forwardedItem.Id; + } + } db.Posts.Add(content); await db.SaveChangesAsync(); @@ -422,7 +453,7 @@ public class ActivityPubActivityHandler( var objectUri = GetStringValue(objectDict, "id"); if (string.IsNullOrEmpty(objectUri)) return false; - + var actor = await GetOrCreateActorAsync(actorUri); var content = await GetPostByUriAsync(objectUri); @@ -730,9 +761,7 @@ public class ActivityPubActivityHandler( .ToList(); if (fediverseTags.Count > 0) - { - metadata["fediverseTags"] = fediverseTags; - } + metadata["fediverse_tags"] = fediverseTags; } var emojiValue = objectDict.GetValueOrDefault("emoji"); @@ -749,10 +778,14 @@ public class ActivityPubActivityHandler( if (emojis.Count > 0) { - metadata["fediverseEmojis"] = emojis; + metadata["fediverse_emojis"] = emojis; } } + var fediverseUrl = objectDict.GetValueOrDefault("url"); + if (fediverseUrl is not null) + metadata["fediverse_url"] = fediverseUrl.ToString()!; + return metadata; } } \ No newline at end of file diff --git a/DysonNetwork.Sphere/ActivityPub/ActivityPubController.cs b/DysonNetwork.Sphere/ActivityPub/ActivityPubController.cs index 6410e45a..2bfe87cb 100644 --- a/DysonNetwork.Sphere/ActivityPub/ActivityPubController.cs +++ b/DysonNetwork.Sphere/ActivityPub/ActivityPubController.cs @@ -158,9 +158,9 @@ public class ActivityPubController( .Take(pageSize) .ToListAsync(); - var items = posts.Select(post => + var items = Task.WhenAll(posts.Select(async post => { - var postObject = objFactory.CreatePostObject(post, actorUrl); + var postObject = await objFactory.CreatePostObject(post, actorUrl); postObject["url"] = $"https://{Domain}/posts/{post.Id}"; return new Dictionary { @@ -172,7 +172,7 @@ public class ActivityPubController( ["cc"] = new[] { $"{actorUrl}/followers" }, ["@object"] = postObject }; - }).Cast().ToList(); + })).Result.Cast().ToList(); var collectionPage = new ActivityPubCollectionPage { diff --git a/DysonNetwork.Sphere/ActivityPub/ActivityPubDeliveryService.cs b/DysonNetwork.Sphere/ActivityPub/ActivityPubDeliveryService.cs index e4a15596..edd17237 100644 --- a/DysonNetwork.Sphere/ActivityPub/ActivityPubDeliveryService.cs +++ b/DysonNetwork.Sphere/ActivityPub/ActivityPubDeliveryService.cs @@ -169,7 +169,7 @@ public class ActivityPubDeliveryService( ["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(), ["to"] = new[] { ActivityPubObjectFactory.PublicTo }, ["cc"] = new[] { $"{actorUrl}/followers" }, - ["object"] = objFactory.CreatePostObject(post, actorUrl) + ["object"] = await objFactory.CreatePostObject(post, actorUrl) }; var followers = await GetRemoteFollowersAsync(); @@ -205,7 +205,7 @@ public class ActivityPubDeliveryService( ["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(), ["to"] = new[] { ActivityPubObjectFactory.PublicTo }, ["cc"] = new[] { $"{actorUrl}/followers" }, - ["object"] = objFactory.CreatePostObject(post, actorUrl) + ["object"] = await objFactory.CreatePostObject(post, actorUrl) }; var followers = await GetRemoteFollowersAsync(); diff --git a/DysonNetwork.Sphere/ActivityPub/ActivityPubObjectFactory.cs b/DysonNetwork.Sphere/ActivityPub/ActivityPubObjectFactory.cs index f9121d71..4ef2ea14 100644 --- a/DysonNetwork.Sphere/ActivityPub/ActivityPubObjectFactory.cs +++ b/DysonNetwork.Sphere/ActivityPub/ActivityPubObjectFactory.cs @@ -16,7 +16,7 @@ public class ActivityPubObjectFactory(IConfiguration configuration, AppDatabase .FirstOrDefaultAsync(a => a.PublisherId == publisherId); } - public Dictionary CreatePostObject( + public async Task> CreatePostObject( SnPost post, string actorUrl ) @@ -51,21 +51,28 @@ public class ActivityPubObjectFactory(IConfiguration configuration, AppDatabase var finalContent = contentBuilder.ToString(); - var postReceivers = new List { PublicTo }; + var postReceivers = new List { $"{actorUrl}/followers" }; - if (post.RepliedPost != null) + if (post.RepliedPostId != null) { + var repliedPost = await db.Posts + .Where(p => p.Id == post.RepliedPostId) + .Include(p => p.Publisher) + .Include(p => p.Actor) + .FirstOrDefaultAsync(); + post.RepliedPost = repliedPost; + // Local post - if (post.RepliedPost.Publisher != null) + if (repliedPost?.Publisher != null) { - var actor = GetLocalActorAsync(post.RepliedPost.PublisherId!.Value).GetAwaiter().GetResult(); + var actor = await GetLocalActorAsync(repliedPost.PublisherId!.Value); if (actor?.FollowersUri != null) postReceivers.Add(actor.FollowersUri); } // Fediverse post - if (post.Actor?.FollowersUri != null) - postReceivers.Add(post.Actor.FollowersUri); + if (repliedPost?.Actor?.FollowersUri != null) + postReceivers.Add(post.Actor!.FollowersUri!); } var postObject = new Dictionary @@ -75,8 +82,8 @@ public class ActivityPubObjectFactory(IConfiguration configuration, AppDatabase ["published"] = (post.PublishedAt ?? post.CreatedAt).ToDateTimeOffset(), ["attributedTo"] = actorUrl, ["content"] = Markdown.ToHtml(finalContent), - ["to"] = postReceivers, - ["cc"] = new[] { $"{actorUrl}/followers" }, + ["to"] = new[] { PublicTo }, + ["cc"] = postReceivers, ["attachment"] = post.Attachments.Select(a => new Dictionary { ["type"] = "Document", @@ -85,6 +92,7 @@ public class ActivityPubObjectFactory(IConfiguration configuration, AppDatabase }).ToList() }; + // The post's replied post is ensure loaded above, so we directly using it here if (post.RepliedPost != null) { // Local post @@ -95,14 +103,19 @@ public class ActivityPubObjectFactory(IConfiguration configuration, AppDatabase postObject["inReplyTo"] = post.RepliedPost.FediverseUri; } - if (post.ForwardedPost != null) + if (post.ForwardedPostId != null) { + var forwardedPost = await db.Posts + .Where(p => p.Id == post.ForwardedPostId) + .Include(p => p.Publisher) + .FirstOrDefaultAsync(); + // Local post - if (post.ForwardedPost.Publisher != null) + if (forwardedPost?.Publisher != null) postObject["quoteUri"] = $"https://{baseDomain}/posts/{post.RepliedPostId}"; // Fediverse post - if (post.ForwardedPost.FediverseUri != null) - postObject["quoteUri"] = post.ForwardedPost.FediverseUri; + if (forwardedPost?.FediverseUri != null) + postObject["quoteUri"] = forwardedPost.FediverseUri; } if (post.EditedAt.HasValue) diff --git a/DysonNetwork.Sphere/Post/PostService.cs b/DysonNetwork.Sphere/Post/PostService.cs index 315923da..13946d2f 100644 --- a/DysonNetwork.Sphere/Post/PostService.cs +++ b/DysonNetwork.Sphere/Post/PostService.cs @@ -645,6 +645,11 @@ public partial class PostService( }); } } + else + { + logger.LogWarning("Seems {PublisherName} didn't enable actor, skipping delivery of Like activity...", + accountPublisher?.Name); + } } _ = Task.Run(async () =>