> ## 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.

# Practical alterself Selfbot Examples and Code Recipes

> Copy-paste examples for common alterself patterns: ping bots, echo commands, auto-reactions, DM logging, guild joining, and admin cogs.

The examples below cover the patterns that come up most often when building a selfbot with alterself. Each one is self-contained and ready to drop into your project — swap in your token, adjust the constants, and run. Click any accordion to expand the full code.

***

<Accordion title="Basic ping/pong bot">
  The simplest possible alterself bot. It connects to the gateway, prints your username when ready, and responds to `.ping` with your current latency.

  ```python theme={null}
  import alterself

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

  @bot.on_event
  async def on_ready():
      print(f"Ready: {bot.me.username}")

  @bot.command()
  async def ping(ctx):
      await ctx.reply(f"pong | {bot.latency * 1000:.1f} ms")

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

<Accordion title="Echo + delete original">
  Deletes your trigger message and re-sends its content as a clean message in the same channel. The `aliases=["say"]` parameter means both `.echo` and `.say` work.

  ```python theme={null}
  @bot.command(aliases=["say"])
  async def echo(ctx, *args):
      await ctx.delete()
      await bot.http.send_message(
          ctx.channel_id,
          content=" ".join(args)
      )
  ```

  <Note>
    `ctx.delete()` removes the original command invocation message. If you lack permission to delete messages in that channel, an `HTTPError` with code `50013` is raised. Consider wrapping this in a `try/except`.
  </Note>
</Accordion>

<Accordion title="Auto-react to a specific user's messages">
  Watches every incoming message and adds a heart reaction whenever a specific user posts. Replace `TARGET_USER` with the user's numeric ID.

  ```python theme={null}
  TARGET_USER = 123456789012345678
  EMOJI       = "❤️"

  @bot.on_event
  async def on_message_create(message: alterself.Message):
      if message.author and int(message.author.id) == TARGET_USER:
          await bot.http.add_reaction(
              int(message.channel_id),
              int(message.id),
              EMOJI,
          )
  ```

  <Tip>
    You can extend `EMOJI` to a list and pick one at random with `random.choice(EMOJIS)` for more variety.
  </Tip>
</Accordion>

<Accordion title="DM logger">
  Prints every incoming direct message to your console with a timestamp. Guild messages are ignored so your logs stay clean.

  ```python theme={null}
  import alterself, datetime

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

  @bot.on_event
  async def on_message_create(message: alterself.Message):
      if message.guild_id:
          return   # only log DMs
      ts   = datetime.datetime.now().strftime("%H:%M:%S")
      who  = message.author.username if message.author else "?"
      print(f"[{ts}] DM from {who}: {message.content}")

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

<Accordion title="Admin Cog with purge command">
  Groups admin commands inside a `Cog` class to keep your project organised. The `purge` command deletes the last `n` messages in the current channel (default: 10).

  ```python theme={null}
  import alterself

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

  class AdminCog(alterself.Cog):

      @alterself.cmd()
      async def purge(self, ctx, amount: int = 10):
          """Delete the last <amount> messages in this channel."""
          deleted = 0
          async for message in bot.http.fetch_messages(ctx.channel_id, limit=amount):
              try:
                  await bot.http.delete_message(
                      int(message["channel_id"]),
                      int(message["id"]),
                  )
                  deleted += 1
              except alterself.HTTPError:
                  pass   # skip messages we can't delete

          await ctx.reply(f"🗑️ Deleted {deleted} message(s).", delete_after=5)

  bot.add_cog(AdminCog())
  bot.run()
  ```

  <Warning>
    Bulk-deleting messages at high speed can trigger rate limits. Add a short `asyncio.sleep` between deletions if you plan to purge large numbers of messages.
  </Warning>
</Accordion>

<Accordion title="Guild joiner via invite codes">
  Joins a list of Discord servers from their invite codes, with a 1.5-second delay between each join to stay under rate limits. Runs without a persistent gateway connection.

  ```python theme={null}
  import alterself, asyncio

  async def join_many(token, codes):
      bot  = alterself.Client(token=token, prefix="!")
      http = bot.http

      for code in codes:
          try:
              data = await http.join_guild(code)
              print(f"[+] Joined {data.get('guild', {}).get('name', code)}")
          except alterself.HTTPError as e:
              print(f"[-] Failed {code}: {e}")
          await asyncio.sleep(1.5)

      await http.close()

  asyncio.run(join_many("TOKEN", ["invite1", "invite2"]))
  ```

  <Note>
    `CaptchaChallenge` can be raised by `http.join_guild()` if Discord requires verification. See the [Error Handling](/guides/error-handling) guide for details on how to handle it.
  </Note>
</Accordion>

<Accordion title="Presence cycling">
  Rotates through a list of activities every 60 seconds using a background `asyncio` task. The task starts automatically once the gateway is ready.

  ```python theme={null}
  import alterself, asyncio

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

  ACTIVITIES = [
      alterself.playing("Chess"),
      alterself.listening("Lo-fi beats"),
      alterself.watching("YouTube"),
      alterself.custom_status("Busy 🔧"),
  ]

  async def cycle_presence():
      index = 0
      while True:
          activity = ACTIVITIES[index % len(ACTIVITIES)]
          await bot.change_presence(status="online", activities=[activity])
          index += 1
          await asyncio.sleep(60)

  @bot.on_event
  async def on_ready():
      print(f"Ready: {bot.me.username}")
      asyncio.create_task(cycle_presence())

  bot.run()
  ```

  <Tip>
    Swap the 60-second interval for any value you like. Avoid going below 30 seconds. Updating presence too frequently can cause Discord to silently drop updates.
  </Tip>
</Accordion>
