mirror of
https://github.com/svemagie/indiekit-endpoint-activitypub.git
synced 2026-04-02 15:44:58 +02:00
feat: visibility/CW compose controls, @mention support (v2.11.0)
Add visibility and content warning controls to the reply compose form. Add @user@domain mention parsing, WebFinger resolution, Mention tags, inbox delivery, and content linkification for outbound posts. Confab-Link: http://localhost:8080/sessions/cc343b15-8d10-43cd-a48f-ca912eb79b83
This commit is contained in:
61
index.js
61
index.js
@@ -8,6 +8,7 @@ import {
|
||||
import {
|
||||
jf2ToActivityStreams,
|
||||
jf2ToAS2Activity,
|
||||
parseMentions,
|
||||
} from "./lib/jf2-to-as2.js";
|
||||
import { dashboardController } from "./lib/controllers/dashboard.js";
|
||||
import {
|
||||
@@ -467,6 +468,40 @@ export default class ActivityPubEndpoint {
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve @user@domain mentions in content via WebFinger
|
||||
const contentText = properties.content?.html || properties.content || "";
|
||||
const mentionHandles = parseMentions(contentText);
|
||||
const resolvedMentions = [];
|
||||
const mentionRecipients = [];
|
||||
|
||||
for (const { handle } of mentionHandles) {
|
||||
try {
|
||||
const mentionedActor = await ctx.lookupObject(
|
||||
new URL(`acct:${handle}`),
|
||||
);
|
||||
if (mentionedActor?.id) {
|
||||
resolvedMentions.push({
|
||||
handle,
|
||||
actorUrl: mentionedActor.id.href,
|
||||
});
|
||||
mentionRecipients.push({
|
||||
handle,
|
||||
actorUrl: mentionedActor.id.href,
|
||||
actor: mentionedActor,
|
||||
});
|
||||
console.info(
|
||||
`[ActivityPub] Resolved mention @${handle} → ${mentionedActor.id.href}`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[ActivityPub] Could not resolve mention @${handle}: ${error.message}`,
|
||||
);
|
||||
// Still add with no actorUrl so it gets a fallback link
|
||||
resolvedMentions.push({ handle, actorUrl: null });
|
||||
}
|
||||
}
|
||||
|
||||
const activity = jf2ToAS2Activity(
|
||||
properties,
|
||||
actorUrl,
|
||||
@@ -475,6 +510,7 @@ export default class ActivityPubEndpoint {
|
||||
replyToActorUrl: replyToActor?.url,
|
||||
replyToActorHandle: replyToActor?.handle,
|
||||
visibility: self.options.defaultVisibility,
|
||||
mentions: resolvedMentions,
|
||||
},
|
||||
);
|
||||
|
||||
@@ -529,12 +565,35 @@ export default class ActivityPubEndpoint {
|
||||
}
|
||||
}
|
||||
|
||||
// Deliver to mentioned actors' inboxes (skip reply-to author, already delivered above)
|
||||
for (const { handle: mHandle, actorUrl: mUrl, actor: mActor } of mentionRecipients) {
|
||||
if (replyToActor?.url === mUrl) continue;
|
||||
try {
|
||||
await ctx.sendActivity(
|
||||
{ identifier: handle },
|
||||
mActor,
|
||||
activity,
|
||||
{ orderingKey: properties.url },
|
||||
);
|
||||
console.info(
|
||||
`[ActivityPub] Mention delivered to @${mHandle}: ${mUrl}`,
|
||||
);
|
||||
} catch (error) {
|
||||
console.warn(
|
||||
`[ActivityPub] Failed to deliver mention to @${mHandle}: ${error.message}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine activity type name
|
||||
const typeName =
|
||||
activity.constructor?.name || "Create";
|
||||
const replyNote = replyToActor
|
||||
? ` (reply to ${replyToActor.url})`
|
||||
: "";
|
||||
const mentionNote = mentionRecipients.length > 0
|
||||
? ` (mentions: ${mentionRecipients.map(m => `@${m.handle}`).join(", ")})`
|
||||
: "";
|
||||
|
||||
await logActivity(self._collections.ap_activities, {
|
||||
direction: "outbound",
|
||||
@@ -542,7 +601,7 @@ export default class ActivityPubEndpoint {
|
||||
actorUrl: self._publicationUrl,
|
||||
objectUrl: properties.url,
|
||||
targetUrl: properties["in-reply-to"] || undefined,
|
||||
summary: `Sent ${typeName} for ${properties.url} to ${followerCount} followers${replyNote}`,
|
||||
summary: `Sent ${typeName} for ${properties.url} to ${followerCount} followers${replyNote}${mentionNote}`,
|
||||
});
|
||||
|
||||
console.info(
|
||||
|
||||
Reference in New Issue
Block a user