> ## Documentation Index
> Fetch the complete documentation index at: https://docs.guilt.icu/llms.txt
> Use this file to discover all available pages before exploring further.

# Messages and Reactions HTTP Methods for the alterself API

> Send, edit, delete, fetch, pin, react to, and search messages using alterself's bot.http message methods. Full parameter reference included.

Every message operation in alterself flows through `bot.http`, a managed HTTP client that sits between your code and Discord's REST API. All methods are **async**, so you must `await` them. The client handles rate-limit buckets automatically: when Discord tells it to back off, it waits and retries without raising an error in your code. Every request is sent with accurate browser-style headers so the selfbot traffic looks indistinguishable from a normal client session.

***

## send\_message

Send a message to any text channel or DM channel you have access to.

```python theme={null}
data = await bot.http.send_message(channel_id, content="hello")

data = await bot.http.send_message(
    channel_id,
    content    = "with embed",
    embeds     = [{"title": "hi", "description": "world", "color": 0xFF6B6B}],
    components = [],
)
```

<ParamField path="channel_id" type="int | str" required>
  The ID of the channel to send the message to.
</ParamField>

<ParamField path="content" type="str">
  The plaintext message body. At least one of `content`, `embeds`, or `attachments` must be provided.
</ParamField>

<ParamField path="embeds" type="list[dict]">
  A list of embed objects. Each dict maps to the [Discord embed structure](https://discord.com/developers/docs/resources/message#embed-object). Maximum of 10 embeds per message.
</ParamField>

<ParamField path="components" type="list[dict]">
  A list of message component objects (action rows, buttons, select menus). Pass an empty list `[]` to explicitly send no components.
</ParamField>

**Returns:** A `dict` representing the created [Message object](https://discord.com/developers/docs/resources/message#message-object).

<Tip>
  Store the returned `data["id"]` if you need to edit or delete the message later in the same session.
</Tip>

***

## edit\_message

Edit the content or embeds of an existing message. You can only edit messages sent by your own account.

```python theme={null}
data = await bot.http.edit_message(channel_id, message_id, content="edited")
```

<ParamField path="channel_id" type="int | str" required>
  The channel containing the message.
</ParamField>

<ParamField path="message_id" type="int | str" required>
  The ID of the message to edit.
</ParamField>

<ParamField path="content" type="str">
  The new message content. Pass an empty string `""` to remove the text body (provided an embed or attachment remains).
</ParamField>

<ParamField path="embeds" type="list[dict]">
  Replacement embed list. Omit this parameter to leave existing embeds unchanged.
</ParamField>

**Returns:** The updated Message object as a `dict`.

<Warning>
  Discord will reject edits to messages you did not author. Attempting to edit another user's message raises a `403 Forbidden` error.
</Warning>

***

## delete\_message / bulk\_delete

Delete a single message or a batch of up to 100 messages at once.

```python theme={null}
await bot.http.delete_message(channel_id, message_id)
await bot.http.bulk_delete(channel_id, [msg_id_1, msg_id_2])
```

<ParamField path="channel_id" type="int | str" required>
  The channel containing the message(s).
</ParamField>

<ParamField path="message_id" type="int | str" required>
  *(delete\_message only)* The ID of the message to delete.
</ParamField>

<ParamField path="message_ids" type="list[int | str]" required>
  *(bulk\_delete only)* A list of message IDs to delete. Must contain between 2 and 100 entries.
</ParamField>

**Returns:** `None` on success.

<Warning>
  Because alterself operates as a user account, `bulk_delete` can only delete messages **you sent yourself**. It cannot be used to mass-delete other users' messages. Pass a list of your own message IDs; any IDs belonging to other authors will be rejected by Discord.
</Warning>

***

## fetch\_message / fetch\_messages

Retrieve a single message by ID, or fetch a paginated list of messages from a channel.

```python theme={null}
msg  = await bot.http.fetch_message(channel_id, message_id)

msgs = await bot.http.fetch_messages(
    channel_id,
    limit  = 50,      # max 100
    before = msg_id,
    after  = msg_id,
    around = msg_id,
)
```

<ParamField path="channel_id" type="int | str" required>
  The channel to fetch messages from.
</ParamField>

<ParamField path="message_id" type="int | str" required>
  *(fetch\_message only)* The specific message ID to retrieve.
</ParamField>

<ParamField path="limit" type="int">
  Number of messages to return. Minimum `1`, maximum `100`. Defaults to `50`.
</ParamField>

<ParamField path="before" type="int | str">
  Return messages sent **before** this message ID. Use for backwards pagination.
</ParamField>

<ParamField path="after" type="int | str">
  Return messages sent **after** this message ID. Use for forwards pagination.
</ParamField>

<ParamField path="around" type="int | str">
  Return messages surrounding this message ID. Discord returns up to `limit` messages centred on the given ID. Cannot be combined with `before` or `after`.
</ParamField>

**Returns:**

* `fetch_message` → a single Message `dict`
* `fetch_messages` → a `list[dict]` of Message objects, ordered newest-first

<Note>
  Only one of `before`, `after`, or `around` may be specified in a single `fetch_messages` call.
</Note>

***

## Pins

Pin or unpin a message, and retrieve all currently pinned messages in a channel.

```python theme={null}
await bot.http.pin_message(channel_id, message_id)
await bot.http.unpin_message(channel_id, message_id)
pins = await bot.http.fetch_pins(channel_id)
```

<ParamField path="channel_id" type="int | str" required>
  The channel whose pins you want to manage.
</ParamField>

<ParamField path="message_id" type="int | str" required>
  *(pin\_message / unpin\_message)* The message to pin or unpin.
</ParamField>

**Returns:**

* `pin_message` / `unpin_message` → `None`
* `fetch_pins` → `list[dict]` of pinned Message objects

<Note>
  Discord limits channels to **50 pinned messages**. Attempting to pin a 51st message raises a `400 Bad Request` with the `Maximum number of pins reached` error.
</Note>

***

## Reactions

Add, remove, and inspect emoji reactions on messages. Both Unicode emoji and custom server emoji are supported.

```python theme={null}
await bot.http.add_reaction(channel_id, message_id, "👍")
await bot.http.add_reaction(channel_id, message_id, "custom_emoji:123456789")
await bot.http.remove_reaction(channel_id, message_id, "👍")
await bot.http.clear_reactions(channel_id, message_id)
users = await bot.http.fetch_reactions(channel_id, message_id, "👍")
```

<ParamField path="channel_id" type="int | str" required>
  The channel containing the target message.
</ParamField>

<ParamField path="message_id" type="int | str" required>
  The message to react to or inspect.
</ParamField>

<ParamField path="emoji" type="str" required>
  For Unicode emoji, pass the literal character (e.g. `"👍"`). For custom emoji, pass `"name:id"` (e.g. `"pepe:123456789"`).
</ParamField>

**Returns:**

* `add_reaction` / `remove_reaction` / `clear_reactions` → `None`
* `fetch_reactions` → `list[dict]` of partial User objects who reacted with that emoji

<Warning>
  `clear_reactions` removes **all** reactions from a message and requires the **Manage Messages** permission.
</Warning>

***

## trigger\_typing

Send a typing indicator to a channel. The indicator appears for approximately 10 seconds or until a message is sent, whichever comes first.

```python theme={null}
await bot.http.trigger_typing(channel_id)
```

<ParamField path="channel_id" type="int | str" required>
  The channel in which to show the typing indicator.
</ParamField>

**Returns:** `None`

<Tip>
  Call `trigger_typing` immediately before a `send_message` call to simulate a more human-like typing delay in automated workflows.
</Tip>

***

## crosspost\_message

Publish (crosspost) a message from an Announcement channel so it propagates to all channels that follow it.

```python theme={null}
await bot.http.crosspost_message(channel_id, message_id)
```

<ParamField path="channel_id" type="int | str" required>
  The ID of the **Announcement** channel containing the message.
</ParamField>

<ParamField path="message_id" type="int | str" required>
  The ID of the message to crosspost.
</ParamField>

**Returns:** The crossposted Message object as a `dict`.

<Note>
  Crossposting only works in channels of type `5` (Guild Announcement). Calling this on a regular text channel raises a `400 Bad Request`.
</Note>

***

## search\_messages

Search for messages within a guild using Discord's built-in message search. Results respect the same access controls as manual searches in the client.

```python theme={null}
results = await bot.http.search_messages(
    guild_id   = guild_id,
    content    = "hello",
    author_id  = user_id,
    channel_id = channel_id,
)
```

<ParamField path="guild_id" type="int | str" required>
  The guild to search within.
</ParamField>

<ParamField path="content" type="str">
  Filter to messages containing this text.
</ParamField>

<ParamField path="author_id" type="int | str">
  Filter to messages sent by this user.
</ParamField>

<ParamField path="channel_id" type="int | str">
  Restrict the search to a specific channel within the guild.
</ParamField>

**Returns:** A `dict` with a `messages` key containing a `list[list[dict]]`, where each inner list is a context cluster of Message objects around a matching result.

<Note>
  Discord's search index is not real-time. Very recently sent messages may not appear in results immediately.
</Note>
