Split View: Discord Bot 개발 완벽 가이드: Pycord로 슬래시 커맨드, 버튼, 모달까지
Discord Bot 개발 완벽 가이드: Pycord로 슬래시 커맨드, 버튼, 모달까지
Discord Bot 개발 준비
Discord 개발자 포털 설정
- Discord Developer Portal에서 New Application 생성
- Bot 탭에서 Token 복사 (절대 공개하지 마세요!)
- OAuth2 탭에서 봇 초대 URL 생성:
- Scopes:
bot,applications.commands - Permissions: 필요한 권한 선택
- Scopes:
프로젝트 설정
# 가상 환경 생성
python -m venv venv
source venv/bin/activate
# Pycord 설치
pip install py-cord python-dotenv aiohttp
# 프로젝트 구조
# my-discord-bot/
# ├── bot.py # 메인 봇 파일
# ├── cogs/
# │ ├── __init__.py
# │ ├── general.py # 일반 명령어
# │ ├── moderation.py # 관리 명령어
# │ └── fun.py # 재미 명령어
# ├── utils/
# │ └── helpers.py
# ├── .env
# └── requirements.txt
.env 파일
DISCORD_TOKEN=your_bot_token_here
GUILD_IDS=123456789012345678
기본 봇 구조
# bot.py
import discord
from discord.ext import commands
import os
from dotenv import load_dotenv
load_dotenv()
# Intents 설정
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} 로그인 완료!")
print(f"📊 {len(bot.guilds)}개 서버에 연결됨")
await bot.change_presence(
activity=discord.Activity(
type=discord.ActivityType.watching,
name="서버를 관찰 중 👀"
)
)
# Cog 로드
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"))
슬래시 커맨드
# cogs/general.py
import discord
from discord.ext import commands
from discord import option
import aiohttp
from datetime import datetime
class General(commands.Cog):
def __init__(self, bot):
self.bot = bot
@discord.slash_command(name="ping", description="봇의 응답 시간을 확인합니다")
async def ping(self, ctx: discord.ApplicationContext):
latency = round(self.bot.latency * 1000)
embed = discord.Embed(
title="🏓 Pong!",
description=f"지연 시간: **{latency}ms**",
color=discord.Color.green() if latency < 100 else discord.Color.red()
)
await ctx.respond(embed=embed)
@discord.slash_command(name="userinfo", description="사용자 정보를 표시합니다")
@option("user", description="정보를 볼 사용자", 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="가입일", value=user.joined_at.strftime("%Y-%m-%d"), inline=True)
embed.add_field(name="계정 생성일", value=user.created_at.strftime("%Y-%m-%d"), inline=True)
embed.add_field(
name="역할",
value=", ".join([r.mention for r in user.roles[1:]]) or "없음",
inline=False
)
await ctx.respond(embed=embed)
@discord.slash_command(name="weather", description="날씨 정보를 가져옵니다")
@option("city", description="도시 이름", type=str, required=True)
async def weather(self, ctx: discord.ApplicationContext, city: str):
await ctx.defer() # 응답 지연 표시
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("❌ 도시를 찾을 수 없습니다.")
return
data = await resp.json()
current = data["current_condition"][0]
embed = discord.Embed(
title=f"🌤 {city} 날씨",
color=discord.Color.blue()
)
embed.add_field(name="🌡 기온", value=f"{current['temp_C']}°C", inline=True)
embed.add_field(name="💧 습도", value=f"{current['humidity']}%", inline=True)
embed.add_field(name="💨 바람", value=f"{current['windspeedKmph']} km/h", inline=True)
embed.add_field(name="상태", value=current["weatherDesc"][0]["value"], inline=False)
await ctx.followup.send(embed=embed)
def setup(bot):
bot.add_cog(General(bot))
버튼 인터랙션
# cogs/fun.py
import discord
from discord.ext import commands
import random
class RockPaperScissorsView(discord.ui.View):
def __init__(self):
super().__init__(timeout=30)
@discord.ui.button(label="✊ 바위", 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="✋ 보", 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="✌️ 가위", 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 = "🤝 무승부!"
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 = "🎉 승리!"
color = discord.Color.green()
else:
result = "😢 패배!"
color = discord.Color.red()
embed = discord.Embed(title=result, color=color)
embed.add_field(name="당신", value=choices[user_choice], inline=True)
embed.add_field(name="봇", value=choices[bot_choice], inline=True)
# 버튼 비활성화
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="가위바위보 게임!")
async def rps(self, ctx: discord.ApplicationContext):
embed = discord.Embed(
title="✊✋✌️ 가위바위보!",
description="버튼을 클릭해서 선택하세요!",
color=discord.Color.blue()
)
await ctx.respond(embed=embed, view=RockPaperScissorsView())
def setup(bot):
bot.add_cog(Fun(bot))
모달 (Modal) 폼
class FeedbackModal(discord.ui.Modal):
def __init__(self):
super().__init__(title="📋 피드백 제출")
self.add_item(discord.ui.InputText(
label="제목",
placeholder="피드백 제목을 입력하세요",
style=discord.InputTextStyle.short,
required=True,
max_length=100
))
self.add_item(discord.ui.InputText(
label="내용",
placeholder="상세 내용을 입력하세요",
style=discord.InputTextStyle.long,
required=True,
max_length=2000
))
self.add_item(discord.ui.InputText(
label="점수 (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 "미입력"
embed = discord.Embed(
title="📋 새 피드백",
color=discord.Color.blue()
)
embed.add_field(name="제목", value=title, inline=False)
embed.add_field(name="내용", value=content, inline=False)
embed.add_field(name="점수", value=f"{'⭐' * int(rating)}" if rating.isdigit() else rating)
embed.set_footer(text=f"작성자: {interaction.user.display_name}")
# 피드백 채널에 전송
feedback_channel = interaction.guild.get_channel(FEEDBACK_CHANNEL_ID)
if feedback_channel:
await feedback_channel.send(embed=embed)
await interaction.response.send_message(
"✅ 피드백이 제출되었습니다! 감사합니다.", ephemeral=True
)
# 슬래시 커맨드로 모달 열기
@discord.slash_command(name="feedback", description="피드백을 제출합니다")
async def feedback(ctx: discord.ApplicationContext):
await ctx.send_modal(FeedbackModal())
Select 메뉴
class RoleSelectView(discord.ui.View):
@discord.ui.select(
placeholder="역할을 선택하세요 (최대 3개)",
min_values=1,
max_values=3,
options=[
discord.SelectOption(label="개발자", emoji="💻", value="developer"),
discord.SelectOption(label="디자이너", emoji="🎨", value="designer"),
discord.SelectOption(label="기획자", emoji="📊", value="planner"),
discord.SelectOption(label="마케터", emoji="📢", value="marketer"),
discord.SelectOption(label="데이터 분석가", 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}", ephemeral=True
)
에러 처리
# bot.py에 글로벌 에러 핸들러 추가
@bot.event
async def on_application_command_error(ctx: discord.ApplicationContext, error):
if isinstance(error, commands.MissingPermissions):
await ctx.respond("❌ 권한이 부족합니다.", ephemeral=True)
elif isinstance(error, commands.CommandOnCooldown):
await ctx.respond(
f"⏳ 쿨다운 중입니다. {error.retry_after:.1f}초 후 다시 시도하세요.",
ephemeral=True
)
elif isinstance(error, commands.MemberNotFound):
await ctx.respond("❌ 사용자를 찾을 수 없습니다.", ephemeral=True)
else:
# 로깅
import traceback
traceback.print_exception(type(error), error, error.__traceback__)
await ctx.respond("❌ 오류가 발생했습니다.", ephemeral=True)
관리 명령어
# cogs/moderation.py
class Moderation(commands.Cog):
def __init__(self, bot):
self.bot = bot
@discord.slash_command(name="clear", description="메시지를 삭제합니다")
@commands.has_permissions(manage_messages=True)
@option("amount", description="삭제할 메시지 수", 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)}개 메시지 삭제됨", ephemeral=True)
@discord.slash_command(name="slowmode", description="슬로우모드를 설정합니다")
@commands.has_permissions(manage_channels=True)
@option("seconds", description="초 단위 (0=해제)", 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("✅ 슬로우모드가 해제되었습니다.")
else:
await ctx.respond(f"✅ 슬로우모드: {seconds}초로 설정됨")
def setup(bot):
bot.add_cog(Moderation(bot))
배포
systemd 서비스
# /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
📝 확인 퀴즈 (6문제)
Q1. Discord Bot의 Intents란?
봇이 수신할 이벤트 유형을 지정하는 설정입니다. Privileged Intents(message_content, members)는 Developer Portal에서 별도 활성화가 필요합니다.
Q2. 슬래시 커맨드에서 ctx.defer()는 언제 사용하나요?
응답이 3초 이상 걸릴 때 사용합니다. defer() 후 ctx.followup.send()로 실제 응답을 보냅니다.
Q3. ephemeral=True의 의미는?
해당 메시지가 명령어를 실행한 사용자에게만 보이게 합니다. 다른 사용자는 볼 수 없습니다.
Q4. Cog의 장점은?
명령어를 모듈별로 분리하여 관리하고, 동적으로 로드/언로드할 수 있습니다. 코드 구조화와 유지보수에 유리합니다.
Q5. View의 timeout 파라미터는 무엇을 제어하나요?
버튼/셀렉트 메뉴가 비활성화되기까지의 시간(초)입니다. None으로 설정하면 타임아웃 없음.
Q6. Modal과 일반 메시지의 차이점은?
Modal은 사용자에게 입력 폼을 표시하여 구조화된 데이터를 받을 수 있습니다. 일반 메시지는 텍스트만 주고받습니다.
Complete Guide to Discord Bot Development: Slash Commands, Buttons, and Modals with Pycord
- Discord Bot Development Setup
- Basic Bot Structure
- Slash Commands
- Button Interactions
- Modal Forms
- Select Menus
- Error Handling
- Moderation Commands
- Deployment
- Quiz
Discord Bot Development Setup
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 permissions
- Scopes:
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
import discord
from discord.ext import commands
import os
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
import discord
from discord.ext import commands
from discord import option
import aiohttp
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
import discord
from discord.ext import commands
import random
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
import traceback
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
📝 Review Quiz (6 Questions)
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.
Q2: What are the key steps for Discord Bot Development Setup?
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...
Q3: Explain the core concept of Deployment.
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?