# Custom Emojis

URL: https://zerodev.ca/docs/medusa/developer-documentation/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.

<Callout type="info">
  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.
</Callout>

## 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/`:

```text
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:

```json title="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:

```json title="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](https://lucide.dev) icons on the Medusa role-icon card and hosted on the Zero Development bucket, one folder per colour:

```text
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.

<Callout type="warning">
  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.
</Callout>

## Usage

### In Placeholders and Text

Use the `%custom_emoji_<module>_<emoji>%` placeholder format anywhere in embed descriptions, titles, fields, and message templates:

```markdown
%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:

```markdown
%custom_emoji_lock% Verification Required
```

### In Interactive Buttons

Buttons accept `%custom_emoji_<module>_<emoji>%` in their emoji parameter:

```json
{
    "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:

```javascript
const lockEmoji = this.medusa.emojis.get("security", "lock");
const shieldEmoji = this.medusa.emojis.get("security_shield");
```

Check if an emoji exists in the registry:

```javascript
if (this.medusa.emojis.has("security", "lock")) {
}
```

Query all registered module emojis:

```javascript
const allModuleEmojis = this.medusa.modules.emojis.all();
```
