brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
discord-apiIntegrates Discord API (Gateway, REST, Slash Commands, Webhooks, Voice) using discord.py v2.7+ with proper intent configuration, cog structuring, slash command patterns, and rate-limit handling.
Independent GitHub snapshot · Automated classification · Original documentation governs
name: discord-api description: Integrates Discord API (Gateway, REST, Slash Commands, Webhooks, Voice) using discord.py v2.7+ with proper intent configuration, cog structuring, slash command patterns, and rate-limit handling. license: MIT compatibility: opencode metadata: version: "1.0.0" domain: coding triggers: discord, discord bot, discord.py, slash commands, discord api, discord webhook, discord gateway, how do i make a discord bot archetypes:
Integrates the Discord API — Gateway, REST, Slash Commands, Webhooks, and Voice — using discord.py v2.7+. When loaded, this skill makes the model implement Discord bots and integrations with proper intent configuration, cog-organized command structure, application command registration, modal interactions, and Gateway event handling.
Intents.default() + specific extra intents (e.g., message_content) and pass them to commands.BotCog classes — one file per cog, loaded with await bot.add_cog()@app_commands.command() for slash commands and @app_commands.describe() for parameter hintsawait bot.tree.sync() after registrationdiscord.Forbidden, discord.HTTPException, and discord.NotFound errors in every commanddiscord.Embed for rich message formatting — never send plain text embeds without structureUse this skill when:
discord.py 2.xAvoid this skill for:
coding-slack-api instead)coding-twilio-api or coding-sendgrid-api instead)discord.py v1.x API — v2.7+ uses app_commands for slash commandsConfigure Intents and Initialize Bot — Create Intents.default() and add required intents (message_content, members, presences). Enable them in the Discord Developer Portal under Bot > Privileged Gateway Intents. Instantiate commands.Bot(command_prefix="!", intents=intents). Checkpoint: Verify the bot comes online by monitoring the on_ready() event — log the bot user tag and guild count.
Define Slash Commands — Use @app_commands.command() within a Cog class or directly on bot.tree. Provide descriptions for every command and parameter. Use @app_commands.describe() for user-facing parameter hints. Checkpoint: Run bot.tree.sync() once at startup (or during setup_hook) to register commands with Discord. Verify commands appear in the Discord client.
Handle Command Logic with Error Handling — Implement each command handler with typed parameters. Wrap all API calls in try/except blocks catching discord.Forbidden (missing permissions), discord.HTTPException (API error), and discord.NotFound (resource missing). Send user-friendly error messages via ephemeral=True followups. Checkpoint: Test each command with both valid and invalid inputs to verify error handling paths.
Use Embeds for Rich Responses — Construct discord.Embed with title, description, color, fields, and optionally set_thumbnail() or set_image(). Set the color parameter to distinguish message types (success=green, error=red, info=blue). Checkpoint: Verify embeds render correctly on both desktop and mobile Discord clients.
Deploy and Run — For local development, run bot.run(os.environ["DISCORD_TOKEN"]) directly. For production, use a process manager (systemd, supervisord, or Docker). Never hardcode your token. Checkpoint: Confirm the bot maintains the Gateway connection by monitoring on_resumed() events — excessive reconnects indicate network or rate-limit issues.
import discord
from discord import app_commands
from discord.ext import commands
# ❌ BAD — no intents, no cog, global commands without sync, no error handling
bot = commands.Bot(command_prefix="!")
@bot.command()
async def ping(ctx):
await ctx.send("Pong!")
# ✅ GOOD — proper intents, cog organization, app commands, error handling, embed responses
import logging
from typing import Optional
logger = logging.getLogger(__name__)
intents = discord.Intents.default()
intents.message_content = True # Required to read message content (enable in developer portal)
bot = commands.Bot(command_prefix="!", intents=intents)
class ModerationCog(commands.Cog, name="Moderation"):
"""Slash commands for server moderation."""
def __init__(self, bot: commands.Bot) -> None:
self.bot = bot
@app_commands.command(name="kick", description="Kick a member from the server")
@app_commands.describe(member="The member to kick", reason="Reason for the kick")
@app_commands.default_permissions(kick_members=True)
async def kick_member(
self,
interaction: discord.Interaction,
member: discord.Member,
reason: Optional[str] = None,
) -> None:
"""Kick a member with audit log reason."""
if not interaction.guild:
await interaction.response.send_message("This command can only be used in a server.", ephemeral=True)
return
if member.top_role >= interaction.user.top_role and interaction.user != interaction.guild.owner:
await interaction.response.send_message("You cannot kick someone with an equal or higher role.", ephemeral=True)
return
reason_text = reason or "No reason provided"
try:
await member.kick(reason=reason_text)
embed = discord.Embed(
title="Member Kicked",
description=f"{member.mention} ({member.id}) has been kicked.",
color=discord.Color.orange(),
)
embed.add_field(name="Reason", value=reason_text, inline=False)
embed.add_field(name="Moderator", value=interaction.user.mention, inline=True)
embed.set_footer(text=f"User ID: {member.id}")
await interaction.response.send_message(embed=embed)
logger.info("Member kicked", extra={"guild": interaction.guild.id, "member": member.id, "reason": reason_text})
except discord.Forbidden:
await interaction.response.send_message("I do not have permission to kick that member.", ephemeral=True)
except discord.HTTPException as exc:
await interaction.response.send_message(f"Failed to kick member: {exc.status}", ephemeral=True)
@bot.event
async def on_ready() -> None:
logger.info(f"Logged in as {bot.user} (ID: {bot.user.id})")
logger.info(f"Connected to {len(bot.guilds)} guilds")
async def setup_hook() -> None:
"""Register cogs and sync commands."""
await bot.add_cog(ModerationCog(bot))
await bot.tree.sync()
logger.info("Slash commands synced")
bot.setup_hook = setup_hook
bot.run(os.environ["DISCORD_TOKEN"])
import os
import requests
from discord import Webhook, SyncWebhook, Embed, Colour
# ❌ BAD — raw HTTP, no embed, no error handling, synchronous blocking
requests.post(
"https://discord.com/api/webhooks/xxx/yyy",
json={"content": "Build failed!"},
)
# ✅ GOOD — typed webhook, embed with fields, async (or sync) usage, error handling
import logging
logger = logging.getLogger(__name__)
def send_build_webhook(
webhook_url: str,
service: str,
version: str,
status: str,
log_url: str | None = None,
) -> bool:
"""Send a build notification via Discord webhook with an embed."""
color = Colour.green() if status == "success" else Colour.red()
embed = Embed(
title=f"Build: {service} v{version}",
description=f"Build **{status.upper()}** for `{service}`",
color=color,
timestamp=__import__("datetime").datetime.now(),
)
embed.add_field(name="Service", value=service, inline=True)
embed.add_field(name="Version", value=version, inline=True)
embed.add_field(name="Status", value=status, inline=True)
if log_url:
embed.add_field(name="Build Log", value=f"[View Log]({log_url})", inline=False)
try:
webhook = SyncWebhook.from_url(webhook_url)
webhook.send(embed=embed, username="CI Bot", avatar_url=None)
return True
except Exception as exc:
logger.error("Webhook send failed", extra={"error": str(exc)})
return False
# Async version for use inside bot cogs
async def send_webhook_async(webhook_url: str, content: str) -> None:
"""Send an async webhook message."""
from discord import AsyncWebhook
async with AsyncWebhook.from_url(webhook_url, session=None) as webhook:
await webhook.send(content)
import discord
from discord import app_commands
from discord.ext import commands
# ❌ BAD — uses old prefix-command era pattern, no modal, no validation
@bot.command()
async def feedback(ctx, *, text):
await ctx.send(f"Thanks for your feedback: {text[:50]}...")
# ✅ GOOD — modal with validated inputs, styled embed response
class FeedbackModal(discord.ui.Modal, title="Submit Feedback"):
"""A modal dialog for collecting user feedback."""
def __init__(self) -> None:
super().__init__()
self.add_item(discord.ui.TextInput(
label="Subject",
placeholder="Brief summary of your feedback",
max_length=100,
required=True,
))
self.add_item(discord.ui.TextInput(
label="Details",
style=discord.TextStyle.paragraph,
placeholder="Describe your feedback in detail...",
max_length=1000,
required=True,
))
self.add_item(discord.ui.TextInput(
label="Rating (1-5)",
placeholder="5",
max_length=1,
required=False,
))
async def on_submit(self, interaction: discord.Interaction) -> None:
subject = self.children[0].value # type: ignore
details = self.children[1].value # type: ignore
embed = Embed(
title="Feedback Received",
description=f"**{subject}**",
color=Colour.blue(),
)
embed.add_field(name="Details", value=details, inline=False)
embed.set_footer(text=f"Submitted by {interaction.user}")
await interaction.response.send_message(embed=embed, ephemeral=True)
async def on_error(self, interaction: discord.Interaction, error: Exception) -> None:
await interaction.response.send_message("An error occurred processing your feedback.", ephemeral=True)
logger.error("Feedback modal error", extra={"error": str(error)})
@app_commands.command(name="feedback", description="Submit feedback via a modal form")
async def feedback_command(interaction: discord.Interaction) -> None:
"""Open the feedback modal."""
await interaction.response.send_modal(FeedbackModal())
message_content, server_members, presences — before they work in codeIntents to commands.Bot — match them exactly to what you enabled in the portal or Discord will silently ignore eventsDISCORD_TOKEN as an environment variable — never hardcode it or commit it to version controlCog classes grouped by domain (moderation, utility, fun, admin) — one file per cog for maintainabilitybot.tree.sync() after adding or modifying commands — changes are not automaticdiscord.Forbidden (permissions), discord.HTTPException (API), and discord.NotFound (missing resource) in every commanddiscord.Embed for rich message formatting with title, description, color, and fieldsephemeral=True if processing takes longerEmbed objects would provide better structure and visual hierarchyWhen implementing Discord API code, the output must follow this structure:
commands.Bot with explicit Intents matching the Developer Portal configurationCog classes grouping related slash commands; register with bot.add_cog()@app_commands.command() with description, @app_commands.describe(), and typed parametersdiscord.Forbidden, discord.HTTPException, discord.NotFound; sends ephemeral error messagesdiscord.Embed with title, description, color, and fields; never plain text onlybot.tree.sync() called in setup_hook or after command registration| Skill | Purpose |
|---|---|
coding-slack-api | Workplace messaging and bot development — similar command/event model for Slack |
coding-twilio-api | SMS and phone verification — complement to Discord for multi-channel notification routing |