@nodebb/nodebb-plugin-reactions 3.1.3 → 3.1.4

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,28 @@ 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
+ if (typeof sourceUrl === 'string') {
40
+ try {
41
+ const url = new URL(sourceUrl);
42
+ const hostname = url.hostname;
43
+ const metadata = await activitypub.emoji.getEmoji(name, hostname);
44
+ if (metadata) {
45
+ const proxyUrl = activitypub.emoji.getProxyUrl(name, hostname);
46
+ return `<img class="not-responsive emoji" src="${proxyUrl}" title=":${name}:" />`;
47
+ }
48
+ } catch (e) {
49
+ // Not a valid URL — skip custom emoji lookup
50
+ }
51
+ }
52
+ return '';
53
+ }
54
+
33
55
  const ReactionsPlugin = module.exports;
34
56
 
35
57
  ReactionsPlugin.init = async function (params) {
@@ -150,7 +172,7 @@ ReactionsPlugin.getPostReactions = async function (data) {
150
172
  for (const reaction of reactions) {
151
173
  const reactionSet = `pid:${post.pid}:reaction:${reaction}`;
152
174
  const uids = reactionSetToUsersMap.get(reactionSet);
153
- const reactionImage = parse(reaction);
175
+ const reactionImage = await parseReaction(reaction, post.pid);
154
176
  if (Array.isArray(uids) && reactionImage) {
155
177
  post.reactions.push({
156
178
  pid: post.pid,
@@ -215,7 +237,7 @@ ReactionsPlugin.getMessageReactions = async function (data) {
215
237
  for (const reaction of reactions) {
216
238
  const reactionSet = `mid:${msg.mid}:reaction:${reaction}`;
217
239
  const uids = reactionSetToUsersMap.get(reactionSet);
218
- const reactionImage = parse(reaction);
240
+ const reactionImage = await parseReaction(reaction, msg.mid);
219
241
  if (Array.isArray(uids) && reactionImage) {
220
242
  msg.reactions.push({
221
243
  mid: msg.mid,
@@ -290,7 +312,7 @@ async function sendPostEvent(data, eventName) {
290
312
  reaction: data.reaction,
291
313
  reactionCount,
292
314
  totalReactions,
293
- reactionImage: parse(data.reaction),
315
+ reactionImage: await parseReaction(data.reaction, data.pid),
294
316
  });
295
317
  } catch (e) {
296
318
  console.error(e);
@@ -314,7 +336,7 @@ async function sendMessageEvent(data, eventName) {
314
336
  reaction: data.reaction,
315
337
  reactionCount,
316
338
  totalReactions,
317
- reactionImage: parse(data.reaction),
339
+ reactionImage: await parseReaction(data.reaction, data.mid),
318
340
  });
319
341
  } catch (e) {
320
342
  console.error(e);
@@ -501,13 +523,36 @@ ReactionsPlugin.applyEmojiReact = async function (activity) {
501
523
 
502
524
  const id = await resolveReactionPost(object);
503
525
  if (!id) {
504
- return;
526
+ return false;
505
527
  }
506
528
 
507
- const reaction = helpers.resolveReaction(content, tag);
508
- if (!reaction) {
529
+ const resolved = helpers.resolveReaction(content, tag);
530
+ if (!resolved) {
509
531
  activitypub.helpers.log(`[reactions/ap] Unresolvable reaction content (${JSON.stringify(content)}), ignoring.`);
510
- return;
532
+ return false;
533
+ }
534
+
535
+ let reaction;
536
+ let emojiTag;
537
+ if (typeof resolved === 'object' && resolved.emoji) {
538
+ reaction = resolved.emoji;
539
+ emojiTag = resolved.emojiTag;
540
+ } else {
541
+ reaction = resolved;
542
+ }
543
+
544
+ if (emojiTag) {
545
+ const cacheTag = { name: emojiTag.name };
546
+ if (emojiTag.icon && emojiTag.icon.url) {
547
+ cacheTag.icon = emojiTag.icon;
548
+ } else if (emojiTag.id) {
549
+ cacheTag.icon = { url: emojiTag.id, mediaType: emojiTag.mediaType || null };
550
+ } else {
551
+ cacheTag = null;
552
+ }
553
+ if (cacheTag) {
554
+ await activitypub.emoji.cacheEmoji(cacheTag);
555
+ }
511
556
  }
512
557
 
513
558
  const allowed = await privileges.posts.can('posts:upvote', id, activitypub._constants.uid);
@@ -519,6 +564,7 @@ ReactionsPlugin.applyEmojiReact = async function (activity) {
519
564
  activitypub.helpers.log(`[reactions/ap] id ${id} (${reaction}) via ${actor}`);
520
565
  await ReactionsPlugin.addPostReaction(id, actor, reaction);
521
566
  await activitypub.feps.announce(object.id, activity);
567
+ return true;
522
568
  };
523
569
 
524
570
  ReactionsPlugin.undoEmojiReact = async function (activity) {
@@ -526,19 +572,22 @@ ReactionsPlugin.undoEmojiReact = async function (activity) {
526
572
 
527
573
  const id = await resolveReactionPost(object);
528
574
  if (!id) {
529
- return;
575
+ return false;
530
576
  }
531
577
 
532
- const reaction = helpers.resolveReaction(content, tag);
533
- if (!reaction) {
578
+ const resolved = helpers.resolveReaction(content, tag);
579
+ if (!resolved) {
534
580
  activitypub.helpers.log(`[reactions/ap] Unresolvable reaction content in undo, ignoring.`);
535
- return;
581
+ return false;
536
582
  }
537
583
 
584
+ const reaction = typeof resolved === 'object' ? resolved.emoji : resolved;
585
+
538
586
  activitypub.helpers.log(`[reactions/ap] undo id ${id} (${reaction}) via ${actor}`);
539
587
  await ReactionsPlugin.removePostReaction(id, actor, reaction);
540
588
  await ReactionsPlugin.rescindPostReaction(id, actor, reaction);
541
589
  await activitypub.feps.announce(object.id, activity);
590
+ return true;
542
591
  };
543
592
 
544
593
  ReactionsPlugin.handleEmojiReact = async function (context) {
@@ -547,10 +596,11 @@ ReactionsPlugin.handleEmojiReact = async function (context) {
547
596
  };
548
597
 
549
598
  ReactionsPlugin.handleLike = async function (context) {
550
- // FEP-c0e0: a Like with content is an emoji reaction; a plain Like falls through to core
551
599
  if (typeof context.activity.content === 'string' && context.activity.content.trim()) {
552
- await ReactionsPlugin.applyEmojiReact(context.activity);
553
- return { ...context, claimed: true };
600
+ const applied = await ReactionsPlugin.applyEmojiReact(context.activity);
601
+ if (applied) {
602
+ return { ...context, claimed: true };
603
+ }
554
604
  }
555
605
  return context;
556
606
  };
@@ -615,8 +665,11 @@ ReactionsPlugin.handleAnnounce = async function (context) {
615
665
  }
616
666
  }
617
667
 
618
- await ReactionsPlugin.applyEmojiReact(object);
619
- return { ...context, claimed: true };
668
+ const applied = await ReactionsPlugin.applyEmojiReact(object);
669
+ if (applied) {
670
+ return { ...context, claimed: true };
671
+ }
672
+ return context;
620
673
  };
621
674
 
622
675
  SocketPlugins.reactions = {