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.reply / wizard.send — Unified Message Helpers

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

wizard.color() — String Color Resolver

wizard.color("green")      # Color.green()
wizard.color("#5865F2")    # Color.from_str()
wizard.color(0x5865F2)     # Color(0x5865F2)
wizard.color("random")     # Color.random()

Simple Formatting Helpers

wizard.bold("text")        # **text**
wizard.italic("text")      # *text*
wizard.underline("text")   # __text__
wizard.strike("text")      # ~~text~~
wizard.spoiler("text")     # ||text||
wizard.code("text")        # `text`
wizard.codeblock("code", "py")  # ```py\ncode\n```
wizard.quote("text")       # > text
wizard.link("label", "url")     # [label](url)

Mention & Timestamp Helpers

wizard.mention(user)             # <@user_id>
wizard.mention_channel(channel)  # <#channel_id>
wizard.mention_role(role)        # <@&role_id>
wizard.ts(datetime, style="R")   # <t:1234567890:R>
# Styles: R=relative, d=short date, D=long date, t=short time, T=long time, f=short datetime, F=long datetime

Constructor Helpers

wizard.intents(all=True)                  # Intents.all()
wizard.intents(messages=True, members=True)  # specific flags
wizard.perms(kick_members=True)           # Permissions(...)
wizard.activity("Playing Minecraft")      # Activity(playing, "Minecraft")
wizard.activity("Listening to Lofi", type="listening")
wizard.file("image.png")                  # File("image.png")
wizard.obj(123456789)                     # Object(123456789)
wizard.mentions(users=True, roles=True)   # AllowedMentions(...)

UI Shortcuts

wizard.button("Click", style="primary")     # Button
wizard.button("Danger", style="danger")     # red button
wizard.select(["A", "B", "C"], placeholder="Pick")
wizard.select([{"label":"X","value":"1"}])

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.0.tar.gz (56.4 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.0-py3-none-any.whl (49.1 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: discord_wizard-1.5.0.tar.gz
  • Upload date:
  • Size: 56.4 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.0.tar.gz
Algorithm Hash digest
SHA256 5eaa3d24f8edeea3eae04b5ca715cf5efdcb43be80ee279e8f16435e388e6332
MD5 f80703ae7575aaff651cc51b9a852d57
BLAKE2b-256 8fcaee6258aff85423895863124d729d97d7246edb6b6bf228d3bd4384be022e

See more details on using hashes here.

File details

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

File metadata

  • Download URL: discord_wizard-1.5.0-py3-none-any.whl
  • Upload date:
  • Size: 49.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.4

File hashes

Hashes for discord_wizard-1.5.0-py3-none-any.whl
Algorithm Hash digest
SHA256 e8c953d45e30292f6736afcfdf27dbe8c86e3f8a6e0d8f3a2d07797f1115b0bf
MD5 b96a51cf54fbd603b37deb59dd30af46
BLAKE2b-256 42863f72f491e4e2de161bfdc6fb00d195a5c961dc5c396d72f0c2258c9f4a61

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