Custom Emojis
Bundle custom Discord application emojis with your module, automatically sync them to Discord, and resolve them in embeds, buttons, and placeholders.
Medusa modules can bundle their own custom emojis. When the bot connects to Discord, Medusa automatically uploads any missing module emojis to the Discord application emoji registry and makes them available across embeds, messages, buttons, and placeholders.
Module emojis are uploaded as Discord application emojis, which do not count against server emoji limits and are usable by the bot in any server.
Supplying Module Emojis
Modules can define emojis in two ways:
- Local Image Files: Place image files directly inside
resources/emojis/. - Remote Manifest: Define an
emojis.jsonfile inresources/orresources/emojis/.
Both methods can be used individually or together.
Option 1: Image Files in resources/emojis/
Drop any .png, .gif, .jpg, .jpeg, or .webp file into resources/emojis/:
modules/security/resources/
└── emojis/
├── lock.png
├── shield.png
└── vip.gifThe filename (without the extension) becomes the emoji name. Medusa automatically prefixes the emoji name with the module identifier:
lock.pnginsecuritybecomes application emojisecurity_lock.shield.pnginsecuritybecomes application emojisecurity_shield.vip.gifinsecuritybecomes animated application emojisecurity_vip.
Option 2: Manifest in resources/emojis.json
Define a JSON array or object containing external URLs or file paths:
[
{ "name": "shield", "url": "https://cdn.example.com/shield.png" },
{ "name": "badge", "url": "https://cdn.example.com/badge.png" }
]You can also use key-value pairs:
{
"shield": "https://cdn.example.com/shield.png",
"badge": "https://cdn.example.com/badge.png"
}Built-in Presets
Medusa ships 299 preset emojis: the full alphabet (AA to ZZ), numbers (00 to 10), a transparent blank, game pieces, and general icons such as check, cross, warning, settings, calendar, mail, link, play, pause, mic, globe, like, dislike, cart, wallet, ticket, gavel and mention. The full list is this.list in src/medusa/handlers/discord/emojis.js. Medusa never sends unicode emojis; use a preset (or add a module emoji) for every marker in messages, embeds and components. Discord only lets the bot itself use application emojis, so reactions that members add (such as the starboard star) are the one place a unicode or server emoji is still needed. They are drawn from Lucide icons on the Medusa role-icon card and hosted on the Zero Development bucket, one folder per colour:
https://bucket.zerodev.ca/emojis/<colour>/<name>.pngThe colour comes from the EMOJI_THEME setting (Settings > Branding > Emoji colour in the dashboard): purple (default), blue, green, red, pink or orange. this.medusa.emojis.theme() returns the active colour and this.medusa.emojis.presets lists { name, url } for it.
When the colour changes, or a bot's application has not been synced with the current colour yet (a new bot, or a bot token moved over from another project), Medusa deletes every application emoji on that bot, including leftovers from other projects, and then uploads the full preset set plus all module emojis. The colour each application was last synced with is stored in the EMOJI_SYNCED setting (keyed by application id), so the reset also runs on the next start if it was interrupted.
Saving a new colour from the dashboard opens an Installing emojis modal with a progress bar for each bot, first while the old emojis are removed and then while the new ones upload. Choosing Run in background closes the modal and the sync keeps running, with a small progress slot in the Settings save bar that reopens the modal when clicked. The slot also appears when Settings is opened while a sync is still running. The same progress is available from this.medusa.emojis.status(), which returns { running, bots: [{ key, label, phase, done, total, failed }] } where phase is queued, removing, uploading or done.
Discord Application Sync
Application emojis belong to one Discord application, so Medusa syncs them to every bot. When each bot connects (medusa.discord.bot.ready), Medusa:
- Fetches all existing application emojis from the Discord API.
- Deletes all application emojis if the application was not synced with
EMOJI_THEMEyet. - Compares existing emojis against the built-in presets and all registered module emojis.
- Uploads any missing emojis to Discord via the application emojis endpoint.
- Registers the resulting emoji markup (
<:name:id>or<a:name:id>) in that bot's registry inthis.medusa.emojis.
Deletes and uploads run four at a time. The registry is updated as each step lands: removed emojis leave it before they are deleted and new ones are added as soon as they upload, so a message sent mid-sync drops the missing emoji instead of pointing at a deleted one. Failed uploads are logged and retried on the next start.
this.medusa.emojis.get() and %custom_emoji_<name>% resolve from the registry of the bot running the current module, so the same placeholder works on every bot. Prefer the placeholder over hard-coded <:name:id> markup, which only renders for the application that owns the emoji.
Discord application emoji names must be between 2 and 32 characters and may only contain alphanumeric characters and underscores ([a-zA-Z0-9_]). Medusa automatically sanitizes and truncates names to meet this requirement.
Usage
In Placeholders and Text
Use the %custom_emoji_<module>_<emoji>% placeholder format anywhere in embed descriptions, titles, fields, and message templates:
%custom_emoji_security_lock% Member Verification
Click the verify button below to gain access to the server.Medusa replaces the token with the rendered Discord custom emoji markup (e.g. <:security_lock:1234567890>).
If the emoji name is unique across all modules, the short form %custom_emoji_<emoji>% is also resolved:
%custom_emoji_lock% Verification RequiredIn Interactive Buttons
Buttons accept %custom_emoji_<module>_<emoji>% in their emoji parameter:
{
"type": "button",
"id": "security_verify",
"label": "Verify",
"style": "secondary",
"emoji": "%custom_emoji_security_lock%"
}Medusa's embed resolver automatically converts the token into the Discord component emoji payload { id, name, animated }.
In Code
Access module emojis programmatically through the emoji registry:
const lockEmoji = this.medusa.emojis.get("security", "lock");
const shieldEmoji = this.medusa.emojis.get("security_shield");Check if an emoji exists in the registry:
if (this.medusa.emojis.has("security", "lock")) {
}Query all registered module emojis:
const allModuleEmojis = this.medusa.modules.emojis.all();