Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,3 +120,5 @@ We're hiring developers, technical support, and product managers all the time. C
# 🗒️ Credits

- Emoji provided graciously by [JoyPixels](https://www.joypixels.com).

<!-- Contribution practice by Nitish -->
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Remove practice comment from production README.

This HTML comment appears to be a test/practice addition that should not be included in the main README.

🧹 Proposed fix
 - Emoji provided graciously by [JoyPixels](https://www.joypixels.com).
-
-<!-- Contribution practice by Nitish -->
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<!-- Contribution practice by Nitish -->
- Emoji provided graciously by [JoyPixels](https://www.joypixels.com).
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` at line 124, Remove the stray HTML practice comment "<!--
Contribution practice by Nitish -->" from the README by deleting that comment
line and ensure no other test/practice HTML comments remain in the README;
update the file so only production content stays and commit the change.

20 changes: 10 additions & 10 deletions apps/meteor/app/emoji-emojione/README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# Updating emojione
# Updating emojis (now using emoji-toolkit)

## Generate new category map variable for emojipicker
Run
Run the generator script which now pulls metadata from `emoji-toolkit`

```
node --experimental-modules generateEmojiIndex.mjs
```
Comment on lines 6 to 8
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add language specification to fenced code block.

The code block is missing a language identifier, which helps with syntax highlighting and accessibility.

📝 Proposed fix
-```
+```shell
 node --experimental-modules generateEmojiIndex.mjs
 ```
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
```
node --experimental-modules generateEmojiIndex.mjs
```
🧰 Tools
🪛 markdownlint-cli2 (0.21.0)

[warning] 6-6: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/app/emoji-emojione/README.md` around lines 6 - 8, The fenced code
block showing the command to run generateEmojiIndex.mjs lacks a language tag;
update the block to include a shell/bash language identifier (e.g., replace ```
with ```shell or ```bash) so the line "node --experimental-modules
generateEmojiIndex.mjs" is syntax-highlighted and accessible.


## Generate new percentage sprite
Clone the repository https://github.com/Ranks/emojione/ and replace the file `assets/sprites/emojione.sprites.mustache` with the content
of [emojione.sprites.mustache](emojione.sprites.mustache), then run at `emojione` folder:
The script will download 64px PNGs from the JoyPixels CDN into `.emoji-cache`
and build updated sprite sheets under `public/packages/emojione/`.

```
grunt sprite
sass --sourcemap=none assets/sprites/emojione.sprites.scss sprites.css
```
## Sprite generation notes
The old manual `grunt`/`sass` workflow is no longer required. If you need to
rebuild sprites offline you can cache the PNG files yourself by running the
script once with internet access; subsequent runs will reuse the cached images.

And replace the file `sprites.css` at Rocket.Chat's `/packages/rocketchat-emoji-emojione/sprites.css`.
The generated CSS is written to `apps/meteor/app/emoji-emojione/client/emojione-sprites.css`.
Binary file modified apps/meteor/app/emoji-emojione/lib/generateEmojiIndex.mjs
Binary file not shown.
302 changes: 24 additions & 278 deletions apps/meteor/app/emoji-emojione/lib/getEmojiConfig.ts
Original file line number Diff line number Diff line change
@@ -1,281 +1,27 @@
import emojione from 'emojione';
import mem from 'mem';
import emojiToolkit from 'emoji-toolkit';

import { emojisByCategory, emojiCategories, toneList } from './emojiPicker';

// TODO remove fix below when issue is solved: https://github.com/joypixels/emojione/issues/617

// add missing emojis not provided by JS object, but included on emoji.json
emojione.shortnames +=
'|:tm:|:copyright:|:registered:|:digit_zero:|:digit_one:|:digit_two:|:digit_three:|:digit_four:|:digit_five:|:digit_six:|:digit_seven:|:digit_eight:|:digit_nine:|:pound_symbol:|:asterisk_symbol:';
emojione.regShortNames = new RegExp(
`<object[^>]*>.*?<\/object>|<span[^>]*>.*?<\/span>|<(?:object|embed|svg|img|div|span|p|a)[^>]*>|(${emojione.shortnames})`,
'gi',
);

emojione.emojioneList[':tm:'] = {
uc_base: '2122',
uc_output: '2122-fe0f',
uc_match: '2122-fe0f',
uc_greedy: '2122-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':copyright:'] = {
uc_base: '00a9',
uc_output: '00a9-f0ef',
uc_match: '00a9-fe0f',
uc_greedy: '00a9-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':registered:'] = {
uc_base: '00ae',
uc_output: '00ae-fe0f',
uc_match: '00ae-fe0f',
uc_greedy: '00ae-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_zero:'] = {
uc_base: '0030',
uc_output: '0030-fe0f',
uc_match: '0030-fe0f',
uc_greedy: '0030-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_one:'] = {
uc_base: '0031',
uc_output: '0031-fe0f',
uc_match: '0031-fe0f',
uc_greedy: '0031-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_two:'] = {
uc_base: '0032',
uc_output: '0032-fe0f',
uc_match: '0032-fe0f',
uc_greedy: '0032-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_three:'] = {
uc_base: '0033',
uc_output: '0033-fe0f',
uc_match: '0033-fe0f',
uc_greedy: '0033-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_four:'] = {
uc_base: '0034',
uc_output: '0034-fe0f',
uc_match: '0034-fe0f',
uc_greedy: '0034-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_five:'] = {
uc_base: '0035',
uc_output: '0035-fe0f',
uc_match: '0035-fe0f',
uc_greedy: '0035-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_six:'] = {
uc_base: '0036',
uc_output: '0036-fe0f',
uc_match: '0036-fe0f',
uc_greedy: '0036-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_seven:'] = {
uc_base: '0037',
uc_output: '0037-fe0f',
uc_match: '0037-fe0f',
uc_greedy: '0037-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_eight:'] = {
uc_base: '0038',
uc_output: '0038-fe0f',
uc_match: '0038-fe0f',
uc_greedy: '0038-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':digit_nine:'] = {
uc_base: '0039',
uc_output: '0039-fe0f',
uc_match: '0039-fe0f',
uc_greedy: '0039-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':pound_symbol:'] = {
uc_base: '0023',
uc_output: '0023-fe0f',
uc_match: '0023-fe0f',
uc_greedy: '0023-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};

emojione.emojioneList[':asterisk_symbol:'] = {
uc_base: '002a',
uc_output: '002a-fe0f',
uc_match: '002a-fe0f',
uc_greedy: '002a-fe0f',
shortnames: [],
category: 'symbols',
emojiPackage: 'emojione',
};
// end fix

// fix for :+1: - had to replace all function that does its conversion: https://github.com/joypixels/emojione/blob/4.5.0/lib/js/emojione.js#L249

emojione.shortnameConversionMap = mem(emojione.shortnameConversionMap, { maxAge: 1000 });

emojione.unicodeCharRegex = mem(emojione.unicodeCharRegex, { maxAge: 1000 });

const convertShortName = mem(
(shortname) => {
// the fix is basically adding this .replace(/[+]/g, '\\$&')
if (typeof shortname === 'undefined' || shortname === '' || emojione.shortnames.indexOf(shortname.replace(/[+]/g, '\\$&')) === -1) {
// if the shortname doesnt exist just return the entire match
return shortname;
}

// map shortname to parent
if (!emojione.emojioneList[shortname]) {
for (const emoji in emojione.emojioneList) {
if (!emojione.emojioneList.hasOwnProperty(emoji) || emoji === '') {
continue;
}
if (emojione.emojioneList[emoji].shortnames.indexOf(shortname) === -1) {
continue;
}
shortname = emoji;
break;
}
}

const unicode = emojione.emojioneList[shortname].uc_output;
const fname = emojione.emojioneList[shortname].uc_base;
const category = fname.indexOf('-1f3f') >= 0 ? 'diversity' : emojione.emojioneList[shortname].category;
const title = emojione.imageTitleTag ? `title="${shortname}"` : '';
// const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32';
// if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path
const ePath =
emojione.defaultPathPNG !== emojione.imagePathPNG ? emojione.imagePathPNG : `${emojione.defaultPathPNG + emojione.emojiSize}/`;

// depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname
const alt = emojione.unicodeAlt ? emojione.convert(unicode.toUpperCase()) : shortname;

if (emojione.sprites) {
return `<span class="emojione emojione-${category} _${fname}" ${title}>${alt}</span>`;
}
return `<img class="emojione" alt="${alt}" ${title} src="${ePath}${fname}${emojione.fileExtension}"/>`;
},
{ maxAge: 1000 },
);

const convertUnicode = mem(
(entire, _m1, m2, m3) => {
const mappedUnicode = emojione.mapUnicodeToShort();

if (typeof m3 === 'undefined' || m3 === '' || !(emojione.unescapeHTML(m3) in emojione.asciiList)) {
// if the ascii doesnt exist just return the entire match
return entire;
}

m3 = emojione.unescapeHTML(m3);
const unicode = emojione.asciiList[m3];
const shortname = mappedUnicode[unicode];
const category = unicode.indexOf('-1f3f') >= 0 ? 'diversity' : emojione.emojioneList[shortname].category;
const title = emojione.imageTitleTag ? `title="${emojione.escapeHTML(m3)}"` : '';
// const size = ns.spriteSize === '32' || ns.spriteSize === '64' ? ns.spriteSize : '32';
// if the emoji path has been set, we'll use the provided path, otherwise we'll use the default path
const ePath =
emojione.defaultPathPNG !== emojione.imagePathPNG ? emojione.imagePathPNG : `${emojione.defaultPathPNG + emojione.emojiSize}/`;

// depending on the settings, we'll either add the native unicode as the alt tag, otherwise the shortname
const alt = emojione.unicodeAlt ? emojione.convert(unicode.toUpperCase()) : emojione.escapeHTML(m3);

if (emojione.sprites) {
return `${m2}<span class="emojione emojione-${category} _${unicode}" ${title}>${alt}</span>`;
}
return `${m2}<img class="emojione" alt="${alt}" ${title} src="${ePath}${unicode}${emojione.fileExtension}"/>`;
},
{ maxAge: 1000, cacheKey: JSON.stringify },
);

emojione.shortnameToImage = (str) => {
// replace regular shortnames first
str = str.replace(emojione.regShortNames, convertShortName);

// if ascii smileys are turned on, then we'll replace them!
if (emojione.ascii) {
const asciiRX = emojione.riskyMatchAscii ? emojione.regAsciiRisky : emojione.regAscii;

return str.replace(asciiRX, convertUnicode);
}

return str;
};

const isEmojiSupported = (str: string) => {
str = str.replace(emojione.regShortNames, convertShortName);

// if ascii smileys are turned on, then we'll replace them!
if (emojione.ascii) {
const asciiRX = emojione.riskyMatchAscii ? emojione.regAsciiRisky : emojione.regAscii;

return str.replace(asciiRX, convertUnicode);
}

return str;
};

export const getEmojiConfig = () => ({
emojione,
emojisByCategory,
emojiCategories,
toneList,
render: emojione.toImage,
renderPicker: emojione.shortnameToImage,
sprites: true,
isEmojiSupported,
});
// The old file contained a large number of fixes for the outdated emojione
// library. The new toolkit is much more well behaved; we simply expose the
// functions we care about and keep the same configuration shape so the
// rest of the system (which references `emoji.packages.emojione`) continues
// working without modification.

export const getEmojiConfig = () => {
// emojiToolkit already includes a structured list on `emojiToolkit.emoji` or
// regardless we can re-export the raw JSON for compatibility.
const base = emojiToolkit;

return {
emojione: base,
sprites: true,
emojisByCategory,
emojiCategories,
toneList,
render: base.toImage,
renderPicker: base.toImage,
};
};

// legacy helper removed; this file now only exports the simple config
4 changes: 2 additions & 2 deletions apps/meteor/app/emoji-emojione/server/callbacks.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import emojione from 'emojione';
import emojiToolkit from 'emoji-toolkit';
import { Meteor } from 'meteor/meteor';

import { callbacks } from '../../../server/lib/callbacks';

Meteor.startup(() => {
callbacks.add(
'beforeSendMessageNotifications',
(message) => emojione.shortnameToUnicode(message),
(message) => emojiToolkit.shortnameToUnicode(message),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n apps/meteor/app/emoji-emojione/server/callbacks.ts

Repository: RocketChat/Rocket.Chat

Length of output: 501


🏁 Script executed:

# Search for emoji-toolkit package info and version
fd -e "package.json" | xargs grep -l "emoji-toolkit" 2>/dev/null | head -5

Repository: RocketChat/Rocket.Chat

Length of output: 48


🌐 Web query:

emoji-toolkit shortnameToUnicode undefined behavior v10

💡 Result:

In emoji-toolkit v10.0.0, joypixels.shortnameToUnicode(str) is implemented as a string transform: it runs str.replace(...) over a shortname regex and:

So the main “undefined behavior” you can hit is passing a non-string input (e.g. undefined/null)—because the function immediately calls str.replace(...), which will throw a TypeError if str isn’t a string. (raw.githubusercontent.com)

A safe call pattern is:

joypixels.shortnameToUnicode(String(value ?? ""));

Citations:


🏁 Script executed:

# Search for beforeSendMessageNotifications callback invocations
rg "beforeSendMessageNotifications" -A 3 -B 3

Repository: RocketChat/Rocket.Chat

Length of output: 2893


🏁 Script executed:

# Look for callback.add patterns to understand the callback signature
rg "callbacks\.add\(" -A 5 | head -100

Repository: RocketChat/Rocket.Chat

Length of output: 8623


Guard callback input before calling shortnameToUnicode.

The beforeSendMessageNotifications callback receives input from message.msg. If msg is undefined for any message, the callback will receive undefined and cause shortnameToUnicode() to throw a TypeError since the method internally calls .replace() on the input. Add a type guard to safely handle non-string inputs.

Safe guard
-		(message) => emojiToolkit.shortnameToUnicode(message),
+		(message) => (typeof message === 'string' ? emojiToolkit.shortnameToUnicode(message) : message),
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
(message) => emojiToolkit.shortnameToUnicode(message),
(message) => (typeof message === 'string' ? emojiToolkit.shortnameToUnicode(message) : message),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/meteor/app/emoji-emojione/server/callbacks.ts` at line 9, The callback
currently calls emojiToolkit.shortnameToUnicode(message) without guarding input;
update the before-send callback (the (message) => ... handler) to first check
that the input is a string (e.g., typeof message === 'string') and only then
call emojiToolkit.shortnameToUnicode(message), otherwise return the original
value (or an empty string) so shortnameToUnicode doesn't receive undefined and
throw.

callbacks.priority.MEDIUM,
'emojione-shortnameToUnicode',
);
Expand Down
8 changes: 5 additions & 3 deletions apps/meteor/app/emoji-emojione/server/lib.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ if (emoji.packages.emojione) {
emoji.packages.emojione.renderPicker = config.renderPicker;
// TODO: check types
// RocketChat.emoji.list is the collection of emojis from all emoji packages
for (const key in config.emojione.emojioneList) {
if (config.emojione.emojioneList.hasOwnProperty(key)) {
const currentEmoji = config.emojione.emojioneList[key];
// toolkit exports its data under "emojiList", keep the old name for
// backwards compatibility when setting the package key.
for (const key in config.emojione.emojiList) {
if (Object.prototype.hasOwnProperty.call(config.emojione.emojiList, key)) {
const currentEmoji = config.emojione.emojiList[key];
currentEmoji.emojiPackage = 'emojione';
emoji.list[key] = currentEmoji;

Expand Down
4 changes: 2 additions & 2 deletions apps/meteor/app/emoji/client/lib.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Emitter } from '@rocket.chat/emitter';
import emojione from 'emojione';
import emojiToolkit from 'emoji-toolkit';

import type { EmojiPackages } from '../lib/rocketchat';

Expand All @@ -14,7 +14,7 @@ export const emoji: EmojiPackages & { dispatchUpdate: () => void } = {
recent: [],
},
toneList: {},
render: emojione.toImage,
render: emojiToolkit.toImage,
renderPicker(emojiToRender) {
const correctPackage = emoji.list[emojiToRender].emojiPackage;
if (!correctPackage) {
Expand Down
Loading