# Scheduling

> Do something later, or on a repeat. One-off actions, clocks and repeating jobs.

There are three ways to make the bot act later:

| Tool | Use it for | Runs |
|---|---|---|
| `schedule` | One ready action later, like "remove this role in 2 days". | Once |
| `schedule_clock` | Your own code later, like a reminder. | Once |
| `start_job` | Your own code on a repeat, like a daily post. | Again and again |

Write `await` in front of every function on this page.

Times are plain words, like `"in 10 minutes"`, `"tomorrow 5pm"` or `"next friday"`. They use your server's timezone, set in the dashboard settings. Without one, the bot uses UTC.

## Limits

| What | Free | Starter | Basic | Advanced |
|---|---|---|---|---|
| `schedule` actions waiting at once | 20 | 100 | 250 | 1,000 |
| Clocks waiting at once | 1,000 | 10,000 | 10,000 | 10,000 |
| Repeating jobs | not available | available | available | available |

A waiting action or clock frees its place when it runs.

## schedule

Runs one ready-made action at a later time. Returns the schedule ID.

```python
await schedule(action, when, data=None, member=None, channel=None)
```

| Argument | What it does |
|---|---|
| `action` | What to do. See the list below. |
| `when` | When to do it, like `"in 2 hours"`. Must be in the future. |
| `data` | The details for the action. A dict, or one plain value. |
| `member` | The member to act on. Defaults to the person who ran the command. |
| `channel` | The channel to act in. Defaults to the current channel. |

**Example: a temporary role**

```python
await schedule("remove_role", "in 7 days", data={"roles": ["Trial"]})
set_content("You have the Trial role for 7 days.")
```

**Example: post a message later**

```python
await schedule("send_message", "tomorrow 9am", data="Good morning everyone!")
```

### Actions you can schedule

When you pass one plain value as `data`, it fills the field marked **main**.

| Action | `data` fields |
|---|---|
| `send_message` | `content` (main), `embed`, `channel` |
| `send_dm` | `content` (main), `embed`, `member` |
| `add_role`, `remove_role` | `roles` (main), `member` |
| `kick_member` | `member` (main), `reason` |
| `ban_member` | `member` (main), `reason`, `delete_days` (0 to 7) |
| `unban_member` | `user` (main), `reason` |
| `delete_message` | `channel`, `message` |
| `edit_message` | `channel`, `message`, `content`, `embed` |
| `pin_message`, `unpin_message` | `channel`, `message` |
| `purge_messages` | `amount` (main, 1 to 100), `channel` |
| `lock_channel`, `unlock_channel` | `channel` (main) |
| `set_slowmode` | `seconds` (main, up to 21,600), `channel` |
| `update_channel` | `channel`, `name`, `topic`, `nsfw`, `slowmode` |
| `delete_channel` | `channel` (main) |
| `mute_member`, `unmute_member` | `member` (main) |
| `create_poll` | `channel`, `question`, `answers` (2 to 10), `duration` (hours) |

`embed` is a saved embed ID or a built embed. An unknown action fails and lists the valid ones.

## cancel_schedule

Cancels a waiting `schedule` action.

```python
await cancel_schedule(schedule_id)
```

```python
sid = await schedule("send_message", "in 1 hour", data="Break is over!")
update_member_var("break_id", sid)
# later, in another command:
await cancel_schedule(member_var("break_id"))
```

Fails with *"No schedule found with id ..."* when it already ran or never existed.

## list_schedules

Lists the waiting `schedule` actions of this server.

```python
await list_schedules()
```

Returns a list of dicts with `id`, `action`, `run_at` and `data`.

```python
waiting = await list_schedules()
set_content(f"{len(waiting)} actions are waiting.")
```

## schedule_clock

Runs your **Clock** event once at a later time. Use it when the ready actions above are not enough.

```python
await schedule_clock(custom_id, time, metadata)
```

| Argument | What it does |
|---|---|
| `custom_id` | A name for this clock. Your Clock event listens for it. |
| `time` | When it fires, like `"in 30 minutes"`. |
| `metadata` | Any data you want back when it fires, like a dict. |

**Step 1.** In a command, set the clock:

```python
await schedule_clock(
    f"remind_{user.id}",
    when,
    {"user_id": user.id, "channel_id": channel.id, "text": text},
)
set_content("Got it. I will remind you.")
```

**Step 2.** Make an event of type **Clock**. In its custom IDs, enter `remind_*`. The `*` matches any ending.

```python
send_message(metadata["channel_id"], f"<@{metadata['user_id']}> Reminder: {metadata['text']}")
```

Only the first Clock event that matches runs. See [Events](https://ccbot.app/docs/getting-started/events) and [Reminders](https://ccbot.app/docs/guides/reminders).

## start_job

Starts a repeating job. Each time it is due, it fires your **Clock** event with the job's `custom_id` and `metadata`.

```python
await start_job(
    custom_id,
    freq,
    interval=1,
    date=None,
    day_name="",
    month=None,
    hour=None,
    minute=None,
    second=None,
    metadata=None,
)
```

| Argument | What it does |
|---|---|
| `custom_id` | A name for the job. Must be new in this server. |
| `freq` | How often: `"hour"`, `"day"`, `"week"`, `"month"` or `"year"`. |
| `interval` | Every how many. `2` with `"day"` means every 2 days. |
| `date` | Day of the month, 1 to 31. |
| `day_name` | Day of the week, in lowercase, like `"monday"`. |
| `month` | Month of the year, 1 to 12. |
| `hour`, `minute`, `second` | Time of day, in your server's timezone. |
| `metadata` | A dict your Clock event gets each time. |

What each `freq` needs:

| `freq` | Needs |
|---|---|
| `hour` | `minute`, `second` |
| `day` | `hour`, `minute`, `second` |
| `week` | `day_name`, `hour`, `minute`, `second` |
| `month` | `date`, `hour`, `minute`, `second` |
| `year` | `month`, `date`, `hour`, `minute`, `second` |

```python
# Every day at 9:00
await start_job("daily_quote", "day", hour=9, minute=0, second=0,
                metadata={"channel": "general"})
```

> **Premium for repeats:** Jobs run only while the server has a paid plan. Without one, a job pauses itself when it is due. It picks up again after you renew.

> **Note:** Run `start_job` from a command, once. A Clock event that starts its own job never runs, because the job must exist first.

You can also make jobs on the dashboard, under **Jobs**.

## pause_job

Pauses a running job. You can resume it from the dashboard.

```python
await pause_job(custom_id)
```

## cancel_job

Stops a job and deletes it.

```python
await cancel_job(custom_id)
```

Both fail when no job has that `custom_id`, or when it is not running.
