# Leveling

> Read and change members' XP and levels, and draw their rank card.

These work only while leveling is on (dashboard, **Leveling**). Otherwise they stop the script with a clear error.

The bot adds `await` for all of them. `member` can be a member, `user`, or an ID. Where it is optional, it means whoever ran the command.

## Read

```python
get_level(member=None)
```

Returns a dict:

| Key | Meaning |
|---|---|
| `xp` | Total XP |
| `level` | Their level |
| `rank` | Their place on the leaderboard, `None` without XP |
| `level_xp` | XP into this level |
| `next_level_xp` | XP this level needs |
| `progress` | 0 to 1 toward the next level |

```python
get_leaderboard(limit=10, page=1)
```

Returns a list of `{user_id, xp, level, rank}`, best first. `limit` is at most 25.

## Change

```python
add_xp(member, amount)     # a negative amount takes XP away
set_xp(member, xp)
set_level(member, level)   # 0 to 1000; their XP becomes the start of that level
```

Each returns the same dict as `get_level`. XP never goes below 0. When a member reaches a new level, they get the reward roles and the **Level up** event and automations run. The level-up message does not post, because there is no chat channel.

A run can change XP 100 times. Inside a **Level up** event, changes still give rewards but do not start another **Level up**.

## Draw

```python
render_rank_card(member=None)
```

Returns the card `/rank` draws, as a file, ready for `send_message` or the reply.

## Example: XP for a daily reward

```python
info = add_xp(user, 50)
set_content(f"+50 XP. You are level {info['level']} (#{info['rank']}).")
```
