Changeset dd7daef in Klonkt
- Timestamp:
- 06/27/2026 12:15:56 PM (2 months ago)
- Branches:
- main
- Children:
- 04c70fc
- Parents:
- bddbfe0
- File:
-
- 1 edited
-
src/services/ActivityPubService.js (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
src/services/ActivityPubService.js
rbddbfe0 rdd7daef 931 931 } 932 932 933 // Extract Mention tag objects from already-linked content (class="u-url mention"). 934 function mentionTags(content) { 935 const tags = [], seen = new Set(); 936 const re = /<a href="([^"]+)" class="u-url mention">@([^<]+)<\/a>/gi; 937 let m; 938 while ((m = re.exec(content || ''))) { 939 const href = m[1]; 940 if (seen.has(href)) continue; seen.add(href); 941 tags.push({ type: 'Mention', href, name: '@' + m[2] }); 942 } 943 return tags; 944 } 945 // Resolve inline @user@domain mentions in reply/post text → link them (href = actor URI) 946 // and collect the mentioned actors' inboxes so they get notified. Best-effort per mention. 947 async function resolveMentionsInText(base, html) { 948 const inboxes = []; 949 const handles = new Set(); 950 const re = /(^|[\s>])@([A-Za-z0-9_.-]+@[A-Za-z0-9.-]+)/g; 951 let m; 952 while ((m = re.exec(html || ''))) handles.add(m[2]); 953 let out = String(html || ''); 954 for (const h of handles) { 955 let actorUri = null; 956 try { actorUri = await webfingerResolve('@' + h); } catch { actorUri = null; } 957 if (!actorUri) continue; 958 const actor = await fetchActor(actorUri).catch(() => null); 959 const inbox = actor && ((actor.endpoints && actor.endpoints.sharedInbox) || actor.inbox); 960 if (inbox) inboxes.push(inbox); 961 const esc = h.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); 962 out = out.replace(new RegExp('(^|[\\s>])@' + esc + '(?![A-Za-z0-9_.-])', 'g'), 963 (full, pre) => `${pre}<a href="${actorUri}" class="u-url mention">@${h}</a>`); 964 } 965 return { html: out, inboxes }; 966 } 967 933 968 export function buildReplyNote(base, site, row) { 934 969 const me = actorId(base, site.slug); … … 944 979 cc: [PUBLIC, `${me}/followers`], 945 980 tag: [ 946 ... (row.to_actor ? [{ type: 'Mention', href: row.to_actor, name: row.to_handle }] : []),981 ...mentionTags(row.content), 947 982 ...hashtagTags(base, row.content), 948 983 ], … … 966 1001 const me = actorId(base, site.slug); 967 1002 const handle = parent.actor_handle || deriveHandle(parent.actor_uri); 1003 const dispHandle = handle && handle[0] === '@' ? handle : '@' + (handle || ''); 968 1004 const body = escHtml(String(text).trim()).replace(/\r?\n/g, '<br>'); 1005 const mres = await resolveMentionsInText(base, body); // link inline @mentions + collect their inboxes 969 1006 const mention = parent.actor_uri 970 ? `<a href="${escHtml(parent.actor_ur l || parent.actor_uri)}" class="u-url mention">${escHtml(handle)}</a> ` : '';971 const content = `<p>${mention}${linkHashtags(base, body)}</p>`;1007 ? `<a href="${escHtml(parent.actor_uri || parent.actor_url)}" class="u-url mention">${escHtml(dispHandle)}</a> ` : ''; 1008 const content = `<p>${mention}${linkHashtags(base, mres.html)}</p>`; 972 1009 // Dedup: skip if the exact same reply was already sent (double-submit guard). 973 1010 const dup = db.prepare('SELECT 1 FROM ap_outbox WHERE site_slug = ? AND IFNULL(in_reply_to, \'\') = ? AND content = ? LIMIT 1') … … 993 1030 (parent.threadInboxes || []).forEach((i) => inboxes.add(i)); // whole ancestor chain 994 1031 for (const f of fStmts().list.all(site.slug)) inboxes.add(f.shared_inbox || f.inbox); 1032 mres.inboxes.forEach((i) => inboxes.add(i)); // people @mentioned inline in the reply 995 1033 inboxes.delete(`${me}/inbox`); // never deliver to ourselves (already in ap_outbox) 996 1034 inboxes.delete(`${base}/ap/inbox`); // (our own shared inbox) → avoids a self-duplicate … … 1122 1160 const mention = row.to_actor 1123 1161 ? `<a href="${escHtml(row.to_actor)}" class="u-url mention">${escHtml(row.to_handle || deriveHandle(row.to_actor))}</a> ` : ''; 1124 const body = escHtml(text).replace(/\r?\n/g, '<br>');1125 const content = `<p>${mention}${linkHashtags(base, body)}</p>`;1162 const mres = await resolveMentionsInText(base, escHtml(text).replace(/\r?\n/g, '<br>')); 1163 const content = `<p>${mention}${linkHashtags(base, mres.html)}</p>`; 1126 1164 db.prepare('UPDATE ap_outbox SET content = ? WHERE id = ?').run(content, outboxId); 1127 1165 const note = buildReplyNote(base, site, iStmts().getO.get(outboxId));
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)