P6: custom emoji, fuller remote profiles, and polls both ways
- Custom emoji (Emoji tags) on posts, display names, bios and profile fields, at most 64 per object, proxied, in Status.emojis and Account.emojis. - Remote profiles keep their header, profile fields, locked flag, published date, movedTo, indexable, memorial and image descriptions (a locked GoToSocial account no longer shows as open). - Polls: incoming Questions (Mastodon, Misskey, Pleroma, GoToSocial shapes) with counts, voters, end and closed; our own polls from the Mastodon API go out as Questions; votes in are counted once per voter and never become replies; personas vote on other servers' polls with one Note per choice; counts refresh with an Update at most every three minutes; a poll closes on time and tells its voters and its author. GET /api/v1/polls/:id and POST /api/v1/polls/:id/votes. - An Update without a newer `updated` only refreshes poll, video, audio and event details and leaves no revision. Checked live against GoToSocial: each side's poll reaches the other as a poll and each side's vote is counted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012CzABvBkbcFqoHdmi8b9WB
This commit is contained in:
1 parent
81de470f64
commit
7be6749a23
33 files changed
+937
-28
No files matched your search
@@ -37,6 +37,7 @@ namespace PrivaPub.Federation.Rendering
|
||||
["discoverable"] = "toot:discoverable",
|
||||
["indexable"] = "toot:indexable",
|
||||
["blurhash"] = "toot:blurhash",
|
||||
["votersCount"] = "toot:votersCount",
|
||||
["focalPoint"] = new JsonObject { ["@container"] = "@list", ["@id"] = "toot:focalPoint" },
|
||||
["schema"] = "http://schema.org#",
|
||||
["PropertyValue"] = "schema:PropertyValue",
|
||||
@@ -156,6 +157,23 @@ namespace PrivaPub.Federation.Rendering
|
||||
return note;
|
||||
}
|
||||
|
||||
static void AddPoll(JsonObject note, PostPoll poll)
|
||||
{
|
||||
note["type"] = "Question";
|
||||
note[poll.Multiple ? "anyOf" : "oneOf"] = new JsonArray(poll.Options.Select(option => (JsonNode)new JsonObject
|
||||
{
|
||||
["type"] = "Note",
|
||||
["name"] = option.Title,
|
||||
["replies"] = new JsonObject { ["type"] = "Collection", ["totalItems"] = option.Votes }
|
||||
}).ToArray());
|
||||
if (poll.ExpiresAt is { } ends)
|
||||
note["endTime"] = Timestamp(ends);
|
||||
if (poll.ClosedAt is { } closed)
|
||||
note["closed"] = Timestamp(closed);
|
||||
if (poll.VotersCount is { } voters)
|
||||
note["votersCount"] = voters;
|
||||
}
|
||||
|
||||
static string Headline(PostEntity post)
|
||||
{
|
||||
var text = System.Text.RegularExpressions.Regex.Replace(post.ContentHtml ?? post.Text ?? string.Empty, "<[^>]+>", " ");
|
||||
@@ -192,6 +210,8 @@ namespace PrivaPub.Federation.Rendering
|
||||
};
|
||||
if (!string.IsNullOrEmpty(post.Language))
|
||||
note["contentMap"] = new JsonObject { [post.Language] = content };
|
||||
if (post.Poll is { } poll)
|
||||
AddPoll(note, poll);
|
||||
var attachments = post.Media.Where(m => !string.IsNullOrEmpty(m.URL)).Select(m =>
|
||||
{
|
||||
var attachment = new JsonObject
|
||||
|
||||
Reference in new issue
Block a user