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.9.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.9.0
File Size Uploaded
discord_self_bot_tester-0.9.0.tar.gz 42.5 kB Details

Built distribution (wheel)

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

Total release size: 91.2 kB

Release files / discord_self_bot_tester-0.9.0.tar.gz

Download URL discord_self_bot_tester-0.9.0.tar.gz
Size 42.5 kB
Tags Source
SHA-256 checksum
How to use checksums
f0027150c24d535ca7743c2b0c1cc564e92391995858e9c1a420d2a7a99f7f8d
BLAKE2b-256 checksum
How to use checksums
3bc2a4403c6e2c8bd14ba1247c80d130059fa82504e159a80e61d99159d62866
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 18, 2026.

Transparency log

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

Download URL discord_self_bot_tester-0.9.0-py3-none-any.whl
Size 48.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
a8d82f2cd42300435501121f928b08e97c0fd7167e58019dbd828086e9898977
BLAKE2b-256 checksum
How to use checksums
afc328ebe2f1b1019f1f6d4f5b5a8f0e56a37179c5024ccc6aabc5357d3b6d17
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 18, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.9.0 This release

2 release files

0.8.0

2 release files

0.7.0

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