From e54e17708fd10f965eaa00959e2d145237cddca3 Mon Sep 17 00:00:00 2001 From: thepra Date: Thu, 1 Oct 2026 11:21:31 +0200 Subject: [PATCH] Outbound documents speak Mastodon's dialect under PrivaPub's route names Local text (Domain/Content/ContentRenderer): Markdig with raw HTML off and autolinks, or plain text for API clients; then @user and @user@host are resolved (locally or through WebFinger) into Mastodon's h-card markup and #tags into hashtag links, skipping code, links and e-mail addresses. A post stores the result, its mentions and its tags, and is delivered to the mentioned actors and the parent's author as well as to followers. Renderer: - Mastodon's @context (toot, schema PropertyValue, discoverable, indexable, blurhash, focalPoint, featured, alsoKnownAs, movedTo) and FEP-2c59's webfinger; - an actor's url is its HTML page, its fields are PropertyValue attachments, published is cut to the day and indexable is false; - a note's content is the stored rendering; a title becomes name and a bold first line, a content warning without its own text is summarised by the title or "Content warning"; Mention and Hashtag tags, contentMap, updated, and to/cc by visibility. Routes take the agreed names: /groupies, /stalking, /scribbles/{id}, /grunts/{id} (a Create resolves), /whispers/{id} for a DM's context. The outbox is a collection with a first page, paged by max_id. A browser asking for an actor or a note is redirected to /@user, and WebFinger's profile-page points there. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB --- PrivaPub.Tests/Domain/ContentRendererTests.cs | 91 ++++++++++ PrivaPub.Tests/Federation/RendererTests.cs | 103 +++++++++++ PrivaPub/Domain/Content/ContentRenderer.cs | 150 ++++++++++++++++ .../Federation/Actors/LocalActorService.cs | 13 +- .../Controllers/PeasantsController.cs | 99 ++++++++--- .../Controllers/WellKnownController.cs | 10 +- .../Rendering/ActivityPubRenderer.cs | 166 ++++++++++++++---- .../Middleware/SocialPubConfigurations.cs | 2 + PrivaPub/Services/PostsService.cs | 30 +++- 9 files changed, 590 insertions(+), 74 deletions(-) create mode 100644 PrivaPub.Tests/Domain/ContentRendererTests.cs create mode 100644 PrivaPub.Tests/Federation/RendererTests.cs create mode 100644 PrivaPub/Domain/Content/ContentRenderer.cs diff --git a/PrivaPub.Tests/Domain/ContentRendererTests.cs b/PrivaPub.Tests/Domain/ContentRendererTests.cs new file mode 100644 index 0000000..2861f32 --- /dev/null +++ b/PrivaPub.Tests/Domain/ContentRendererTests.cs @@ -0,0 +1,91 @@ +using PrivaPub.Domain.Content; +using PrivaPub.Federation.Actors; +using PrivaPub.Infrastructure.Http; +using PrivaPub.Models.Federation; +using PrivaPub.Models.User; + +using GroupEntity = PrivaPub.Models.Group.Group; + +namespace PrivaPub.Tests.Domain +{ + public class ContentRendererTests + { + const string Base = "https://privapub.test"; + readonly ContentRenderer _renderer = new(new StubLocalActors(), new StubRemoteActors()); + + [Fact] + public async Task Decorates_local_and_remote_mentions_and_hashtags() + { + var rendered = await _renderer.Markdown("hello @alice and @bob@m.example about #Fedi **now** ", TestContext.Current.CancellationToken); + + Assert.Contains("now", rendered.Html); + Assert.DoesNotContain("