A production-grade Discord bot framework that removes boilerplate around commands, plugins, middleware, and AI orchestration. Build command-driven bots fast. Scale to AI-powered agents with intelligent tool routing, multi-provider LLM support, and permission-gated function calling.
- Install from GitHub:
pip install git+https://github.com/rolling-codes/EasyCord.git - Create a bot with one slash command.
- Split features into plugins once the bot grows.
from easycord import Bot
bot = Bot()
bot.load_builtin_plugins()
@bot.slash()
async def ping(ctx):
await ctx.respond("Pong!")
bot.run("YOUR_TOKEN")If you want the shortest possible path to a working bot, open docs/getting-started.md.
pip install git+https://github.com/rolling-codes/EasyCord.gitgit clone https://github.com/rolling-codes/EasyCord.git
cd EasyCord
pip install .pip install -e ".[dev]"Build bots that speak your server's language:
# Define translations in a locale file (en.json)
{
"commands": {
"ping": {
"response": "Pong!"
}
}
}
# Use in your command
@bot.slash()
async def ping(ctx):
await ctx.respond(ctx.t("commands.ping.response"))Initialize the bot with localization:
from easycord import Bot, LocalizationManager
locales = LocalizationManager()
locales.register("en", "locales/en.json")
locales.register("es", "locales/es.json")
bot = Bot(localization=locales, default_locale="en")Translations fallback gracefully: user locale → guild locale → default locale → English. See docs/localization.md for the full guide.
Ask Claude directly from Discord:
from easycord import Bot
from easycord.plugins import OpenClaudePlugin
bot = Bot()
bot.add_plugin(OpenClaudePlugin(api_key="sk-ant-...")) # or ANTHROPIC_API_KEY env var
bot.run("YOUR_TOKEN")Members use /ask "your question" to query Claude API. Responses are automatically truncated to Discord's 2000-char limit.
Setup: Install anthropic SDK and set ANTHROPIC_API_KEY environment variable.
See docs/examples.md for examples with OpenAI, Gemini, Groq, Ollama, and custom providers.
Let AI safely call into your bot via @ai_tool decorator:
from easycord import Plugin, ai_tool, ToolSafety
from datetime import timedelta
class ModToolsPlugin(Plugin):
@ai_tool(description="Check if user is a member of the server")
async def is_member(self, ctx, user_id: int):
try:
await ctx.guild.fetch_member(user_id)
return "User is a member"
except:
return "User is not a member"
@ai_tool(
description="Timeout a user from the server",
safety=ToolSafety.CONTROLLED,
require_admin=True,
parameters={
"type": "object",
"properties": {
"user_id": {"type": "integer"},
"seconds": {"type": "integer"}
}
}
)
async def timeout_user(self, ctx, user_id: int, seconds: int = 3600):
member = await ctx.guild.fetch_member(user_id)
await member.timeout(timedelta(seconds=seconds))
return f"Timed out {member.name} for {seconds}s"Tools are categorized by safety:
- SAFE — read-only (queries, lookups, member info)
- CONTROLLED — validated actions (moderation, database writes, role changes)
- RESTRICTED — never expose to AI (admin-only, destructive operations)
Each tool can require require_admin=True, specific allowed_roles, or allowed_users.
Use the orchestration layer for intelligent provider selection with fallback chains:
from easycord import Bot, Plugin, slash, Orchestrator, FallbackStrategy, RunContext
from easycord.plugins import AnthropicProvider, GroqProvider, OpenAIProvider
bot = Bot()
# Create orchestrator with fallback chain
orchestrator = Orchestrator(
strategy=FallbackStrategy([
AnthropicProvider(), # Try first
GroqProvider(), # Fallback
OpenAIProvider(), # Last resort
]),
tools=bot.tool_registry, # Auto-includes @ai_tool methods
)
class AIPlugin(Plugin):
@slash(description="Ask AI with tool access")
async def ask_with_tools(self, ctx, prompt: str):
await ctx.defer()
response = await orchestrator.run(
RunContext(
messages=[{"role": "user", "content": prompt}],
ctx=ctx,
max_steps=5, # Max tool calls before returning
)
)
await ctx.respond(response.text[:2000])
bot.add_plugin(AIPlugin())
bot.run("YOUR_TOKEN")The orchestrator:
- Routes intelligently: tries best provider first, falls back if it fails
- Detects tool calls: when AI requests a function call
- Executes safely: checks permissions, enforces timeouts, handles exceptions
- Loops: feeds tool results back to AI, continues until final response
- Respects constraints: admin-only, role-gated, and user-allowlisted tools
Foundations:
- Slash commands, buttons, select menus, modals — all with decorators
- Permission checks (built-in or custom via middleware)
- Cooldowns, rate limiting, error handling
- Plugins: reusable feature bundles with lifecycle hooks
- Per-guild configuration and persistent storage (SQLite or in-memory)
- Localization: user/guild/default locale fallback
AI & Orchestration:
- 9 LLM providers: Anthropic (Claude), OpenAI (GPT), Google (Gemini), Groq, Mistral, HuggingFace, Together.ai, Ollama (local), LiteLLM (proxy)
- Multi-provider routing: fallback chain (try Anthropic → Groq → OpenAI if first fails)
- Tool registration: expose Discord commands and custom functions to AI via
@ai_tooldecorator - Permission-gated tools: SAFE (read-only), CONTROLLED (validated), RESTRICTED (never expose) — each tool can require admin/roles/users
- Tool execution loop: AI detects function calls, executes with timeout + exception handling, feeds results back
- Smart truncation: responses auto-fit Discord's 2000-char limit
Developer experience:
- Minimal boilerplate — decorators handle registration
- Middleware for cross-cutting concerns (logging, auth, rate limits)
- Fluent builder (
Composer) for declarative bot setup - Context object with shortcuts for common operations
- Embed helpers with buttons/selects built-in
Built for the moment a bot stops being a weekend project and becomes production infrastructure. EasyCord started as a way to eliminate repetitive Discord bot boilerplate, then evolved into a complete orchestration platform for AI-driven agents.
| Task | Raw discord.py |
This framework |
|---|---|---|
| Slash commands | Build command tree, sync manually | @bot.slash(...) |
| Permission checks | Repeat in each command | Declare on decorator |
| Cooldowns | Track timestamps yourself | cooldown=... |
| Components | Wire interaction handlers by ID | @bot.component(...) |
| Middleware | Write custom decorators | bot.use(log_middleware()) |
| Plugins | Custom Cog wiring |
Plugin + lifecycle |
| AI integration | Build from discord.py + LLM SDK | Orchestrator + ToolRegistry |
| Tool calling | Manual prompt engineering | @ai_tool + routing |
my_bot/
├── bot.py
├── plugins/
│ ├── fun.py
│ └── moderation.py
└── pyproject.toml
- Keep
bot.pyfor startup and wiring. - Put each feature in its own plugin.
- Move shared config into
ServerConfigStorewhen you need it.
Commands & Interaction:
Botfor slash commands, events, components, and plugin loading@slash,@on,@component,@modal,@taskdecoratorsSlashGroupfor command namespacesContextfor replies, DMs, embeds, moderationEmbedCardand themed embed helpers
Plugins & Configuration:
Pluginfor reusable feature bundles withon_load()/on_unload()Bot.dbfor guild-scoped storage (SQLite or in-memory)ServerConfigStorefor per-guild settings without a databaseComposerfor fluent declarative setup
Middleware & Utilities:
- Middleware for logging, error handling, rate limiting, permission guards
- Built-in:
guild_only,admin_only,allowed_roles,has_permission,boost_only LocalizationManagerfor multi-language support
AI & Orchestration:
- 9
AIProviderimplementations (Anthropic, OpenAI, Gemini, Groq, Mistral, HuggingFace, Together, Ollama, LiteLLM) Orchestratorfor provider routing + tool execution loopsToolRegistryfor explicit tool registration with permission gates@ai_tooldecorator for AI-callable functionsFallbackStrategyfor multi-provider resilience
- Read
docs/getting-started.mdto make your first bot. - Read
docs/concepts.mdto understand the pieces. - Copy
examples/basic_bot.pyand make one change. - Move a command into a plugin once the file starts feeling crowded.
examples/basic_bot.py: the smallest practical starter botexamples/plugin_bot.py: a feature split across pluginsexamples/group_bot.py: grouped slash commands withSlashGroupdocs/index.md: documentation homedocs/examples.md: patterns and snippetsdocs/fork-and-expand.md: how to grow a real bot projectserver_commands/__init__.py: one place to load the bundled plugins
This project started as a way to cut down the repetitive work of Discord bot development for a school server. That original goal still drives the project: make the first command easy, then make the second and third commands feel just as simple.
MIT