# Giveaways

> Start a giveaway with a Join button, then pick a random winner with one command.

You will build a giveaway system:

| Part | Job |
|---|---|
| `/giveaway` | Posts the giveaway with a **Join** button |
| `Giveaway join` event | Saves each member who clicks **Join** |
| `/gend` | Picks a random winner and announces it |

Works on every plan. One giveaway runs at a time.

## What you need

- About 10 minutes.
- A channel for giveaways.

Entries are saved in a **Mongo database** called `giveaway`, one row per member. A member who clicks twice still has one entry. See [Saving data](https://ccbot.app/docs/concepts/saving-data).

## 1. /giveaway

Open **Create → Slash Command**.

| Field | Value |
|---|---|
| Command Name | `giveaway` |
| Description | `Start a giveaway` |

Add a **Text** argument named `prize`, **Required** on.

Open the permissions part of the form. In **Discord permissions**, pick **Manage Server**. Under **Behavior**, turn on **Only the user sees the reply**.

Pre-hook:

```python
# Start fresh: remove entries from the last giveaway.
entries = SlashMongo("giveaway")
await entries.delete_many({})
update_server_var("giveaway_prize", prize)

join = ComponentBuilder()
join.add_button("Join", style="success", emoji="🎉", custom_id="giveaway_join")

send_message(
    channel,
    message=Message(
        title=f"🎉 Giveaway: {prize}",
        description="Press **Join** to enter. Good luck!",
        color="#E91E63",
    ),
    components=join,
)

set_content("Giveaway started. Run /gend to pick the winner.")
```

`update_server_var` saves the prize name, so `/gend` can show it later.

## 2. The Join button

Open **Create → Event**.

| Field | Value |
|---|---|
| Name | `Giveaway join` |
| Type | `Button Click / Select Menu Change` |
| Custom ids | `giveaway_join` |

Code:

```python
defer_response(ephemeral=True)

entries = SlashMongo("giveaway")
await entries.update_one(
    {"_id": str(user.id)},
    {"$setOnInsert": {"joined": int(unix_timestamp)}},
    upsert=True,
)
total = await entries.count()

respond_interaction(interaction, f"You are in! {total} entries so far.")
```

- The member's ID is the row's `_id`, so a second click finds the same row. No double entries.
- `$setOnInsert` only writes when the row is new.
- `count()` counts every entry.

## 3. /gend

Make another slash command named `gend`, description `Pick the giveaway winner`. Give it **Manage Server** too, and turn on **Only the user sees the reply**.

Pre-hook:

```python
entries = SlashMongo("giveaway")
picked = await entries.aggregate([{"$sample": {"size": 1}}])
if not picked:
    set_error("Nobody has joined yet.")

winner = picked[0]["_id"]
prize = server_var("giveaway_prize", "the prize")

send_message(channel, f"🎉 Congratulations <@{winner}>! You won **{prize}**!")
await entries.delete_many({})

set_content("Winner picked.")
```

`$sample` picks one random row. The database does the picking, so it stays fast with thousands of entries.

## 4. Test it

1. Run `/giveaway` with prize `Nitro`.
2. Press **Join**. It says "You are in! 1 entries so far."
3. Press **Join** again. Still 1 entry.
4. Run `/gend`. You win.

Errors from your code show in **Logs → Error Logs** on the dashboard.

## Ideas

- **Reroll.** Remove the `delete_many` line from `/gend`. Entries then stay until the next `/giveaway`, so running `/gend` again picks a new winner.
- **Only members with a role can join.** In the event, check `has_role(user, "Member")` before saving. When it is `False`, answer with `respond_interaction` and skip the save.
- **End on its own.** `schedule` can run a built-in action later, but it cannot run your own code. A **Clock** event with `schedule_clock` can. See [Scheduling](https://ccbot.app/docs/reference/scheduling).
- **Show the count on the message.** Edit the giveaway message with `edit_message` after each join. See [Messages](https://ccbot.app/docs/reference/messages).
