mirror of
https://github.com/svemagie/indiekit-endpoint-activitypub.git
synced 2026-04-02 15:44:58 +02:00
feat: add own posts to ap_timeline after syndication
Own Micropub posts weren't appearing in Mastodon Client API timelines (Phanpy/Moshidon) because there was no mechanism to add them to ap_timeline. The inbox round-trip doesn't work (we don't follow ourselves) and startup backfill only runs once. Now the AP syndicator adds the post to ap_timeline after successful delivery, using addTimelineItem ($setOnInsert — idempotent). Content flows directly from Micropub properties with proper HTML links.
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
|||||||
} from "./jf2-to-as2.js";
|
} from "./jf2-to-as2.js";
|
||||||
import { lookupWithSecurity } from "./lookup-helpers.js";
|
import { lookupWithSecurity } from "./lookup-helpers.js";
|
||||||
import { logActivity } from "./activity-log.js";
|
import { logActivity } from "./activity-log.js";
|
||||||
|
import { addTimelineItem } from "./storage/timeline.js";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create the ActivityPub syndicator object.
|
* Create the ActivityPub syndicator object.
|
||||||
@@ -219,6 +220,54 @@ export function createSyndicator(plugin) {
|
|||||||
`[ActivityPub] Syndication queued: ${typeName} for ${properties.url}${replyNote}`,
|
`[ActivityPub] Syndication queued: ${typeName} for ${properties.url}${replyNote}`,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Add own post to ap_timeline so it appears in Mastodon Client API
|
||||||
|
// timelines (Phanpy/Moshidon). Uses $setOnInsert — idempotent.
|
||||||
|
try {
|
||||||
|
const profile = await plugin._collections.ap_profile?.findOne({});
|
||||||
|
const content = normalizeContent(properties.content);
|
||||||
|
const timelineItem = {
|
||||||
|
uid: properties.url,
|
||||||
|
url: properties.url,
|
||||||
|
type: mapPostType(properties["post-type"]),
|
||||||
|
content,
|
||||||
|
author: {
|
||||||
|
name: profile?.name || handle,
|
||||||
|
url: profile?.url || plugin._publicationUrl,
|
||||||
|
photo: profile?.icon || "",
|
||||||
|
handle: `@${handle}`,
|
||||||
|
emojis: [],
|
||||||
|
bot: false,
|
||||||
|
},
|
||||||
|
published: properties.published || new Date().toISOString(),
|
||||||
|
createdAt: new Date().toISOString(),
|
||||||
|
visibility: properties.visibility || "public",
|
||||||
|
sensitive: properties.sensitive === "true",
|
||||||
|
category: Array.isArray(properties.category)
|
||||||
|
? properties.category
|
||||||
|
: properties.category ? [properties.category] : [],
|
||||||
|
photo: normalizeMedia(properties.photo, plugin._publicationUrl),
|
||||||
|
video: normalizeMedia(properties.video, plugin._publicationUrl),
|
||||||
|
audio: normalizeMedia(properties.audio, plugin._publicationUrl),
|
||||||
|
counts: { replies: 0, boosts: 0, likes: 0 },
|
||||||
|
};
|
||||||
|
if (properties.name) timelineItem.name = properties.name;
|
||||||
|
if (properties.summary) timelineItem.summary = properties.summary;
|
||||||
|
if (properties["content-warning"]) {
|
||||||
|
timelineItem.summary = properties["content-warning"];
|
||||||
|
timelineItem.sensitive = true;
|
||||||
|
}
|
||||||
|
if (properties["in-reply-to"]) {
|
||||||
|
timelineItem.inReplyTo = Array.isArray(properties["in-reply-to"])
|
||||||
|
? properties["in-reply-to"][0]
|
||||||
|
: properties["in-reply-to"];
|
||||||
|
}
|
||||||
|
await addTimelineItem(plugin._collections, timelineItem);
|
||||||
|
} catch (tlError) {
|
||||||
|
console.warn(
|
||||||
|
`[ActivityPub] Failed to add own post to timeline: ${tlError.message}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return properties.url || undefined;
|
return properties.url || undefined;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("[ActivityPub] Syndication failed:", error.message);
|
console.error("[ActivityPub] Syndication failed:", error.message);
|
||||||
@@ -237,3 +286,35 @@ export function createSyndicator(plugin) {
|
|||||||
update: async (properties) => plugin.update(properties),
|
update: async (properties) => plugin.update(properties),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Timeline helpers ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function normalizeContent(content) {
|
||||||
|
if (!content) return { text: "", html: "" };
|
||||||
|
if (typeof content === "string") return { text: content, html: `<p>${content}</p>` };
|
||||||
|
return {
|
||||||
|
text: content.text || content.value || "",
|
||||||
|
html: content.html || content.text || content.value || "",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function mapPostType(postType) {
|
||||||
|
if (postType === "article") return "article";
|
||||||
|
if (postType === "repost") return "boost";
|
||||||
|
return "note";
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeMedia(value, siteUrl) {
|
||||||
|
if (!value) return [];
|
||||||
|
const base = siteUrl?.replace(/\/$/, "") || "";
|
||||||
|
const arr = Array.isArray(value) ? value : [value];
|
||||||
|
return arr.map((item) => {
|
||||||
|
if (typeof item === "string") {
|
||||||
|
return item.startsWith("http") ? item : `${base}/${item.replace(/^\//, "")}`;
|
||||||
|
}
|
||||||
|
if (item?.url && !item.url.startsWith("http")) {
|
||||||
|
return { ...item, url: `${base}/${item.url.replace(/^\//, "")}` };
|
||||||
|
}
|
||||||
|
return item;
|
||||||
|
}).filter(Boolean);
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@rmdes/indiekit-endpoint-activitypub",
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
||||||
"version": "3.10.0",
|
"version": "3.10.1",
|
||||||
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"indiekit",
|
"indiekit",
|
||||||
|
|||||||
Reference in New Issue
Block a user