feat: deliver likes as bookmarks, revert announce cc, add OG images
- Likes are now sent as Create/Note with bookmark-style content (🔖) instead of Like activities, ensuring proper display on Mastodon - Announce activities reverted to upstream addressing (to: Public only, no cc:followers) - Add per-post OG image to both plain JSON-LD and Fedify Note/Article objects, derived from the post URL pattern (/og/{date}-{slug}.png) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+27
-38
@@ -14,7 +14,6 @@ import {
|
||||
Create,
|
||||
Hashtag,
|
||||
Image,
|
||||
Like,
|
||||
Mention,
|
||||
Note,
|
||||
Video,
|
||||
@@ -95,24 +94,7 @@ function linkifyMentions(html, resolvedMentions) {
|
||||
export function jf2ToActivityStreams(properties, actorUrl, publicationUrl, options = {}) {
|
||||
const postType = properties["post-type"];
|
||||
|
||||
if (postType === "like") {
|
||||
// Serve like posts as Note objects for AP content negotiation.
|
||||
// Returning a bare Like activity breaks Mastodon's authorize_interaction
|
||||
// flow because it expects a content object (Note/Article), not an activity.
|
||||
const likeOf = properties["like-of"];
|
||||
const postUrl = resolvePostUrl(properties.url, publicationUrl);
|
||||
return {
|
||||
"@context": "https://www.w3.org/ns/activitystreams",
|
||||
type: "Note",
|
||||
id: postUrl,
|
||||
attributedTo: actorUrl,
|
||||
published: properties.published,
|
||||
url: postUrl,
|
||||
to: ["https://www.w3.org/ns/activitystreams#Public"],
|
||||
cc: [`${actorUrl.replace(/\/$/, "")}/followers`],
|
||||
content: `\u2764\uFE0F <a href="${likeOf}">${likeOf}</a>`,
|
||||
};
|
||||
}
|
||||
// Likes are delivered as bookmarks — fall through to bookmark handling below
|
||||
|
||||
// Reposts are always public — Mastodon and other implementations expect this
|
||||
if (postType === "repost") {
|
||||
@@ -156,8 +138,8 @@ export function jf2ToActivityStreams(properties, actorUrl, publicationUrl, optio
|
||||
: [followersUrl],
|
||||
};
|
||||
|
||||
if (postType === "bookmark") {
|
||||
const bookmarkUrl = properties["bookmark-of"];
|
||||
if (postType === "bookmark" || postType === "like") {
|
||||
const bookmarkUrl = properties["bookmark-of"] || properties["like-of"];
|
||||
const commentary = linkifyUrls(properties.content?.html || properties.content || "");
|
||||
object.content = commentary
|
||||
? `${commentary}<br><br>\u{1F516} <a href="${bookmarkUrl}">${bookmarkUrl}</a>`
|
||||
@@ -178,6 +160,16 @@ export function jf2ToActivityStreams(properties, actorUrl, publicationUrl, optio
|
||||
object.content += `<p>\u{1F517} <a href="${postUrl}">${postUrl}</a></p>`;
|
||||
}
|
||||
|
||||
// OG image for fediverse preview cards
|
||||
const ogMatch = postUrl && postUrl.match(/\/([\w-]+)\/(\d{4})\/(\d{2})\/(\d{2})\/([\w-]+)\/?$/);
|
||||
if (ogMatch) {
|
||||
object.image = {
|
||||
type: "Image",
|
||||
url: `${publicationUrl.replace(/\/$/, "")}/og/${ogMatch[2]}-${ogMatch[3]}-${ogMatch[4]}-${ogMatch[5]}.png`,
|
||||
mediaType: "image/png",
|
||||
};
|
||||
}
|
||||
|
||||
if (isArticle) {
|
||||
object.name = properties.name;
|
||||
if (properties.summary) {
|
||||
@@ -253,28 +245,16 @@ export function jf2ToAS2Activity(properties, actorUrl, publicationUrl, options =
|
||||
const postType = properties["post-type"];
|
||||
const actorUri = new URL(actorUrl);
|
||||
|
||||
if (postType === "like") {
|
||||
const likeOf = properties["like-of"];
|
||||
if (!likeOf) return null;
|
||||
const followersUrl = `${actorUrl.replace(/\/$/, "")}/followers`;
|
||||
return new Like({
|
||||
actor: actorUri,
|
||||
object: new URL(likeOf),
|
||||
to: new URL("https://www.w3.org/ns/activitystreams#Public"),
|
||||
cc: new URL(followersUrl),
|
||||
});
|
||||
}
|
||||
// Likes are delivered as bookmarks — fall through to bookmark handling below
|
||||
|
||||
// Reposts are always public — Mastodon and other implementations expect this
|
||||
// Reposts are always public — upstream @rmdes addressing
|
||||
if (postType === "repost") {
|
||||
const repostOf = properties["repost-of"];
|
||||
if (!repostOf) return null;
|
||||
const followersUrl = `${actorUrl.replace(/\/$/, "")}/followers`;
|
||||
return new Announce({
|
||||
actor: actorUri,
|
||||
object: new URL(repostOf),
|
||||
to: new URL("https://www.w3.org/ns/activitystreams#Public"),
|
||||
cc: new URL(followersUrl),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -336,8 +316,8 @@ export function jf2ToAS2Activity(properties, actorUrl, publicationUrl, options =
|
||||
}
|
||||
|
||||
// Content
|
||||
if (postType === "bookmark") {
|
||||
const bookmarkUrl = properties["bookmark-of"];
|
||||
if (postType === "bookmark" || postType === "like") {
|
||||
const bookmarkUrl = properties["bookmark-of"] || properties["like-of"];
|
||||
const commentary = linkifyUrls(properties.content?.html || properties.content || "");
|
||||
noteOptions.content = commentary
|
||||
? `${commentary}<br><br>\u{1F516} <a href="${bookmarkUrl}">${bookmarkUrl}</a>`
|
||||
@@ -382,6 +362,15 @@ export function jf2ToAS2Activity(properties, actorUrl, publicationUrl, options =
|
||||
noteOptions.attachments = fedifyAttachments;
|
||||
}
|
||||
|
||||
// OG image for fediverse preview cards
|
||||
const ogMatchF = postUrl && postUrl.match(/\/([\w-]+)\/(\d{4})\/(\d{2})\/(\d{2})\/([\w-]+)\/?$/);
|
||||
if (ogMatchF) {
|
||||
noteOptions.image = new Image({
|
||||
url: new URL(`${publicationUrl.replace(/\/$/, "")}/og/${ogMatchF[2]}-${ogMatchF[3]}-${ogMatchF[4]}-${ogMatchF[5]}.png`),
|
||||
mediaType: "image/png",
|
||||
});
|
||||
}
|
||||
|
||||
// Tags: hashtags + Mention for reply addressing + @mentions
|
||||
const fedifyTags = buildFedifyTags(properties, publicationUrl, postType);
|
||||
|
||||
@@ -575,7 +564,7 @@ function buildPlainTags(properties, publicationUrl, existing) {
|
||||
|
||||
function buildFedifyTags(properties, publicationUrl, postType) {
|
||||
const tags = [];
|
||||
if (postType === "bookmark") {
|
||||
if (postType === "bookmark" || postType === "like") {
|
||||
tags.push(
|
||||
new Hashtag({
|
||||
name: "#bookmark",
|
||||
|
||||
Reference in New Issue
Block a user