# Placeholders

URL: https://zerodev.ca/docs/medusa/developer-documentation/placeholders

> Contribute live values to the bot activity status and add placeholder buttons to configuration fields.



Medusa has two placeholder systems:

* **Placeholder providers** in `resources/placeholders/` contribute live values (such as `%tickets_open%`) to the bot activity status. Every provider shows up as a button under the status text on the dashboard **Bot** page.
* **Field placeholders** declared with `placeholders` on a `string` or `text` configuration field render buttons under that input that insert the token at the cursor.

## Placeholder Providers

Providers reside in `resources/placeholders/` and load from every `*.js` file located directly in that directory. One provider can supply several keys.

```js title="modules/example/resources/placeholders/tickets.js"
import { Placeholders } from "#medusa/modules";

export class TicketPlaceholders extends Placeholders {
    constructor(medusa) {
        super(medusa, { name: "tickets" });
    }

    keys() {
        return [
            { key: "tickets_open", label: "Open Tickets", sample: "7" },
            { key: "tickets_total", label: "Total Tickets", sample: "319" }
        ];
    }

    async values() {
        const model = this.medusa.modules.models.get("ticket", "ticket_tickets");
        return { tickets_open: await model.count({ status: "open" }), tickets_total: await model.count() };
    }
}
```

### Constructor Options

| Property | Type     | Default  | Description                                                                                 |
| -------- | -------- | -------- | ------------------------------------------------------------------------------------------- |
| `name`   | `string` | Required | Provider identifier, unique within the module.                                              |
| `order`  | `number` | `0`      | Sort priority between providers of the same module. Core providers are always listed first. |

### Methods

| Method            | Returns                                     | Description                                                                                                                                          |
| ----------------- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `keys()`          | `Array<{ key, label, sample }>`             | The tokens this provider fills. `key` is used as `%key%`. Prefix keys with the module id (`tickets_open`, `giveaways_active`) so they never collide. |
| `values(context)` | `Promise<Record<string, string \| number>>` | Resolves every key. `context` holds `client` (the Discord.js client of the bot whose status is being set) and `bot`.                                 |

<Callout type="info">
  `values` only runs when the status text contains one of the provider's keys, so a provider can query the database without slowing down statuses that do not use it. A provider that throws is logged and its keys stay unresolved.
</Callout>

### Translating Labels

The dashboard reads button labels from `placeholder.<module>.<key>` in the module's `resources/lang/<code>/common.json`, falling back to the English `label`:

```json title="modules/example/resources/lang/de/common.json"
{
    "placeholder.ticket.tickets_open": "Offene Tickets"
}
```

### Built-in Keys

Core ships these keys, and the global placeholders from `embeds.fill` (custom **Placeholders** settings, `%server%`, `%version%`, `%name%`) also work in the status:

| Key          | Value                               |
| ------------ | ----------------------------------- |
| `%servers%`  | Servers the bot is in.              |
| `%members%`  | Total members across those servers. |
| `%channels%` | Cached channels.                    |
| `%ping%`     | Gateway ping in milliseconds.       |
| `%uptime%`   | Uptime such as `2d 4h` or `3h 12m`. |
| `%modules%`  | Loaded modules.                     |
| `%bot_name%` | The bot's username.                 |

### Refresh

When any status line contains `%`, the bot re-applies the current line every 60 seconds so the values stay current. Rotation between several lines keeps its own interval.

### Placeholder Manager Methods

```js
const keys = this.medusa.placeholders.list();

const text = await this.medusa.placeholders.fill("Watching %tickets_open% tickets", { client, bot });
```

| Method                | Returns                                 | Description                                                                                               |
| --------------------- | --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `list()`              | `Array<{ key, label, sample, module }>` | Every key from enabled modules, Core first. Served at `GET /api/placeholders`.                            |
| `fill(text, context)` | `Promise<string>`                       | Resolves the providers used in `text`, then runs `embeds.fill` for global placeholders and custom emojis. |

## Field Placeholders

Add `placeholders` to a `string` or `text` field, top-level or inside a `list` item, to render insert buttons under the input. The value is only a hint for the dashboard; your code still fills the tokens, usually through `this.medusa.embeds.fill(template, tokens)`.

```js title="modules/example/resources/config/settings.js"
naming: {
    type: "string",
    default: "ticket-%number%",
    label: "Channel Naming",
    placeholders: [
        { key: "number", label: "Ticket Number", sample: "42" },
        { key: "user", label: "Username", sample: "muffiny" }
    ]
}
```

Button labels translate through `schema.<module>.config.<group>.<field>.placeholders.<key>` for top-level fields and `schema.<module>.config.<group>.<list>.<field>.placeholders.<key>` for list item fields.

<Callout type="warn">
  Keep the `placeholders` list in sync with the tokens your code passes to `embeds.fill`. A button for a token the code never fills leaves the raw `%key%` in the output.
</Callout>
