Skip to main content

A unified Discord.py utility library — events, rate limiting, UI, permissions, tasks, errors, simulation, and recording.

Project description

discord-wizard

A batteries-included utility layer for discord.py. Zero import discord — all 318+ discord.py types re-exported through wizard.*.

from discord.wizard import wizard   # convenience singleton
# or
from discord.wizard import Wizard   # class, if you prefer
w = Wizard()

bt = wizard.Bot(command_prefix="!")  # wizard.bot also works
wizard.enable_stability(bt)

@bt.slash()
async def hello(interaction: wizard.Interaction):
    await wizard.respond(interaction, "Hello!")

Install

pip install discord-wizard

Requires Python 3.10+ and discord.py 2.x (installed automatically).

Why no import discord?

Every public name from discord.py is accessible as wizard.<Name>:

ChannelType, AppInfo, VoiceClient, FFmpegPCMAudio,
StageChannel, ForumChannel, Thread, DMChannel,
Permissions, MemberCacheFlags, AuditLogEntry,
Webhook, Sticker, Emoji, Template, Invite,
ScheduledEvent, AutoModRule, OnboardingPrompt,
Subscription, SKU, Entitlement, Poll, SoundboardSound,
...

All 318+ names. The only exception: wizard replaces Bot and Cog with its own enhanced versions (wizard.Bot / wizard.Cog).

Quick Start

from discord.wizard import wizard

bt = wizard.bot(command_prefix="!", intents=wizard.Intents.all())
wizard.enable_stability(bt)  # crash protection, auto-restart tasks, graceful shutdown


# ── Slash command with permission check ──────────────────────────────
@bt.slash()
@wizard.require("admin")
async def panel(interaction: wizard.Interaction):
    """Deploy a button panel."""
    p = wizard.Panel(title="Menu", buttons=[
        wizard.Button(label="Click me", style="primary",
                      callback=lambda i: wizard.respond(i, "Clicked!")),
    ])
    await p.send(interaction)


# ── Prefix command with type hints ──────────────────────────────────
@bt.cmd()
async def status(ctx, member: wizard.Member):
    e = wizard.embed(f"{member} Status", color="blue", fields=[
        ("ID", str(member.id)),
        ("Joined", str(member.joined_at)),
    ])
    await wizard.send(ctx, embed=e)


# ── Event with filter ──────────────────────────────────────────────
@wizard.on("member_join", guild_id=12345)
async def welcome(member):
    await member.send(f"Welcome {member.mention}!")


# ── Scheduled task ──────────────────────────────────────────────────
@wizard.every("30m")
async def clean_cache():
    print("Cache cleaned")


bt.run("TOKEN")

Module Overview

wizard.on — Event Decorators

Three syntaxes, same result:

@wizard.on("member_join")           # string — simplest
@wizard.on.member_join              # flat attribute
@wizard.on.member.join              # grouped attribute

@wizard.on("member_join", guild_id=12345)  # with filter

Available events: member_join/leave/ban/unban, message_create/delete/edit, voice_join/leave/move, role_create/delete/add, reaction_add/remove.

wizard.require — Permission Decorators

@wizard.require("admin")               # built-in level
@wizard.require("role:Admin")          # role name
@wizard.require_any("mod", "admin")    # any of
@wizard.require_all("admin", "owner")  # all of
@wizard.require_custom(lambda m: m.id == 123)
wizard.configure_permissions(guild_id, roles={"admin": ["Admin", "Staff"]})

wizard.ui — Declarative UI Components

# Form (modal)
form = wizard.Form(title="Feedback", fields=[
    wizard.TextInput(label="Name", max_length=50),
    wizard.TextInput(label="Message", style="long"),
], on_submit=callback)
await form.send(interaction)

# Panel (button group)
panel = wizard.Panel(title="Actions", buttons=[
    wizard.Button(label="Save", style="success", callback=save_fn),
    wizard.Button(label="Delete", style="danger", callback=del_fn),
])
await panel.send(interaction)        # respond to interaction
await panel.send_to(channel, embed=e)  # send to channel

# Other components: Dropdown, MultiSelect, Tabs, Paginator

wizard.tasks — Task Scheduling

@wizard.every("5m")
@wizard.cron("0 */6 * * *")
@wizard.at("09:00", timezone="US/Eastern")
# retry=3, retry_delay=10, on_error=handler

wizard.embed — One-Shot Embed Builder

e = wizard.embed("Title", "Description", color="green", fields=[
    ("Name", "Value", True),   # (name, value, inline)
    ("Name2", "Value2"),       # inline defaults to False
])

Colors accept strings: "blue", "green", "red", "#5865F2", 0x5865F2.

wizard.respond / wizard.send — Unified Message Helpers

await wizard.respond(interaction, "Hello", ephemeral=True)  # auto followup
await wizard.send(channel, embed=e)                          # channel or ctx
await wizard.send(interaction, "Hello")                      # also works

wizard.rateguard — Rate Limit Protection

wizard.enable_rateguard(bt, warn_at=0.8, max_retries=3)
print(wizard.rateguard_stats())  # {requests_sent: 42, ...}

wizard.errors — Error Formatting

wizard.enable_errors(bt, embed_mode=True)  # formats errors as embeds
print(wizard.format_terminal(error))         # structured terminal output

wizard.stability — Crash Protection (one-call)

wizard.enable_stability(bt)
# Crash-proof dispatch, safe tree.on_error, task auto-restart,
# connection health monitoring, graceful SIGINT/SIGTERM shutdown

wizard.simulate — Offline Bot Testing

discord-wizard simulate --port 8321  # web UI at localhost:8321
from discord.wizard import wizard

async def test():
    client = wizard.MockClient()
    result = await client.send("!hello")
    print(result.calls)

wizard.recorder — Event Recording

rec = wizard.Recorder(backend=wizard.SQLiteStore("bot.db"))
session = await rec.session.start(name="test")
# ... bot runs ...
await rec.session.stop()
await rec.export_json("log.json")

Backends: MemoryStore, SQLiteStore, PostgreSQLStore.

Discord.py Re-exports (all 318+)

Every public name from discord.py is accessible as wizard.<Type> — no import discord needed:

Channel types: TextChannel, VoiceChannel, CategoryChannel, StageChannel, ForumChannel, Thread, DMChannel, GroupChannel, PartialMessageable

Models: Member, User, Guild, Role, Message, Interaction, Embed, Emoji, Sticker, StickerPack, Webhook, Template, Invite, AppInfo, PartialAppInfo, ClientUser, Team, TeamMember, BanEntry, AuditLogEntry, AuditLogDiff, ScheduledEvent, AutoModRule, AutoModTrigger, Onboarding, OnboardingPrompt, Poll, Entitlement, SKU, Subscription, SoundboardSound

Enums: ChannelType, VideoQualityMode, VoiceRegion, NotificationLevel, ContentFilter, VerificationLevel, MFALevel, NSFWLevel, PrivacyLevel, EntityType, EventStatus, StickerType, StickerFormatType, AppCommandType, InteractionType, ComponentType, ButtonStyle, TextStyle, MessageType, AuditLogAction, AuditLogActionCategory, AutoModRuleEventType, AutoModRuleTriggerType, ForumLayoutType, ForumOrderType, InviteTarget, Locale, SKUType, SubscriptionStatus, WebhookType, EntitlementType, EntitlementOwnerType

Flags: Permissions, PermissionOverwrite, Intents, MemberCacheFlags, MemberFlags, MessageFlags, ChannelFlags, SystemChannelFlags, ApplicationFlags, PublicUserFlags, UserFlags, RoleFlags, AttachmentFlags, SKUFlags, InviteFlags, EmbedFlags

Exceptions: DiscordException, HTTPException, Forbidden, NotFound, InvalidData, ClientException, LoginFailure, GatewayNotFound, ConnectionClosed, PrivilegedIntentsRequired, InteractionResponded, RateLimited, DiscordServerError, MissingApplicationID, FFmpegProcessError

Voice: VoiceClient, VoiceProtocol, VoiceState, FFmpegPCMAudio, FFmpegOpusAudio, PCMAudio, PCMVolumeTransformer, AudioSource, opus

And everything else: Color, Colour, File, Asset, Object, Activity, Game, Status, CustomActivity, Spotify, Streaming, SelectOption, View, Button, ActionRow, TextInput, Component, AllowedMentions, PartialMessage, MessageReference, Reaction, ShardInfo, SessionStartLimits, Widget, WelcomeScreen, Integration, BotIntegration, StreamIntegration, IntegrationAccount, IntegrationApplication, VoiceChannelEffect, SoundboardDefaultSound, Container, Collectible, RoleTags, RoleSubscriptionInfo, ThreadMember, StageInstance, GuildPreview, GuildProductPurchase, PrimaryGuild, UnfurledMediaItem, MediaGalleryItem, CallMessage, DeletedReferencedMessage, MessageInteraction, MessageInteractionMetadata, MessageSnapshot, MessageApplication, RawMemberRemoveEvent, RawMessageDeleteEvent, RawMessageUpdateEvent, RawBulkMessageDeleteEvent, RawReactionActionEvent, RawReactionClearEvent, RawReactionClearEmojiEvent, RawTypingEvent, RawPresenceUpdateEvent, RawIntegrationDeleteEvent, RawThreadDeleteEvent, RawThreadMembersUpdate, RawThreadUpdateEvent, RawPollVoteActionEvent, RawAppCommandPermissionsUpdateEvent, PartialEmoji, PartialIntegration, PartialInviteChannel, PartialInviteGuild, PartialWebhookChannel, PartialWebhookGuild, SyncWebhook, SyncWebhookMessage, BaseActivity, BaseSoundboardSound, CustomActivity, `SpeakingState

Syntax Comparison

Pattern Raw discord.py Wizard
Event @bot.event / def on_member_join(m) @wizard.on("member_join")
Event filter Custom check function @wizard.on("member_join", guild_id=123)
Embed 3+ lines wizard.embed("T", fields=[...], color="blue")
Interaction response Check + followup + send_message wizard.respond(i, "msg", ephemeral=True)
Send (any target) Type check + different methods wizard.send(target, embed=e)
Color Color.blue(), Color.from_str() color="blue" or color="#5865F2"
Permissions Custom decorator @wizard.require("admin")
Task scheduling asyncio + manual @wizard.every("5m")

Import Styles

from discord.wizard import wizard   # singleton — most common
wizard.Bot(command_prefix="!", intents=wizard.Intents.all())

from discord.wizard import Wizard   # class — explicit
w = Wizard()
w.Bot(command_prefix="!", intents=w.Intents.all())

Both are equivalent. The singleton wizard is pre-instantiated and ready to use.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

discord_wizard-1.5.0rc0.tar.gz (54.2 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

discord_wizard-1.5.0rc0-py3-none-any.whl (47.6 kB view details)

Uploaded Python 3

File details

Details for the file discord_wizard-1.5.0rc0.tar.gz.

File metadata

  • Download URL: discord_wizard-1.5.0rc0.tar.gz
  • Upload date:
  • Size: 54.2 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for discord_wizard-1.5.0rc0.tar.gz
Algorithm Hash digest
SHA256 5760af66b4d1a14daa06573ae90411c408223d46c8f33974e37f5fb21f06a907
MD5 80a2059219f7138e15be29a5a6f45d18
BLAKE2b-256 200ba14c5f9a72b9ba415a94eca0b71ec143296d72e2bdc54fd2f1622a1aeae7

See more details on using hashes here.

File details

Details for the file discord_wizard-1.5.0rc0-py3-none-any.whl.

File metadata

File hashes

Hashes for discord_wizard-1.5.0rc0-py3-none-any.whl
Algorithm Hash digest
SHA256 edc56178d6df99eda1ffd2c39f8a2332ba95d14846402f986590c52fe0339d9f
MD5 e07cd818ef25dff0e0235a02052da0ce
BLAKE2b-256 f11c5c076e11e1d4a476a18ff148051a7673fba64400f8d8aa60bf3ab8d439bb

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page