EasyCord v5.61.1 is a patch release focused on making the v5.61 developer experience match the documented API, especially around lifecycle hooks, grouped plugins, built-in plugin reliability, and docs accuracy.
What was fixed
Lifecycle hooks now work in real bots
The docs described bot.hooks, before_command, after_command, on_plugin_load, and on_plugin_unload, but the main Bot object did not actually initialize a HookRegistry, and the command/plugin paths did not fire those hooks. This made the documented hook examples fail or silently do nothing.
Fixed by:
- Creating
bot.hooksduringBotinitialization. - Firing
before_commandandafter_commandaround slash command callback execution. - Firing
on_plugin_loadandon_plugin_unloadduring plugin load, late-load, unload, and reload flows. - Adding regression tests for command hook order and plugin unload hooks.
SlashGroup plugins behave like normal plugins
Several grouped-plugin paths were incomplete compared with regular Plugin loading. This affected developers building command groups as plugins.
Fixed by:
- Allowing
Bot.load_builtin_plugins()andBot.add_plugin()to registerSlashGroupinstances throughadd_group(). - Enforcing
requiresdependencies for grouped plugins and raisingPluginDependencyErrorwhen dependencies are missing. - Firing lifecycle hooks for late-loaded groups.
- Cleaning cooldown registry entries for nested group commands on unload so hot reloads do not leave stale callbacks or plugin state behind.
Mixin-decorated plugin commands are discovered again
Plain Python mixins with decorated methods were not reliably discovered during plugin scanning. That broke a useful composition pattern for sharing commands, events, components, modals, and subscriptions across plugins.
Fixed by walking the plugin class MRO in source-definition order while skipping only object, then registering decorated methods from mixins as expected.
Plugin @slash(...) matches the direct bot API more closely
The plugin decorator accepted fewer options than Bot.slash(...), so moving a command into a plugin could silently drop metadata.
Fixed by adding support for:
nsfwallowed_contextsallowed_installs
Those values now propagate through the existing command metadata scanner.
Built-in plugin reliability fixes
Fixed several concrete runtime bugs in bundled plugins:
TagsPluginnow registers as the documented/tagslash-command group.- Tag responses disable mentions with
discord.AllowedMentions.none(), preventing stored tag content from pinging users, roles,@everyone, or@here. TicketsPlugin.ticket_open()no longer crashes if the ticket panel message cannot be sent. It now skips persistent view registration when no message ID exists.LevelsPlugininvalidates its config cache after rank and role-reward config changes, so updates apply immediately instead of waiting for the cache TTL.ConfigHelpers.update_atomic()now routes throughServerConfigStore.mutate()instead of a separate load/save sequence, avoiding lost concurrent writes.- Optional JuiceWRLD external-service imports are treated as optional by Pyright rather than blocking release type checks.
Documentation and examples now match the shipped API
The docs sweep corrected user-facing examples that pointed developers at APIs or command names that did not exist in v5.61.
Fixed docs/examples include:
- README version, release links, install URL, and test count.
- Built-in plugin command names such as
/tag set,/ticket_open,/ticket_close,/rep,/rep_check,/giveaway,/giveaway_end,/filter_add,/autorole_add, and related commands. - AI examples now construct providers/orchestrators using the current constructors.
database=replaces staledb=snippets where appropriate.- Default database docs now describe the current local SQLite default instead of saying the default is memory-only.
- Event bus docs now state that callbacks run sequentially in registration order, matching implementation.
- Command sync examples now use
plan_command_sync(...)andsync_commands(dry_run=True, ...)instead of the nonexistentpreview_sync_commands()helper. - Hook unregister examples now use public
HookRegistry.unregister()instead of mutating private callback lists. - Troubleshooting no longer recommends a nonexistent
bot.cacheattribute. - Type-checking docs import
SENDABLE_CHANNEL_TYPESfrom the supported top-level export. - Broken or stale internal doc links were corrected.
Agent review coverage
This release was prepared from multiple read-only agent sweeps plus local integration work:
- Docs agents checked README, docs, examples, command names, AI snippets, database configuration, hook docs, event bus behavior, and links.
- DX/runtime agents checked hook wiring, grouped plugin behavior, decorator parity, mixin discovery, built-in plugin ergonomics, and unfinished public API promises.
- Package/release agents checked version metadata, release asset names, changelog shape, plugin test coverage gates, build output, and release readiness.
Several larger DX ideas from the sweep remain candidates for future releases rather than patch material, including Composer parity for more Bot(...) options, a real remote-fetching sync preview helper, and broader built-in plugin persistence configuration.
Verification
python -m pytest -q-> 1871 passedpython scripts/check_release_metadata.py-> passedpython scripts/verify_plugin_tests.py-> passedruff check easycord tests --select E9,F63,F7,F82-> passedpyright easycord/ tests/-> 0 errorspython -m build --no-isolation-> built wheel and sdistpython -m twine check dist\*-> passed
Artifacts
easycord-5.61.1-py3-none-any.whleasycord-5.61.1.tar.gz
PyPI status
GitHub release publishing succeeded. PyPI upload was attempted, but this machine does not have a Twine API token configured, so the upload stopped with:
ERROR NonInteractive: Credential not found for API token.