Medusa/developer documentation/Custom Emojis

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:

  1. Local Image Files: Place image files directly inside resources/emojis/.
  2. Remote Manifest: Define an emojis.json file in resources/ or resources/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.gif

The filename (without the extension) becomes the emoji name. Medusa automatically prefixes the emoji name with the module identifier:

  • lock.png in security becomes application emoji security_lock.
  • shield.png in security becomes application emoji security_shield.
  • vip.gif in security becomes animated application emoji security_vip.

Option 2: Manifest in resources/emojis.json

Define a JSON array or object containing external URLs or file paths:

modules/security/resources/emojis.json
[
    { "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:

modules/security/resources/emojis.json
{
    "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>.png

The 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:

  1. Fetches all existing application emojis from the Discord API.
  2. Deletes all application emojis if the application was not synced with EMOJI_THEME yet.
  3. Compares existing emojis against the built-in presets and all registered module emojis.
  4. Uploads any missing emojis to Discord via the application emojis endpoint.
  5. Registers the resulting emoji markup (<:name:id> or <a:name:id>) in that bot's registry in this.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 Required

In 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();