@rmdes/indiekit-endpoint-activitypub 3.12.1 → 3.12.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/mastodon/entities/status.js +23 -8
- package/lib/syndicator.js +28 -7
- package/package.json +1 -1
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
*/
|
|
16
16
|
import { serializeAccount } from "./account.js";
|
|
17
17
|
import { sanitizeHtml } from "./sanitize.js";
|
|
18
|
+
import { remoteActorId } from "../helpers/id-mapping.js";
|
|
18
19
|
|
|
19
20
|
// Module-level defaults set once at startup via setLocalIdentity()
|
|
20
21
|
let _localPublicationUrl = "";
|
|
@@ -99,7 +100,17 @@ export function serializeStatus(item, { baseUrl, favouritedIds, rebloggedIds, bo
|
|
|
99
100
|
}
|
|
100
101
|
|
|
101
102
|
// Regular status (note, article, question)
|
|
102
|
-
|
|
103
|
+
let content = item.content?.html || item.content?.text || "";
|
|
104
|
+
|
|
105
|
+
// Append permalink for own posts at read time — matches what fediverse
|
|
106
|
+
// users see via federation (jf2-to-as2 appends the same link).
|
|
107
|
+
// Done here instead of at write time so it survives backfills and cleanups.
|
|
108
|
+
const isOwnPost = _localPublicationUrl && item.author?.url === _localPublicationUrl;
|
|
109
|
+
const postUrl = item.uid || item.url;
|
|
110
|
+
if (isOwnPost && postUrl && !content.includes(postUrl)) {
|
|
111
|
+
const escaped = postUrl.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
|
112
|
+
content += `\n<p>\u{1F517} <a href="${escaped}">${escaped}</a></p>`;
|
|
113
|
+
}
|
|
103
114
|
const spoilerText = item.summary || "";
|
|
104
115
|
const sensitive = item.sensitive || false;
|
|
105
116
|
const visibility = item.visibility || "public";
|
|
@@ -168,13 +179,17 @@ export function serializeStatus(item, { baseUrl, favouritedIds, rebloggedIds, bo
|
|
|
168
179
|
url: `${baseUrl}/tags/${encodeURIComponent(tag)}`,
|
|
169
180
|
}));
|
|
170
181
|
|
|
171
|
-
// Mentions
|
|
172
|
-
const mentions = (item.mentions || []).map((m) =>
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
182
|
+
// Mentions — use actorUrl for deterministic ID, parse acct from handle
|
|
183
|
+
const mentions = (item.mentions || []).map((m) => {
|
|
184
|
+
const handle = (m.name || "").replace(/^@/, "");
|
|
185
|
+
const parts = handle.split("@");
|
|
186
|
+
return {
|
|
187
|
+
id: m.actorUrl ? remoteActorId(m.actorUrl) : "0",
|
|
188
|
+
username: parts[0] || handle,
|
|
189
|
+
url: m.url || m.actorUrl || "",
|
|
190
|
+
acct: handle,
|
|
191
|
+
};
|
|
192
|
+
});
|
|
178
193
|
|
|
179
194
|
// Custom emojis
|
|
180
195
|
const emojis = (item.emojis || []).map((e) => ({
|
package/lib/syndicator.js
CHANGED
|
@@ -225,20 +225,41 @@ export function createSyndicator(plugin) {
|
|
|
225
225
|
try {
|
|
226
226
|
const profile = await plugin._collections.ap_profile?.findOne({});
|
|
227
227
|
const content = buildTimelineContent(properties);
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
//
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
228
|
+
// Permalink is appended at read time by serializeStatus, not here.
|
|
229
|
+
|
|
230
|
+
// Linkify @mentions in content using resolved WebFinger data.
|
|
231
|
+
// This ensures the ap_timeline HTML has proper <a> links for
|
|
232
|
+
// mentions, matching what the federated AS2 activity contains.
|
|
233
|
+
if (resolvedMentions.length > 0 && content.html) {
|
|
234
|
+
const { default: jf2Mod } = await import("./jf2-to-as2.js");
|
|
235
|
+
// Import linkifyMentions — it's not exported, so inline the logic
|
|
236
|
+
for (const { handle, profileUrl, actorUrl } of resolvedMentions) {
|
|
237
|
+
const escaped = handle.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
238
|
+
const pattern = new RegExp(`(?<!["\\/\\w])@${escaped}(?![\\w])`, "gi");
|
|
239
|
+
const parts = handle.split("@");
|
|
240
|
+
const url = profileUrl || (actorUrl ? actorUrl : `https://${parts[1]}/@${parts[0]}`);
|
|
241
|
+
content.html = content.html.replace(
|
|
242
|
+
pattern,
|
|
243
|
+
`<a href="${url}" class="mention" rel="nofollow noopener" target="_blank">@${handle}</a>`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
235
246
|
}
|
|
236
247
|
|
|
248
|
+
// Store resolved mentions for Mastodon API serialization
|
|
249
|
+
const timelineMentions = resolvedMentions
|
|
250
|
+
.filter(m => m.actorUrl)
|
|
251
|
+
.map(m => ({
|
|
252
|
+
name: `@${m.handle}`,
|
|
253
|
+
url: m.profileUrl || m.actorUrl,
|
|
254
|
+
actorUrl: m.actorUrl,
|
|
255
|
+
}));
|
|
256
|
+
|
|
237
257
|
const timelineItem = {
|
|
238
258
|
uid: properties.url,
|
|
239
259
|
url: properties.url,
|
|
240
260
|
type: mapPostType(properties["post-type"]),
|
|
241
261
|
content,
|
|
262
|
+
mentions: timelineMentions,
|
|
242
263
|
author: {
|
|
243
264
|
name: profile?.name || handle,
|
|
244
265
|
url: profile?.url || plugin._publicationUrl,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rmdes/indiekit-endpoint-activitypub",
|
|
3
|
-
"version": "3.12.
|
|
3
|
+
"version": "3.12.3",
|
|
4
4
|
"description": "ActivityPub federation endpoint for Indiekit via Fedify. Adds full fediverse support: actor, inbox, outbox, followers, following, syndication, and Mastodon migration.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"indiekit",
|