Skip to content

필사 모드: Discord Bot開発完全ガイド:Pycordでスラッシュコマンド、ボタン、モーダルまで

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

Discord Bot開発の準備

Discord開発者ポータルの設定

1. [Discord Developer Portal](https://discord.com/developers/applications)でNew Applicationを作成

2. Botタブでトークンをコピー(絶対に公開しないでください!)

3. OAuth2タブでボット招待URLを生成:

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

- Permissions: 必要な権限を選択

プロジェクト設定

仮想環境の作成

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

from discord.ext import commands

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

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="ボットの応答時間を確認します")

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

from discord.ext import commands

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))

モーダルフォーム

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())

セレクトメニュー

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:

ロギング

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

**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はユーザーに入力フォームを表示して構造化されたデータを受け取ることができます。通常のメッセージはテキストのみのやり取りです。

クイズ

Q1: 「Discord

Bot開発完全ガイド:Pycordでスラッシュコマンド、ボタン、モーダルまで」の主なトピックは何ですか?

Pycordを使ったDiscord

Bot開発の全プロセスをハンズオンで学びます。スラッシュコマンド、ボタンインタラクション、モーダルフォーム、Embedメッセージ、Cog構造化までプロダクションレベルで実装します。

Discord開発者ポータルの設定 Discord Developer PortalでNew Applicationを作成

Botタブでトークンをコピー(絶対に公開しないでください!) OAuth2タブでボット招待URLを生成:

Scopes: bot, applications.commands Permissions: 必要な権限を選択 プロジェクト設定 .envファイル

systemdサービス Q1. Discord BotのIntentsとは?

ボットが受信するイベントの種類を指定する設定です。Privileged

Intents(message_content、members)はDeveloper Portalで別途有効化が必要です。 Q2.

スラッシュコマンドでctx.defer()はいつ使いますか?

応答に3秒以上かかる場合に使用します。defer()の後、ctx.followup.send()で実際の応答を送信します。

Q3. ephemeral=Trueの意味は?

현재 단락 (1/271)

1. [Discord Developer Portal](https://discord.com/developers/applications)でNew Applicationを作成

작성 글자: 0원문 글자: 10,276작성 단락: 0/271