@nodebb/nodebb-plugin-reactions 3.1.3 → 3.1.5

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/helpers.js CHANGED
@@ -46,10 +46,6 @@ function resolveByName(name) {
46
46
  return null;
47
47
  }
48
48
 
49
- /**
50
- * Resolve FEP-c0e0 reaction content (unicode grapheme, `:shortcode:`, bare name,
51
- * or a custom-emoji `tag`) to a local emoji name. Returns null when unresolvable.
52
- */
53
49
  function resolveReaction(content, tag) {
54
50
  if (typeof content !== 'string' || !content.trim()) {
55
51
  return null;
@@ -90,7 +86,13 @@ function resolveReaction(content, tag) {
90
86
  if (!reaction && Array.isArray(tag)) {
91
87
  const emojiTag = tag.find(t => t && t.type === 'Emoji' && typeof t.name === 'string');
92
88
  if (emojiTag) {
93
- reaction = resolveByName(emojiTag.name.replace(/^:|:$/g, ''));
89
+ const name = emojiTag.name.replace(/^:|:$/g, '');
90
+ // Try to resolve as a local emoji first
91
+ reaction = resolveByName(name);
92
+ // If not local, return the tag so the caller can cache the custom emoji
93
+ if (!reaction) {
94
+ return { emoji: name, emojiTag };
95
+ }
94
96
  }
95
97
  }
96
98
 
package/library.js CHANGED
@@ -30,6 +30,49 @@ function parse(name) {
30
30
  return emoji ? emojiParser.buildEmoji(emoji, '') : '';
31
31
  }
32
32
 
33
+ async function parseReaction(name, sourceUrl) {
34
+ const emoji = nameToEmoji(name) || helpers.getEmojiTable()[helpers.getEmojiAliases()[name]];
35
+ if (emoji) {
36
+ return emojiParser.buildEmoji(emoji, '');
37
+ }
38
+
39
+ // Try to find the custom emoji: first by source URL hostname, then by scanning emoji:ap:lookup
40
+ let hostname = null;
41
+ if (typeof sourceUrl === 'string') {
42
+ try {
43
+ hostname = new URL(sourceUrl).hostname;
44
+ } catch (e) {
45
+ // Not a valid URL (e.g. numeric pid)
46
+ }
47
+ }
48
+
49
+ let metadata = null;
50
+ if (hostname) {
51
+ metadata = await activitypub.emoji.getEmoji(name, hostname);
52
+ }
53
+
54
+ // Fallback: scan emoji:ap:lookup for any entry matching this shortcode
55
+ if (!metadata) {
56
+ const allKeys = await db.getObjectKeys('emoji:ap:lookup');
57
+ if (allKeys && allKeys.length > 0) {
58
+ const matchKey = allKeys.find(k => k.startsWith(`${name}:`));
59
+ if (matchKey) {
60
+ const idx = matchKey.indexOf(':');
61
+ if (idx !== -1) {
62
+ hostname = matchKey.slice(idx + 1);
63
+ metadata = await activitypub.emoji.getEmoji(name, hostname);
64
+ }
65
+ }
66
+ }
67
+ }
68
+
69
+ if (metadata) {
70
+ const proxyUrl = activitypub.emoji.getProxyUrl(name, hostname);
71
+ return `<img class="not-responsive emoji" src="${proxyUrl}" title=":${name}:" />`;
72
+ }
73
+ return '';
74
+ }
75
+
33
76
  const ReactionsPlugin = module.exports;
34
77
 
35
78
  ReactionsPlugin.init = async function (params) {
@@ -150,7 +193,7 @@ ReactionsPlugin.getPostReactions = async function (data) {
150
193
  for (const reaction of reactions) {
151
194
  const reactionSet = `pid:${post.pid}:reaction:${reaction}`;
152
195
  const uids = reactionSetToUsersMap.get(reactionSet);
153
- const reactionImage = parse(reaction);
196
+ const reactionImage = await parseReaction(reaction, post.pid);
154
197
  if (Array.isArray(uids) && reactionImage) {
155
198
  post.reactions.push({
156
199
  pid: post.pid,
@@ -215,7 +258,7 @@ ReactionsPlugin.getMessageReactions = async function (data) {
215
258
  for (const reaction of reactions) {
216
259
  const reactionSet = `mid:${msg.mid}:reaction:${reaction}`;
217
260
  const uids = reactionSetToUsersMap.get(reactionSet);
218
- const reactionImage = parse(reaction);
261
+ const reactionImage = await parseReaction(reaction, msg.mid);
219
262
  if (Array.isArray(uids) && reactionImage) {
220
263
  msg.reactions.push({
221
264
  mid: msg.mid,
@@ -290,7 +333,7 @@ async function sendPostEvent(data, eventName) {
290
333
  reaction: data.reaction,
291
334
  reactionCount,
292
335
  totalReactions,
293
- reactionImage: parse(data.reaction),
336
+ reactionImage: await parseReaction(data.reaction, data.pid),
294
337
  });
295
338
  } catch (e) {
296
339
  console.error(e);
@@ -314,7 +357,7 @@ async function sendMessageEvent(data, eventName) {
314
357
  reaction: data.reaction,
315
358
  reactionCount,
316
359
  totalReactions,
317
- reactionImage: parse(data.reaction),
360
+ reactionImage: await parseReaction(data.reaction, data.mid),
318
361
  });
319
362
  } catch (e) {
320
363
  console.error(e);
@@ -501,13 +544,36 @@ ReactionsPlugin.applyEmojiReact = async function (activity) {
501
544
 
502
545
  const id = await resolveReactionPost(object);
503
546
  if (!id) {
504
- return;
547
+ return false;
505
548
  }
506
549
 
507
- const reaction = helpers.resolveReaction(content, tag);
508
- if (!reaction) {
550
+ const resolved = helpers.resolveReaction(content, tag);
551
+ if (!resolved) {
509
552
  activitypub.helpers.log(`[reactions/ap] Unresolvable reaction content (${JSON.stringify(content)}), ignoring.`);
510
- return;
553
+ return false;
554
+ }
555
+
556
+ let reaction;
557
+ let emojiTag;
558
+ if (typeof resolved === 'object' && resolved.emoji) {
559
+ reaction = resolved.emoji;
560
+ emojiTag = resolved.emojiTag;
561
+ } else {
562
+ reaction = resolved;
563
+ }
564
+
565
+ if (emojiTag) {
566
+ const cacheTag = { name: emojiTag.name };
567
+ if (emojiTag.icon && emojiTag.icon.url) {
568
+ cacheTag.icon = emojiTag.icon;
569
+ } else if (emojiTag.id) {
570
+ cacheTag.icon = { url: emojiTag.id, mediaType: emojiTag.mediaType || null };
571
+ } else {
572
+ cacheTag = null;
573
+ }
574
+ if (cacheTag) {
575
+ await activitypub.emoji.cacheEmoji(cacheTag);
576
+ }
511
577
  }
512
578
 
513
579
  const allowed = await privileges.posts.can('posts:upvote', id, activitypub._constants.uid);
@@ -519,6 +585,7 @@ ReactionsPlugin.applyEmojiReact = async function (activity) {
519
585
  activitypub.helpers.log(`[reactions/ap] id ${id} (${reaction}) via ${actor}`);
520
586
  await ReactionsPlugin.addPostReaction(id, actor, reaction);
521
587
  await activitypub.feps.announce(object.id, activity);
588
+ return true;
522
589
  };
523
590
 
524
591
  ReactionsPlugin.undoEmojiReact = async function (activity) {
@@ -526,19 +593,22 @@ ReactionsPlugin.undoEmojiReact = async function (activity) {
526
593
 
527
594
  const id = await resolveReactionPost(object);
528
595
  if (!id) {
529
- return;
596
+ return false;
530
597
  }
531
598
 
532
- const reaction = helpers.resolveReaction(content, tag);
533
- if (!reaction) {
599
+ const resolved = helpers.resolveReaction(content, tag);
600
+ if (!resolved) {
534
601
  activitypub.helpers.log(`[reactions/ap] Unresolvable reaction content in undo, ignoring.`);
535
- return;
602
+ return false;
536
603
  }
537
604
 
605
+ const reaction = typeof resolved === 'object' ? resolved.emoji : resolved;
606
+
538
607
  activitypub.helpers.log(`[reactions/ap] undo id ${id} (${reaction}) via ${actor}`);
539
608
  await ReactionsPlugin.removePostReaction(id, actor, reaction);
540
609
  await ReactionsPlugin.rescindPostReaction(id, actor, reaction);
541
610
  await activitypub.feps.announce(object.id, activity);
611
+ return true;
542
612
  };
543
613
 
544
614
  ReactionsPlugin.handleEmojiReact = async function (context) {
@@ -547,10 +617,11 @@ ReactionsPlugin.handleEmojiReact = async function (context) {
547
617
  };
548
618
 
549
619
  ReactionsPlugin.handleLike = async function (context) {
550
- // FEP-c0e0: a Like with content is an emoji reaction; a plain Like falls through to core
551
620
  if (typeof context.activity.content === 'string' && context.activity.content.trim()) {
552
- await ReactionsPlugin.applyEmojiReact(context.activity);
553
- return { ...context, claimed: true };
621
+ const applied = await ReactionsPlugin.applyEmojiReact(context.activity);
622
+ if (applied) {
623
+ return { ...context, claimed: true };
624
+ }
554
625
  }
555
626
  return context;
556
627
  };
@@ -615,8 +686,11 @@ ReactionsPlugin.handleAnnounce = async function (context) {
615
686
  }
616
687
  }
617
688
 
618
- await ReactionsPlugin.applyEmojiReact(object);
619
- return { ...context, claimed: true };
689
+ const applied = await ReactionsPlugin.applyEmojiReact(object);
690
+ if (applied) {
691
+ return { ...context, claimed: true };
692
+ }
693
+ return context;
620
694
  };
621
695
 
622
696
  SocketPlugins.reactions = {