A modern, beautiful Discord bot library for Python 3.10+
Project description
Disquerd
A modern, performant Discord bot library for Python 3.10+
Built on Discord API v10 · msgspec-powered models · Full component support
Features
- Discord API v10 — no legacy cruft, only the latest API
- msgspec Struct models — zero-copy JSON decoding, strict typing, no silent field misses
- Slash commands — full support with options (USER, CHANNEL, ROLE, INTEGER, STRING, BOOLEAN, NUMBER, ATTACHMENT, MENTIONABLE)
- Prefix commands — with aliases,
shlexargument parsing,case_insensitive,strip_after_prefix - Context menus — user commands and message commands
- Subcommands & groups —
SubCommand,SubCommandGroupfor organized command trees - Autocomplete — first-class autocomplete handler support
- Components V1 —
View,Button,SelectMenu,ActionRow,Modal,TextInput - Components V2 —
Container,Section,Separator,TextDisplay,MediaGallery - Modal system —
ModalBuilder,@bot.modal()decorator,ModalContext.value() - Button listeners —
@bot.button()decorator,ComponentContextwithdefer_update() - Cog system —
Cog.listener()for event handlers inside cogs, auto-registration of commands - Command checks —
is_owner,has_permissions,has_role,has_any_role,guild_only,dm_only,nsfw,cooldown - Global checks —
@bot.checkfor prefix-wide validation - Invoke hooks —
@bot.before_invoke/@bot.after_invoke - Error events —
SLASH_COMMAND_ERROR,COMMAND_ERROR,COMPONENT_ERROR,MODAL_ERROR - Resolved objects —
ctx.get_user(),ctx.get_channel(),ctx.get_role()from interactionresolveddata - Moderation —
bot.mute(),bot.unmute(),bot.set_nickname(),bot.set_slowmode(),bot.purge_messages() - Rich embeds —
Embedwith fluent builder API,embed()shorthand,ctx.send_success()/ctx.send_error() - Confirm dialogs —
ctx.confirm()withConfirmView(auto-responds to interactions) - Intents system — bit-flag
Intentsclass withALL,DEFAULT,ALL_PRIVILEGED - Beautiful logging — ANSI-colored formatter, startup banner with bot name/guilds/commands/latency
- Lazy imports —
__getattr__-based, no circular dependency issues - Rate limiting — per-bucket rate limit handling with
X-RateLimit-*header awareness - zlib-stream — full zlib-stream gateway decompression with session-persistent decompressor
- Test mode — interactive CLI REPL for local testing
- CLI —
disquerd init <name>scaffolding
Installation
pip install disquerd
For development:
pip install disquerd[dev]
For performance extras:
pip install disquerd[speed]
Quick Start
Minimal Bot
import disquerd
bot = disquerd.Bot("YOUR_TOKEN")
@bot.slash_command(name="ping", description="Check latency")
async def ping(ctx):
await ctx.respond("Pong!")
bot.run()
Full-Featured Bot
import disquerd
bot = disquerd.Bot(
"YOUR_TOKEN",
intents=disquerd.Intents.default(),
prefix=["!", "?"],
case_insensitive=True,
strip_after_prefix=True,
)
# ── Slash command with options ──
@bot.slash_command(
name="avatar",
description="Show user avatar",
options=[
{"type": 6, "name": "user", "description": "Which user", "required": False},
],
)
async def avatar(ctx):
target = ctx.get_user("user")
url = getattr(target, "avatar_url", None)
if not url:
return await ctx.send_error("No avatar")
e = disquerd.Embed(title="Avatar", colour=0x5865F2).set_image(url)
await ctx.respond(embed=e)
# ── Prefix command with aliases ──
@bot.command(name="hello", aliases=["hi", "hey"])
async def hello(ctx):
await ctx.reply(f"Hello, <@{ctx.author_id}>!")
# ── Modal with builder ──
@bot.slash_command(name="feedback", description="Send feedback")
async def feedback(ctx):
m = disquerd.ModalBuilder(title="Feedback", custom_id="fb")
m.add_text("topic", "Topic", placeholder="What about?")
m.add_text("body", "Body", style=2, max_length=1000)
await ctx.show_modal(m.build())
@bot.modal(custom_id="fb")
async def on_feedback(ctx):
topic = ctx.value("topic", "No topic")
body = ctx.value("body", "Empty")
await ctx.respond(f"Thanks! **{topic}**: {body[:200]}")
# ── Button listener ──
@bot.button(custom_id="click_me")
async def on_click(ctx):
await ctx.update_message(content="Clicked!")
# ── Cog with listener ──
class MyCog(disquerd.Cog):
@disquerd.Cog.listener(disquerd.EventType.GUILD_MEMBER_ADD)
async def on_member_join(self, member):
gid = getattr(member, "guild_id", None)
if gid:
await self.bot.rest.create_message(
channel_id=123456789,
content=f"Welcome <@{getattr(member, 'id', '?')}>!",
)
bot.add_cog(MyCog(bot))
# ── Hooks ──
@bot.before_invoke
async def log_cmd(ctx):
print(f"Command by {ctx.author_id}")
@bot.check
async def global_check(ctx):
return True
# ── Events ──
@bot.event(disquerd.EventType.READY)
async def on_ready():
await bot.change_presence(activity={"name": "with disquerd", "type": 0})
@bot.event(disquerd.EventType.SLASH_COMMAND_ERROR)
async def on_slash_err(ctx, error):
await ctx.send_error(str(error))
bot.run()
Option Types Reference
When defining slash command options, use these type values:
| Type | Value | Description |
|---|---|---|
STRING |
3 | Text input |
INTEGER |
4 | Whole number |
BOOLEAN |
5 | True/false |
USER |
6 | User (with resolved) |
CHANNEL |
7 | Channel (with resolved) |
ROLE |
8 | Role (with resolved) |
MENTIONABLE |
9 | User or role |
NUMBER |
10 | Floating-point number |
ATTACHMENT |
11 | File attachment |
Access resolved objects in command handler:
ctx.get_user("option_name")— returnsMemberorUserctx.get_channel("option_name")— returnsChannelctx.get_role("option_name")— returnsRole
Event Types
All EventType enum values available for @bot.event():
| Event | Description |
|---|---|
READY |
Bot connected and ready |
RESUMED |
Gateway session resumed |
MESSAGE_CREATE |
New message received |
MESSAGE_UPDATE |
Message edited |
MESSAGE_DELETE |
Message deleted |
INTERACTION_CREATE |
Any interaction |
GUILD_CREATE |
Bot joined / guild available |
GUILD_UPDATE |
Guild updated |
GUILD_DELETE |
Bot removed from guild |
GUILD_MEMBER_ADD |
Member joined |
GUILD_MEMBER_UPDATE |
Member updated |
GUILD_MEMBER_REMOVE |
Member left/kicked |
GUILD_ROLE_CREATE |
Role created |
GUILD_ROLE_UPDATE |
Role updated |
GUILD_ROLE_DELETE |
Role deleted |
CHANNEL_CREATE |
Channel created |
CHANNEL_UPDATE |
Channel updated |
CHANNEL_DELETE |
Channel deleted |
COMMAND |
Prefix command invoked |
COMMAND_COMPLETION |
Prefix command completed |
COMMAND_ERROR |
Prefix command errored |
SLASH_COMMAND_ERROR |
Slash command errored |
COMPONENT_ERROR |
Component interaction errored |
MODAL_ERROR |
Modal submit errored |
Bot Properties
| Property | Type | Description |
|---|---|---|
user |
User | None |
Bot's own user |
guilds |
list[Guild] |
Cached guilds |
application_id |
int | None |
Application ID |
state |
State |
Internal state/cache |
rest |
RESTClient |
REST API client |
cogs |
dict[str, Cog] |
Loaded cogs (copy) |
modals |
dict[str, Modal] |
Registered modals (copy) |
button_listeners |
dict[str, Callable] |
Button handlers (copy) |
username |
str |
Bot's display name |
avatar_url |
str | None |
Bot's avatar URL |
mention |
str |
Bot's mention string |
latency |
float |
Heartbeat interval (seconds) |
latency_ms |
float |
Heartbeat interval (milliseconds) |
Architecture
disquerd/
├── client.py # Bot, Intents
├── gateway.py # Gateway v10: identify, resume, heartbeat, zlib-stream
├── rest.py # RESTClient: all REST endpoints
├── state.py # State: cache + msgspec parsing with ID coercion
├── _http.py # HTTPClient: rate-limit-aware requests
├── _websocket.py # WebSocketClient: zlib decompression
├── _registry.py # Global REST registry (msgspec workaround)
├── _types.py # Status, EventType, MISSING sentinel
├── log.py # ANSI formatter, banner, logging setup
├── template.py # quick(), use_template()
├── test_mode.py # Interactive REPL test mode
├── commands/
│ ├── core.py # CommandBase, CooldownMapping
│ ├── slash.py # SlashCommand
│ ├── prefix.py # PrefixCommand
│ ├── context.py # ApplicationContext, ComponentContext, ModalContext, Context
│ ├── context_menu.py # UserCommand, MessageCommand
│ ├── subcommands.py # SubCommand, SubCommandGroup
│ ├── autocomplete.py # autocomplete decorator
│ ├── checks.py # is_owner, has_permissions, etc.
│ ├── cog.py # Cog + Cog.listener()
│ └── converters.py # UserConverter, ChannelConverter, etc.
├── models/
│ ├── user.py # User
│ ├── guild.py # Guild
│ ├── channel.py # Channel, TextChannel, VoiceChannel, etc.
│ ├── message.py # Message, Attachment
│ ├── member.py # Member
│ ├── role.py # Role
│ ├── emoji.py # Emoji, PartialEmoji
│ ├── interaction.py # Interaction, InteractionType
│ ├── application.py # ApplicationCommand, ApplicationCommandOption
│ ├── component.py # Component models
│ ├── extras.py # AllowedMentions, Thread, Invite, Sticker, etc.
│ └── webhook.py # Webhook
├── ui/
│ ├── view.py # View
│ ├── button.py # Button, ButtonStyle
│ ├── select.py # SelectMenu, SelectOption
│ ├── modal.py # Modal, ModalBuilder, TextInput, combine_text_inputs
│ ├── action_row.py # ActionRow
│ └── components_v2.py # Container, Section, Separator, etc.
├── events/
│ └── dispatch.py # EventDispatcher
├── errors/
│ ├── gateway.py # GatewayError
│ ├── rest.py # RESTError, HTTPError, RateLimitError, etc.
│ └── command.py # CommandError, CommandCheckFailure, etc.
└── utils/
├── snowflake.py # Snowflake
├── cache.py # Cache, LRUCache, TTLCache
├── colour.py # Colour (int subclass with named constructors)
├── embed.py # Embed, embed() shorthand
├── permissions.py # Permissions
├── file.py # File
├── pagination.py # Paginator
└── confirm.py # ConfirmView
Logging
By default, Disquerd is silent (WARNING+ level). Enable verbose output:
disquerd.verbose_logging()
Or pass debug=True to Bot():
bot = disquerd.Bot("TOKEN", debug=True)
CLI
# Scaffold a new bot project
disquerd init mybot
# Show version
disquerd version
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file disquerd-0.1.0.tar.gz.
File metadata
- Download URL: disquerd-0.1.0.tar.gz
- Upload date:
- Size: 57.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
f5f61796d60c488b814cbfb9fe34492807f277f5cc8a5fd746843c39f2ceefc1
|
|
| MD5 |
3432aa5fa9d30c1a6db5630cc0bad9f5
|
|
| BLAKE2b-256 |
fd9d09ad7f1f7b8f5a0d5fd15b38d1e973a226a4674a7607d005e9c58bf4c5ba
|
File details
Details for the file disquerd-0.1.0-py3-none-any.whl.
File metadata
- Download URL: disquerd-0.1.0-py3-none-any.whl
- Upload date:
- Size: 77.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9778e2ba30ed0c3cf63e6ad2fbfec1ecb7bd41ea8ac00ec5674fd9a32da12679
|
|
| MD5 |
e6b217af6c65f754a68354bf7a55bbb1
|
|
| BLAKE2b-256 |
aa9da9e2dd03b98307f199e26aecc01ebdbdea8ae7831b7d1f08d36d1140ae38
|