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
@@ -169,6 +169,51 @@ namespace PrivaPub.Tests.Federation
|
||||
Assert.Equal("https://loops.example/poster.jpg", loop.CoverURL);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Keeps_custom_emoji_and_drops_ones_that_could_not_render()
|
||||
{
|
||||
var note = Parse("""
|
||||
{
|
||||
"id": "https://k.example/notes/1", "type": "Note", "attributedTo": "https://k.example/users/1", "content": ":blobcat: hi :bad:",
|
||||
"tag": [
|
||||
{ "type": "Emoji", "name": ":blobcat:", "updated": "2026-01-01T00:00:00Z", "icon": { "type": "Image", "mediaType": "image/png", "url": "https://k.example/emoji/blobcat.png" } },
|
||||
{ "type": "Emoji", "name": ":blobcat:", "icon": { "type": "Image", "url": "https://k.example/emoji/duplicate.png" } },
|
||||
{ "type": "Emoji", "name": ":bad name:", "icon": { "type": "Image", "url": "https://k.example/emoji/x.png" } },
|
||||
{ "type": "Emoji", "name": ":nourl:", "icon": { "type": "Image", "url": "data:image/png;base64,AAAA" } }
|
||||
]
|
||||
}
|
||||
""");
|
||||
|
||||
var emoji = Assert.Single(note.Emojis);
|
||||
Assert.Equal("blobcat", emoji.Shortcode);
|
||||
Assert.Equal("https://k.example/emoji/blobcat.png", emoji.URL);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Reads_polls_from_mastodon_misskey_and_closed_ones()
|
||||
{
|
||||
var mastodon = Parse("""
|
||||
{ "id": "https://m.example/q/1", "type": "Question", "attributedTo": "https://m.example/u", "content": "?", "endTime": "2026-10-02T00:00:00Z", "votersCount": 7,
|
||||
"oneOf": [{ "type": "Note", "name": "a", "replies": { "type": "Collection", "totalItems": 4 } }, { "type": "Note", "name": "b", "replies": { "type": "Collection", "totalItems": 3 } }] }
|
||||
""");
|
||||
var misskey = Parse("""
|
||||
{ "id": "https://k.example/q/1", "type": "Question", "attributedTo": "https://k.example/u", "_misskey_content": "?",
|
||||
"anyOf": [{ "type": "Note", "name": "x", "_misskey_votes": 2 }, { "type": "Note", "name": "y", "_misskey_votes": 5 }] }
|
||||
""");
|
||||
var closed = Parse("""
|
||||
{ "id": "https://g.example/q/1", "type": "Question", "attributedTo": "https://g.example/u", "content": "?", "closed": "2026-09-30T12:00:00Z",
|
||||
"oneOf": [{ "type": "Note", "name": "p" }, { "type": "Note", "name": "q" }] }
|
||||
""");
|
||||
|
||||
Assert.False(mastodon.Poll.Multiple);
|
||||
Assert.Equal(new[] { 4, 3 }, mastodon.Poll.Options.Select(o => o.Votes));
|
||||
Assert.Equal(7, mastodon.Poll.VotersCount);
|
||||
Assert.True(misskey.Poll.Multiple);
|
||||
Assert.Equal(new[] { 2, 5 }, misskey.Poll.Options.Select(o => o.Votes));
|
||||
Assert.Null(misskey.Poll.VotersCount);
|
||||
Assert.Equal(new DateTime(2026, 9, 30, 12, 0, 0, DateTimeKind.Utc), closed.Poll.ClosedAt);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Normalises_hashtags_like_mastodon() =>
|
||||
Assert.Equal("fedi".Normalize(System.Text.NormalizationForm.FormKC).ToLowerInvariant(), Assert.Single(Parse("""
|
||||
|
||||
Reference in new issue
Block a user