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

# alterself Gateway Events: Registration and Reference

> Listen to Discord gateway events with decorators or manual registration. alterself dispatches parsed model objects to your handlers automatically.

alterself connects to the Discord gateway over a persistent WebSocket and receives a continuous stream of JSON payloads describing everything that happens in Discord, including messages, reactions, guild changes, voice state transitions, and more. For each payload, alterself looks up the event name, builds the appropriate model objects (such as `Message`, `Guild`, or `User`), and calls every registered listener in the order it was added. You register listeners using decorators or the manual registration API, and alterself takes care of the rest.

## Registering Listeners

alterself offers three styles of listener registration so you can choose whichever fits your code structure best.

### `@bot.on_event`: register by function name

Decorate a coroutine whose name starts with `on_`. alterself strips the `on_` prefix and matches the remainder (case-insensitively) against gateway event names:

```python theme={null}
@bot.on_event
async def on_ready():
    print(f"Logged in as {bot.me.username}#{bot.me.discriminator}")

@bot.on_event
async def on_message_create(message):
    print(f"[{message.channel_id}] {message.author.username}: {message.content}")
```

### `@bot.listen(event_name)`: explicit name

Use this decorator when you want to give the function a different name, or when you need multiple handlers for the same event in the same scope:

```python theme={null}
@bot.listen("MESSAGE_CREATE")
async def log_messages(message):
    print(f"Message in {message.channel_id}: {message.content}")

@bot.listen("message_create")   # case-insensitive
async def react_to_keywords(message):
    if "hello" in message.content.lower():
        print("Greeting detected!")
```

### `bot.add_listener` / `bot.remove_listener`: manual registration

Register and deregister listeners programmatically, which is useful for testing, conditional feature flags, or dynamic plugin systems:

```python theme={null}
async def on_typing(typing_event):
    print(f"User {typing_event.user_id} is typing in {typing_event.channel_id}")

bot.add_listener("TYPING_START", on_typing)

# Later, remove the listener
bot.remove_listener("TYPING_START", on_typing)
```

<Note>
  Multiple listeners can be registered for the same event. alterself calls them all concurrently using `asyncio.gather`, so a slow listener does not block others.
</Note>

## Event Reference

### Connection

| Event | Handler signature | Description |
| - | - | - |
| `READY` | `on_ready()` | The gateway session is established and the initial state is available. `bot.me` is populated. |
| `RESUMED` | `on_resumed()` | A dropped connection was successfully resumed. Previously dispatched events were not replayed. |

### Messages

| Event | Handler signature | Description |
| - | - | - |
| `MESSAGE_CREATE` | `on_message_create(message: Message)` | A message was created in a visible channel |
| `MESSAGE_UPDATE` | `on_message_update(message: Message \| None)` | A message was edited; `None` if the message is not in the cache |
| `MESSAGE_DELETE` | `on_message_delete(message_id: int, channel_id: int, guild_id: int \| None)` | A single message was deleted |
| `MESSAGE_DELETE_BULK` | `on_message_delete_bulk(message_ids: list[int], channel_id: int, guild_id: int \| None)` | Multiple messages were bulk-deleted |
| `MESSAGE_REACTION_ADD` | `on_message_reaction_add(reaction: dict)` | A reaction was added to a message |
| `MESSAGE_REACTION_REMOVE` | `on_message_reaction_remove(reaction: dict)` | A reaction was removed from a message |

### Guilds

| Event | Handler signature | Description |
| - | - | - |
| `GUILD_CREATE` | `on_guild_create(guild: Guild)` | A guild became available (login, join, or reconnect) |
| `GUILD_UPDATE` | `on_guild_update(guild: Guild)` | A guild's settings were changed |
| `GUILD_DELETE` | `on_guild_delete(guild_id: int)` | You left a guild or it became unavailable |
| `GUILD_MEMBER_ADD` | `on_guild_member_add(member: Member)` | A user joined a guild |
| `GUILD_MEMBER_REMOVE` | `on_guild_member_remove(user: User, guild_id: int)` | A user left or was removed from a guild |
| `GUILD_MEMBER_UPDATE` | `on_guild_member_update(member: Member)` | A guild member's roles, nickname, or other attributes changed |

### Channels

| Event | Handler signature | Description |
| - | - | - |
| `CHANNEL_CREATE` | `on_channel_create(channel: Channel)` | A new channel was created in a guild |
| `CHANNEL_UPDATE` | `on_channel_update(channel: Channel)` | A channel's settings were updated |
| `CHANNEL_DELETE` | `on_channel_delete(channel: Channel)` | A channel was deleted |
| `THREAD_CREATE` | `on_thread_create(thread: Thread)` | A new thread was created or you were added to one |
| `THREAD_UPDATE` | `on_thread_update(thread: Thread)` | A thread's metadata changed |
| `THREAD_DELETE` | `on_thread_delete(thread_id: int, channel_id: int, guild_id: int)` | A thread was deleted |

### Presence and Typing

| Event | Handler signature | Description |
| - | - | - |
| `TYPING_START` | `on_typing_start(event: dict)` | A user started typing in a channel |
| `PRESENCE_UPDATE` | `on_presence_update(presence: dict)` | A user's online status or activity changed |

### Relationships

| Event | Handler signature | Description |
| - | - | - |
| `RELATIONSHIP_ADD` | `on_relationship_add(relationship: dict)` | A friend request was received or accepted, or a user was blocked |
| `RELATIONSHIP_REMOVE` | `on_relationship_remove(relationship: dict)` | A friend was removed or a block was lifted |

### Voice

| Event | Handler signature | Description |
| - | - | - |
| `VOICE_STATE_UPDATE` | `on_voice_state_update(state: dict)` | A user joined, moved, or left a voice channel, or their mute/deafen state changed |
| `VOICE_SERVER_UPDATE` | `on_voice_server_update(event: dict)` | The voice server endpoint for a guild changed |

## Raw Event Access

If you need the unprocessed gateway payload before alterself parses it into model objects, listen for the special `RAW` event. Your handler receives the full payload dictionary exactly as it arrived from Discord:

```python theme={null}
@bot.listen("RAW")
async def on_raw(payload: dict):
    event_type = payload.get("t")
    data       = payload.get("d")
    print(f"Raw event: {event_type} → {data}")
```

<Warning>
  Raw payloads contain field names and structures defined by the Discord API, which can change without notice. Prefer the typed model objects whenever possible, and fall back to raw access only when you need a field that alterself does not yet expose.
</Warning>

## Complete Examples

<CodeGroup>
  ```python on_ready: set presence on login theme={null}
  import logging
  import alterself

  bot = alterself.Client(token="...", prefix=".")

  @bot.on_event
  async def on_ready():
      await bot.change_presence(
          status="idle",
          activities=[alterself.watching("everything")],
          afk=False,
      )
      logging.info("Ready as %s (ID: %s)", bot.me.username, bot.me.id)

  bot.run(log_level=logging.INFO)
  ```

  ```python on_message_create: keyword responder theme={null}
  import alterself

  bot = alterself.Client(token="...", prefix=".")

  @bot.on_event
  async def on_message_create(message):
      # Only react to your own messages
      if message.author.id != bot.me.id:
          return

      if message.content.lower() == "ping":
          await bot.http.edit_message(
              message.channel_id,
              message.id,
              content=f"Pong! ({bot.latency * 1000:.1f} ms)",
          )

  bot.run()
  ```
</CodeGroup>
