Skip to content

필사 모드: Complete Guide to Discord Bot Development: Slash Commands, Buttons, and Modals with Pycord

English
0%
정확도 0%
💡 왼쪽 원문을 읽으면서 오른쪽에 따라 써보세요. Tab 키로 힌트를 받을 수 있습니다.
원문 렌더가 준비되기 전까지 텍스트 가이드로 표시합니다.

Discord Bot Development Setup

Discord Developer Portal Configuration

1. Create a New Application at the [Discord Developer Portal](https://discord.com/developers/applications)

2. Copy the Token from the Bot tab (never expose this publicly!)

3. Generate a bot invite URL from the OAuth2 tab:

- Scopes: `bot`, `applications.commands`

- Permissions: Select the required permissions

Project Setup

Create a virtual environment

python -m venv venv

source venv/bin/activate

Install Pycord

pip install py-cord python-dotenv aiohttp

Project structure

my-discord-bot/

├── bot.py # Main bot file

├── cogs/

│ ├── __init__.py

│ ├── general.py # General commands

│ ├── moderation.py # Moderation commands

│ └── fun.py # Fun commands

├── utils/

│ └── helpers.py

├── .env

└── requirements.txt

.env File

DISCORD_TOKEN=your_bot_token_here

GUILD_IDS=123456789012345678

Basic Bot Structure

bot.py

from discord.ext import commands

from dotenv import load_dotenv

load_dotenv()

Intents configuration

intents = discord.Intents.default()

intents.message_content = True

intents.members = True

bot = discord.Bot(intents=intents)

@bot.event

async def on_ready():

print(f"✅ {bot.user} logged in!")

print(f"📊 Connected to {len(bot.guilds)} servers")

await bot.change_presence(

activity=discord.Activity(

type=discord.ActivityType.watching,

name="Watching the server 👀"

)

)

Load Cogs

for filename in os.listdir("./cogs"):

if filename.endswith(".py") and not filename.startswith("_"):

bot.load_extension(f"cogs.{filename[:-3]}")

bot.run(os.getenv("DISCORD_TOKEN"))

Slash Commands

cogs/general.py

from discord.ext import commands

from discord import option

from datetime import datetime

class General(commands.Cog):

def __init__(self, bot):

self.bot = bot

@discord.slash_command(name="ping", description="Check the bot's response time")

async def ping(self, ctx: discord.ApplicationContext):

latency = round(self.bot.latency * 1000)

embed = discord.Embed(

title="🏓 Pong!",

description=f"Latency: **{latency}ms**",

color=discord.Color.green() if latency < 100 else discord.Color.red()

)

await ctx.respond(embed=embed)

@discord.slash_command(name="userinfo", description="Display user information")

@option("user", description="User to view info for", type=discord.Member, required=False)

async def userinfo(self, ctx: discord.ApplicationContext, user: discord.Member = None):

user = user or ctx.author

embed = discord.Embed(

title=f"👤 {user.display_name}",

color=user.color

)

embed.set_thumbnail(url=user.display_avatar.url)

embed.add_field(name="ID", value=user.id, inline=True)

embed.add_field(name="Joined", value=user.joined_at.strftime("%Y-%m-%d"), inline=True)

embed.add_field(name="Account Created", value=user.created_at.strftime("%Y-%m-%d"), inline=True)

embed.add_field(

name="Roles",

value=", ".join([r.mention for r in user.roles[1:]]) or "None",

inline=False

)

await ctx.respond(embed=embed)

@discord.slash_command(name="weather", description="Get weather information")

@option("city", description="City name", type=str, required=True)

async def weather(self, ctx: discord.ApplicationContext, city: str):

await ctx.defer() # Show response delay

async with aiohttp.ClientSession() as session:

url = f"https://wttr.in/{city}?format=j1"

async with session.get(url) as resp:

if resp.status != 200:

await ctx.followup.send("❌ City not found.")

return

data = await resp.json()

current = data["current_condition"][0]

embed = discord.Embed(

title=f"🌤 Weather in {city}",

color=discord.Color.blue()

)

embed.add_field(name="🌡 Temperature", value=f"{current['temp_C']}°C", inline=True)

embed.add_field(name="💧 Humidity", value=f"{current['humidity']}%", inline=True)

embed.add_field(name="💨 Wind", value=f"{current['windspeedKmph']} km/h", inline=True)

embed.add_field(name="Condition", value=current["weatherDesc"][0]["value"], inline=False)

await ctx.followup.send(embed=embed)

def setup(bot):

bot.add_cog(General(bot))

Button Interactions

cogs/fun.py

from discord.ext import commands

class RockPaperScissorsView(discord.ui.View):

def __init__(self):

super().__init__(timeout=30)

@discord.ui.button(label="✊ Rock", style=discord.ButtonStyle.primary, custom_id="rock")

async def rock(self, button: discord.ui.Button, interaction: discord.Interaction):

await self.play(interaction, "rock")

@discord.ui.button(label="✋ Paper", style=discord.ButtonStyle.success, custom_id="paper")

async def paper(self, button: discord.ui.Button, interaction: discord.Interaction):

await self.play(interaction, "paper")

@discord.ui.button(label="✌️ Scissors", style=discord.ButtonStyle.danger, custom_id="scissors")

async def scissors(self, button: discord.ui.Button, interaction: discord.Interaction):

await self.play(interaction, "scissors")

async def play(self, interaction: discord.Interaction, user_choice: str):

choices = {"rock": "✊", "paper": "✋", "scissors": "✌️"}

bot_choice = random.choice(list(choices.keys()))

if user_choice == bot_choice:

result = "🤝 Draw!"

color = discord.Color.yellow()

elif (user_choice == "rock" and bot_choice == "scissors") or \

(user_choice == "paper" and bot_choice == "rock") or \

(user_choice == "scissors" and bot_choice == "paper"):

result = "🎉 You Win!"

color = discord.Color.green()

else:

result = "😢 You Lose!"

color = discord.Color.red()

embed = discord.Embed(title=result, color=color)

embed.add_field(name="You", value=choices[user_choice], inline=True)

embed.add_field(name="Bot", value=choices[bot_choice], inline=True)

Disable buttons

for child in self.children:

child.disabled = True

await interaction.response.edit_message(embed=embed, view=self)

class Fun(commands.Cog):

def __init__(self, bot):

self.bot = bot

@discord.slash_command(name="rps", description="Rock Paper Scissors game!")

async def rps(self, ctx: discord.ApplicationContext):

embed = discord.Embed(

title="✊✋✌️ Rock Paper Scissors!",

description="Click a button to make your choice!",

color=discord.Color.blue()

)

await ctx.respond(embed=embed, view=RockPaperScissorsView())

def setup(bot):

bot.add_cog(Fun(bot))

Modal Forms

class FeedbackModal(discord.ui.Modal):

def __init__(self):

super().__init__(title="📋 Submit Feedback")

self.add_item(discord.ui.InputText(

label="Title",

placeholder="Enter the feedback title",

style=discord.InputTextStyle.short,

required=True,

max_length=100

))

self.add_item(discord.ui.InputText(

label="Content",

placeholder="Enter detailed content",

style=discord.InputTextStyle.long,

required=True,

max_length=2000

))

self.add_item(discord.ui.InputText(

label="Rating (1-5)",

placeholder="1",

style=discord.InputTextStyle.short,

required=False,

max_length=1

))

async def callback(self, interaction: discord.Interaction):

title = self.children[0].value

content = self.children[1].value

rating = self.children[2].value or "Not provided"

embed = discord.Embed(

title="📋 New Feedback",

color=discord.Color.blue()

)

embed.add_field(name="Title", value=title, inline=False)

embed.add_field(name="Content", value=content, inline=False)

embed.add_field(name="Rating", value=f"{'⭐' * int(rating)}" if rating.isdigit() else rating)

embed.set_footer(text=f"Author: {interaction.user.display_name}")

Send to feedback channel

feedback_channel = interaction.guild.get_channel(FEEDBACK_CHANNEL_ID)

if feedback_channel:

await feedback_channel.send(embed=embed)

await interaction.response.send_message(

"✅ Feedback submitted! Thank you.", ephemeral=True

)

Open modal via slash command

@discord.slash_command(name="feedback", description="Submit feedback")

async def feedback(ctx: discord.ApplicationContext):

await ctx.send_modal(FeedbackModal())

Select Menus

class RoleSelectView(discord.ui.View):

@discord.ui.select(

placeholder="Select roles (up to 3)",

min_values=1,

max_values=3,

options=[

discord.SelectOption(label="Developer", emoji="💻", value="developer"),

discord.SelectOption(label="Designer", emoji="🎨", value="designer"),

discord.SelectOption(label="Planner", emoji="📊", value="planner"),

discord.SelectOption(label="Marketer", emoji="📢", value="marketer"),

discord.SelectOption(label="Data Analyst", emoji="📈", value="analyst"),

]

)

async def select_callback(self, select: discord.ui.Select, interaction: discord.Interaction):

selected = ", ".join(select.values)

await interaction.response.send_message(

f"✅ Selected roles: {selected}", ephemeral=True

)

Error Handling

Add global error handler to bot.py

@bot.event

async def on_application_command_error(ctx: discord.ApplicationContext, error):

if isinstance(error, commands.MissingPermissions):

await ctx.respond("❌ Insufficient permissions.", ephemeral=True)

elif isinstance(error, commands.CommandOnCooldown):

await ctx.respond(

f"⏳ On cooldown. Please try again in {error.retry_after:.1f} seconds.",

ephemeral=True

)

elif isinstance(error, commands.MemberNotFound):

await ctx.respond("❌ User not found.", ephemeral=True)

else:

Logging

traceback.print_exception(type(error), error, error.__traceback__)

await ctx.respond("❌ An error occurred.", ephemeral=True)

Moderation Commands

cogs/moderation.py

class Moderation(commands.Cog):

def __init__(self, bot):

self.bot = bot

@discord.slash_command(name="clear", description="Delete messages")

@commands.has_permissions(manage_messages=True)

@option("amount", description="Number of messages to delete", type=int, min_value=1, max_value=100)

async def clear(self, ctx: discord.ApplicationContext, amount: int):

deleted = await ctx.channel.purge(limit=amount)

await ctx.respond(f"🗑️ {len(deleted)} messages deleted", ephemeral=True)

@discord.slash_command(name="slowmode", description="Set slow mode")

@commands.has_permissions(manage_channels=True)

@option("seconds", description="Seconds (0=disable)", type=int, min_value=0, max_value=21600)

async def slowmode(self, ctx: discord.ApplicationContext, seconds: int):

await ctx.channel.edit(slowmode_delay=seconds)

if seconds == 0:

await ctx.respond("✅ Slow mode has been disabled.")

else:

await ctx.respond(f"✅ Slow mode set to {seconds} seconds")

def setup(bot):

bot.add_cog(Moderation(bot))

Deployment

systemd Service

/etc/systemd/system/discord-bot.service

[Unit]

Description=Discord Bot

After=network.target

[Service]

Type=simple

User=bot

WorkingDirectory=/opt/discord-bot

ExecStart=/opt/discord-bot/venv/bin/python bot.py

Restart=always

RestartSec=10

EnvironmentFile=/opt/discord-bot/.env

[Install]

WantedBy=multi-user.target

sudo systemctl enable discord-bot

sudo systemctl start discord-bot

sudo journalctl -u discord-bot -f

**Q1. What are Discord Bot Intents?**

Intents are settings that specify the types of events the bot will receive. Privileged Intents (message_content, members) require separate activation in the Developer Portal.

**Q2. When do you use ctx.defer() in slash commands?**

When the response takes more than 3 seconds. After defer(), you send the actual response using ctx.followup.send().

**Q3. What does ephemeral=True mean?**

It makes the message visible only to the user who executed the command. Other users cannot see it.

**Q4. What are the advantages of Cogs?**

They allow you to separate commands into modules for better organization and enable dynamic loading/unloading. This is beneficial for code structure and maintainability.

**Q5. What does the View's timeout parameter control?**

The time in seconds until buttons/select menus are deactivated. Setting it to None means no timeout.

**Q6. What is the difference between a Modal and a regular message?**

A Modal displays an input form to the user for collecting structured data. Regular messages only exchange text.

Quiz

Q1: What is the main topic covered in "Complete Guide to Discord Bot Development: Slash

Commands, Buttons, and Modals with Pycord"?

A complete hands-on guide to developing a Discord Bot with Pycord. Covers slash commands, button

interactions, modal forms, embed messages, and Cog-based architecture at a production level.

Discord Developer Portal Configuration Create a New Application at the Discord Developer Portal

Copy the Token from the Bot tab (never expose this publicly!) Generate a bot invite URL from the

OAuth2 tab: Scopes: bot, applications.commands Permissions: Select the required permiss...

systemd Service Q1. What are Discord Bot Intents? Intents are settings that specify the types of

events the bot will receive. Privileged Intents (message_content, members) require separate

activation in the Developer Portal. Q2. When do you use ctx.defer() in slash commands?

현재 단락 (1/268)

1. Create a New Application at the [Discord Developer Portal](https://discord.com/developers/applica...

작성 글자: 0원문 글자: 11,342작성 단락: 0/268