# Messages

> Send, edit, delete and react to messages from a command or event.

These functions work in commands (pre-hook and post-hook), events and component handlers.

You do not need to write `await` in front of them. The bot adds it for you. Writing it is fine too.

> **The reply is free:** The command's own reply (the **Response** box, or `set_content`) does not count toward any limit. Use `send_message` only for **extra** messages, like a log in another channel.

## Limits

Each run of a command or event can send a fixed number of messages. Pre-hook and post-hook share one count.

| Function | Free | Premium |
|---|---|---|
| `send_message` + `forward_message` (together) | 3 | 5 |
| `send_dm` | 2 | 2 |

Going over stops the script with *"Maximum messages sent. Can't send more."*

## send_message

Sends a message to a channel in this server. Returns the sent message.

```python
send_message(
    channel,
    content=None,
    embed=None,
    message=None,
    components=None,
    attachments=None,
    everyone_mentions=True,
    role_mentions=True,
    user_mentions=True,
    reply=None,
)
```

| Argument | Type | What it does |
|---|---|---|
| `channel` | channel, ID or name | Where to send. |
| `content` | text | The message text. |
| `embed` | embed | One embed, for example from `load_embed("id")`. |
| `message` | `Message` | Text and embed together, built with `Message(...)`. |
| `components` | `ComponentBuilder` or `View` | Buttons and menus. |
| `attachments` | list | Files, from `load_file(...)` or `render_template(...)`. |
| `everyone_mentions` | yes / no | Set `False` so `@everyone` does not ping. |
| `role_mentions` | yes / no | Set `False` so role mentions do not ping. |
| `user_mentions` | yes / no | Set `False` so user mentions do not ping. |
| `reply` | message ID | Send as a reply to that message. |

**Example: log who used a command**

```python
log = get_channel("mod-log")
send_message(log, f"{user.mention} used /{command_name} in {channel.mention}")
```

**Example: a message with an embed**

```python
send_message(channel, message=Message(
    title="Server rules",
    description="1. Be kind\n2. No spam",
    color="#5865F2",
))
```

> **Watch out:** Do not use `channel.send(...)`. It is blocked. Always use `send_message`.

## send_dm

Sends a direct message to a member. Fails when the member has DMs turned off.

```python
send_dm(member, content=None, embed=None, message=None)
```

```python
send_dm(user, "Thanks for joining the giveaway!")
```

`member` can be a member or a member ID.

## edit_message

Changes a message the bot sent before.

```python
edit_message(
    channel,
    message_id,
    content=None,
    embed=None,
    message=None,
    components=None,
    attachments=None,
)
```

```python
msg = send_message(channel, "Counting…")
edit_message(channel, msg.id, "Done! 3 of 3 checked.")
```

> **Note:** Discord only lets a bot edit its own messages.

## delete_message

Deletes a message.

```python
delete_message(channel_id, message_id)
```

```python
# In a message event: remove messages that contain a blocked word
if "badword" in message.content.lower():
    delete_message(channel_id, message_id)
```

The bot needs **Manage Messages** to delete other people's messages.

## forward_message

Forwards a message into another channel, like Discord's own **Forward** button.

```python
forward_message(channel, message, from_channel=None)
```

| Argument | What it does |
|---|---|
| `channel` | Where the forwarded copy goes. |
| `message` | A message, or a message ID. |
| `from_channel` | Where the message is. Needed only when `message` is an ID. |

```python
forward_message("starboard", message)
forward_message("starboard", 1290000000000000000, from_channel="general")
```

Both channels must be in this server. It counts toward the `send_message` limit.

## get_message

Fetches a message by its ID. Returns `None` when the channel is not found.

```python
get_message(channel_id, message_id)
```

```python
msg = get_message(channel.id, 1290000000000000000)
set_content(f"That message says: {msg.content}")
```

## purge_messages

Deletes many messages at once.

```python
purge_messages(channel_id, count, _filter=None, skip=0)
```

| Argument | What it does |
|---|---|
| `count` | How many messages to delete. |
| `_filter` | A function. Only messages where it returns `True` are deleted. |
| `skip` | Skip this many recent messages first. |

```python
# Delete the last 20 messages from bots
purge_messages(channel.id, 20, lambda m: m.author.is_bot)
```

> **Watch out:** Discord only bulk-deletes messages newer than **14 days**. Older ones are skipped. The bot needs **Manage Messages** and **Read Message History**.

## add_reactions

Adds one or more reactions to a message.

```python
add_reactions(message, *emojis)
```

```python
msg = send_message(channel, "Do you like pizza?")
add_reactions(msg, "👍", "👎")
```

For a custom emoji from your server, pass its name: `add_reactions(msg, "pepe_happy")`.

## remove_reactions

Removes reactions one user added.

```python
remove_reactions(message, user, *emojis)
```

## remove_all_reactions

Removes every reaction from a message, or every reaction of one emoji.

```python
remove_all_reactions(message, emoji=None)
```

## get_reaction_users

Lists the users who reacted with an emoji.

```python
get_reaction_users(message, emoji)
```

```python
msg = get_message(channel.id, giveaway_id)
users = get_reaction_users(msg, "🎉")
users = [u for u in users if not u.is_bot]   # skip the bot's own reaction
winner = random(users)
set_content(f"The winner is {winner.mention}!")
```

## get_reaction_count

Counts the reactions of one emoji on a message. Returns `0` when there are none.

```python
get_reaction_count(message, emoji)
```

## mention_message

Makes a link that jumps to a message.

```python
mention_message(channel_id, message_id)
```

```python
link = mention_message(channel.id, msg.id)
send_message("mod-log", f"New report: {link}")
```
