Skip to main content

Discord Self Bot Tester (DSBT) Library

Package for testing commands using discord selfbot. Assertions (messages, and autocomplete) and requests (interactions, events, reactions) can be made using the bot.

Install

pip install discord-self-bot-tester

Needs python 3.11 or newer.

Setup

  • Create a Bot with a user token
  • Run it with bot.run(). This has to be called inside a running event loop, and every test has to use that same loop
  • Wait for it with await bot.wait_ready()
  • Index the commands with await bot.index_application_commands(GUILD_ID) (has to happen before sending any command)
  • Make tests and have fun or pain, idk ur pick
  • Call await bot.stop() in a finally so a failed test still closes the socket
from discord_self_bot_tester import Bot
import asyncio

async def main():
    bot = Bot(USER_TOKEN)
    bot.run()
    try:
        await bot.wait_ready()
        await bot.index_application_commands(GUILD_ID)
        # tests go here
    finally:
        await bot.stop()

asyncio.run(main())

Commands

Build the command, then assert on the reply. assert_request sends it for you.

from discord_self_bot_tester.requests import ApplicationCommandBuilder, InteractionType
from discord_self_bot_tester.assertions import MessageAssertionBuilder

cmd = ApplicationCommandBuilder(InteractionType.APP_COMMAND, "ping")\
    .set_arg("count", 3)\
    .compile(APPLICATION_ID, GUILD_ID, CHANNEL_ID)

msg = await MessageAssertionBuilder()\
    .assert_by_message_content("pong")\
    .compile()\
    .assert_request(bot, cmd)

set_main_command("sub") for subcommands. await cmd.send(bot) if u dont care about the reply.

Filters

Which message to pick. Leave them all out and it picks the reply to the command.

  • filter_by_author(user_id)
  • filter_by_channel(channel_id)
  • filter_by_message_update() for deferred or edited replies
  • filter_by_message_flags(64) for ephemeral replies
  • filter_by_followup_message() ties a filtered assertion back to the command

Checks

  • assert_by_message_content(regex)
  • assert_by_mentions([ids])
  • assert_by_mention_roles([ids])

Default deadline is 5 seconds, change it with deadline= on assert_request. Raises TimeoutError if nothing shows up.

Views

Assert what buttons and dropdowns are on the message.

from discord_self_bot_tester.gateway import ButtonStyle

msg = await MessageAssertionBuilder()\
    .assert_by_button("Confirm", style=ButtonStyle.SUCCESS)\
    .assert_by_button_labels(["Confirm", "Cancel"])\
    .assert_by_dropdown(placeholder="Pick one", option_labels=["A", "B"])\
    .compile()\
    .assert_request(bot, cmd)

Then click stuff. Ids get pulled off the message so u only need the label.

from discord_self_bot_tester.requests import press_button, select_dropdown, select_dropdown_values

await MessageAssertionBuilder()\
    .filter_by_message_update()\
    .compile()\
    .assert_request(bot, press_button(msg, "Confirm"))

await select_dropdown(msg, "A", "B", dropdown="Pick one").send(bot)
await select_dropdown_values(msg, USER_ID, dropdown="pick_user").send(bot)

dropdown can be left out if the message only has one. User, role and channel selects take snowflakes, so use select_dropdown_values for those.

Modals

Open it, fill it in by label, submit it.

from discord_self_bot_tester.requests import ModalResponseBuilder
from discord_self_bot_tester.assertions import ModalAssertionBuilder

modal = await ModalAssertionBuilder()\
    .assert_by_modal_title("Sign up")\
    .compile()\
    .assert_request(bot, press_button(msg, "Register"))

submit = ModalResponseBuilder(modal)\
    .select("Name").set_text("oliver")\
    .select("Colour").choose("Green")\
    .select("Agree").set_checked()\
    .compile(GUILD_ID, CHANNEL_ID, APPLICATION_ID)

await MessageAssertionBuilder().compile().assert_request(bot, submit)
  • set_text(str) for text inputs
  • choose(*labels) for selects, radios and checkbox groups
  • set_values(*values) and set_option(value) if u want raw values instead of labels
  • set_checked(bool) for checkboxes
  • select_by_custom_id(id) if the label is annoying

Nested labels work like select("Address", "Postcode"). Required fields left empty fail at compile, not on discord.

Autocomplete

from discord_self_bot_tester.assertions import AutoCompleteAssertionBuilder

res = await AutoCompleteAssertionBuilder().compile().assert_request(
    bot,
    ApplicationCommandBuilder(InteractionType.APPLICATION_COMMAND_AUTOCOMPLETE, "search")
        .set_arg("query", "ab", focused=True)
        .compile(APPLICATION_ID, GUILD_ID, CHANNEL_ID)
)

Other requests

Reactions, events and attachments all have builders. .send(bot) sends them.

from discord_self_bot_tester.requests import build_reaction, build_attachment

await build_reaction("🖥️", MESSAGE_ID, CHANNEL_ID).send(bot)

file = build_attachment(data, "image.png", CHANNEL_ID)
await file.send(bot)
file.get_attachment_id()  # pass this to set_values on a modal file upload

Metadata

Release files for discord-self-bot-tester 0.7.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for discord-self-bot-tester 0.7.0
File Size Uploaded
discord_self_bot_tester-0.7.0.tar.gz 42.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for discord-self-bot-tester 0.7.0
File Interpreter ABI Platform
discord_self_bot_tester-0.7.0-py3-none-any.whl Python 3 none any Details

Total release size: 91.1 kB

Release files / discord_self_bot_tester-0.7.0.tar.gz

Download URL discord_self_bot_tester-0.7.0.tar.gz
Size 42.4 kB
Tags Source
SHA-256 checksum
How to use checksums
b8b1e862986bb6b5681af93db45dc0c7c3fd658d941d0f81c817207ec90b534e
BLAKE2b-256 checksum
How to use checksums
dd88bb983f2661bf825b08e273962b284c2de910abcc6190b5cb66ca8dba2df3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release files / discord_self_bot_tester-0.7.0-py3-none-any.whl

Download URL discord_self_bot_tester-0.7.0-py3-none-any.whl
Size 48.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
05a018aab8136d2a7d5ae8391c6043063c5b53a30f9d6c37a7b50eddb4e09b00
BLAKE2b-256 checksum
How to use checksums
7507c1113fc33b31af65370e7a21462eff5e6bfce62aa0162ccbfe984940df5e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 9, 2026.

Transparency log

Release history Release notifications | RSS feed

0.9.0

2 release files

0.8.0

2 release files

This release

0.7.0 This release

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page