カテゴリー: Discordボット開発

Discordボット開発#13ロール・チャンネルの自動化でサーバーを管理しよう! (Pythonサンプル付き)

この記事は韓国語の原文から翻訳したものです。

目次9項目

こんにちは!Mojangcamです 😎

前回の#12では、コマンドのクールダウンと権限でボットを安全に守る方法を学びました。今回の#13ではさらに一歩進んで、サーバー管理者の繰り返し作業をまるごとボットに任せてみます。今日作る自動化はちょうど3つです。

  • 新しいメンバーが参加したら自動でロールを付与 (歓迎ロール)
  • ボタンひとつで好きなロールを自分で取得 (ロールボタンパネル)
  • ボイスチャンネルに入ったら個人部屋を自動作成 (Join to Create)

サーバーが大きくなるほど、「新しく来た人にロールを付ける」「ゲームする人にロールを配る」「会議用の部屋を作る」といった作業が延々と繰り返されます。これを人の手でやっていたら、徹夜しても終わりません。ボットに任せれば、24時間自動で処理してくれます。それでは始めましょう! 🙌

📌 準備するもの

まずは開発環境から準備しましょう。

  • Python 3.9以上 (discord.py 2.7には3.9+が必要)
  • discord.py 2.x (2026年7月時点の最新安定版は 2.7.1)

ライブラリのインストールは、ターミナルで1行打つだけで終わりです。

ターミナル
pip install -U discord.py

それから今回は、特権インテント(Privileged Intents) の設定が必ず必要です。新しいメンバーの参加を検知するには、Server Members Intentをオンにしなければならないからです。

  1. Discord Developer Portalにアクセス → 自分のボットを選択
  2. 左のBotタブ → Privileged Gateway Intents
  3. Server Members Intentをオン ✅ (メッセージコマンドを使うならMessage Content Intentもオン)

最後に、サーバーでボットのロールの権限も確認します。自動化が動くには、ボットに次の権限が必要です。

  • ロールの管理(Manage Roles) — ロールを付けたり外したりする
  • チャンネルの管理(Manage Channels) — ボイスチャンネルを作ったり消したりする
  • メンバーを移動(Move Members) — 作った部屋へメンバーを移す

💡 いちばん大事なポイント! ボットが付与するロールは、必ずボット自身のロールより下になければいけません。サーバー設定 → ロール一覧で、ボットのロールを上のほうへドラッグしてください。これを見落とすと、後で出てくるForbiddenエラーになります。

📌 ステップ1 - 新しいメンバーが来たら自動でロールを付与

まずはいちばん基本となる、自動の歓迎ロールから作ります。誰かがサーバーに入った瞬間にon_member_joinイベントが実行されます。ここでロールを探して付ければ完成です。

Python
import discord
from discord.ext import commands

intents = discord.Intents.default()
intents.members = True          # メンバーの参加を検知 (特権インテント)
intents.message_content = True  # プレフィックスコマンド用

bot = commands.Bot(command_prefix="!", intents=intents)

AUTO_ROLE_NAME = "若葉"   # 参加時に自動で付与するロール名

@bot.event
async def on_member_join(member: discord.Member):
    role = discord.utils.get(member.guild.roles, name=AUTO_ROLE_NAME)
    if role is not None:
        await member.add_roles(role)
        print(f"👋 {member.name} さんに「{role.name}」ロールを付与しました!")

ひとつずつ見ていきましょう 💡

  • intents.members = True : メンバーの参加・退出を検知するスイッチです。これがオフだと、on_member_joinがそもそも実行されません。
  • on_member_join(member) : 新しいメンバーが入るたびに自動で呼ばれるイベントです。memberに入ってきた人の情報が入っています。
  • discord.utils.get(member.guild.roles, name=...) : サーバーのロール一覧から、名前が一致するロールを探してくれます。なければNoneを返すので、必ず確認しましょう。
  • await member.add_roles(role) : 見つけたロールをそのメンバーに付けます。核心となる1行ですね!

あとは「若葉」というロールをサーバーに作っておけば、新しいメンバーは入ってくるなり若葉ロールを付けて登場します。🌱

📌 ステップ2 - ボタンひとつでロールを自分で取得

以前の#5 ボタン(Button)編でボタンを学びましたよね?今回はそのボタンで、メンバーが自分でロールを選べるパネルを作ります。管理者がいちいちロールを付けてあげる必要がなくなります。

ここで必ず知っておきたい概念が永続View(Persistent View) です。ボットを再起動すると普通はボタンが反応しなくなりますが、次の2つの条件を守れば、再起動後もボタンが動き続けます。

  • Viewのtimeout=None
  • すべてのボタンにcustom_idを指定
Python
# ボタンロールの一覧 (custom_id, ロール名, ボタンのラベル, 絵文字)
BUTTON_ROLES = [
    ("role_notice", "お知らせ通知", "お知らせ通知", "🔔"),
    ("role_game",   "ゲーム仲間", "ゲーム仲間", "🎮"),
    ("role_study",  "勉強仲間",  "勉強仲間",   "📚"),
]

class RoleButton(discord.ui.Button):
    def __init__(self, custom_id, role_name, label, emoji):
        super().__init__(
            label=label, emoji=emoji,
            style=discord.ButtonStyle.secondary,
            custom_id=custom_id,        # 永続Viewにはcustom_idが必須!
        )
        self.role_name = role_name

    async def callback(self, interaction: discord.Interaction):
        role = discord.utils.get(interaction.guild.roles, name=self.role_name)
        if role is None:
            await interaction.response.send_message(
                f"❌ 「{self.role_name}」ロールがありません。先にサーバーで作成してください!",
                ephemeral=True)
            return
        if role in interaction.user.roles:
            await interaction.user.remove_roles(role)
            msg = f"「{role.name}」ロールを外しました。👋"
        else:
            await interaction.user.add_roles(role)
            msg = f"「{role.name}」ロールを付与しました! 🎉"
        await interaction.response.send_message(msg, ephemeral=True)

class RoleView(discord.ui.View):
    def __init__(self):
        super().__init__(timeout=None)   # 永続Viewはtimeout=None!
        for custom_id, role_name, label, emoji in BUTTON_ROLES:
            self.add_item(RoleButton(custom_id, role_name, label, emoji))

ひとつずつ見ていきましょう 💡

  • custom_id : ボタンごとに付ける固有の名札です。再起動後にボットが「あ、このボタンはあのボタンだ!」と見分けられるようにしてくれます。永続Viewの必須条件です。
  • callback(self, interaction) : ボタンが押されたときに実行される関数です。interaction.userがボタンを押した人です。
  • role in interaction.user.roles : すでにそのロールがあれば外し(remove_roles)、なければ付けます(add_roles)。ボタンひとつでトグルになるわけです。
  • ephemeral=True : 「自分にだけ見えるメッセージ」です。ほかの人の画面を散らかさずに、こっそり知らせてくれます。

次に、このパネルをチャットに表示するコマンドと、再起動後もボタンが生き続けるようにするコードを追加します。

Python
class MyBot(commands.Bot):
    def __init__(self):
        super().__init__(command_prefix="!", intents=intents)

    async def setup_hook(self):
        self.add_view(RoleView())   # 再起動後にボタンを復活させる

bot = MyBot()

@bot.command(name="역할판")
@commands.has_permissions(administrator=True)
async def role_panel(ctx: commands.Context):
    embed = discord.Embed(
        title="🎭 ロールを受け取る",
        description="ボタンを押すとロールを付与します!もう一度押すとロールが外れます。",
        color=0x5865F2,
    )
    await ctx.send(embed=embed, view=RoleView())
  • setup_hook + add_view : ボットの起動時に永続Viewを再登録します。この2行のおかげで、ボットを再起動しても、以前に表示しておいたボタンがそのまま動きます。
  • @commands.has_permissions(administrator=True) : 管理者だけが!역할판コマンドを使えるように制限します。

📌 ステップ3 - 入ったら個人ボイスチャンネルを自動作成

最後は、最近のサーバーならどこでも使っているJoin to Createです。「➕ チャンネル作成」のようなボイスチャンネルをひとつ作っておき、誰かがそこに入ると、その人専用の部屋をその場で作って移動させる機能です。部屋が空になると自動で消えるので、サーバーもすっきり保てます。

ボイスチャンネルの移動はon_voice_state_updateイベントで検知します。

Python
CREATOR_CHANNEL_ID = 123456789012345678   # 「➕ チャンネル作成」ボイスチャンネルのID
temp_channels = set()                     # 自分たちが作った一時チャンネルのIDを保管

@bot.event
async def on_voice_state_update(member, before, after):
    # (作成) 「作成」チャンネルに入ったら個人チャンネルを作って移動
    if after.channel is not None and after.channel.id == CREATOR_CHANNEL_ID:
        new_channel = await member.guild.create_voice_channel(
            name=f"🔊 {member.display_name}の部屋",
            category=after.channel.category,
        )
        temp_channels.add(new_channel.id)
        await member.move_to(new_channel)

    # (削除) 自分たちが作った一時チャンネルが空になったら削除
    if before.channel is not None and before.channel.id in temp_channels:
        if len(before.channel.members) == 0:
            await before.channel.delete()
            temp_channels.discard(before.channel.id)

ひとつずつ見ていきましょう 💡

  • on_voice_state_update(member, before, after) : 誰かがボイスチャンネルを移動するたびに実行されます。beforeは抜けたチャンネル、afterは入ったチャンネルです。
  • after.channel.id == CREATOR_CHANNEL_ID : たった今入ったのが「作成」チャンネルかどうかを確認します。そうなら、新しい部屋を作る番です!
  • create_voice_channel(name, category) : 個人ボイスチャンネルを作ります。categoryを元のチャンネルと同じにすると、同じカテゴリーの下にきれいに作られます。
  • member.move_to(new_channel) : 作ったばかりの部屋へメンバーをスッと移動させます。
  • len(before.channel.members) == 0 : 抜けた部屋に誰も残っていなければ、delete()で消します。temp_channelsで「自分たちが作った部屋」だけを消すように安全装置を付けています。

💡 CREATOR_CHANNEL_IDは、Discordで開発者モードをオンにしてから(設定 → 詳細設定 → 開発者モード)、作成用のボイスチャンネルを右クリック → 「IDをコピー」で取得できます。

📌 全体コード (コピペOK)

下のコードをコピーして、TOKEN、AUTO_ROLE_NAME、CREATOR_CHANNEL_ID、BUTTON_ROLESだけを自分のサーバーに合わせて変えれば、すぐに動きます。

Python
# discord.py 2.x (2.7.1 基準) / Python 3.9+
import discord
from discord.ext import commands

# ── 設定 ──────────────────────────────
TOKEN = "ここにボットのトークン"
AUTO_ROLE_NAME = "若葉"                       # 参加時に自動で付与するロール名
CREATOR_CHANNEL_ID = 123456789012345678       # 「➕ チャンネル作成」ボイスチャンネルのID

# ボタンロールの一覧 (custom_id, ロール名, ボタンのラベル, 絵文字)
BUTTON_ROLES = [
    ("role_notice", "お知らせ通知", "お知らせ通知", "🔔"),
    ("role_game",   "ゲーム仲間", "ゲーム仲間", "🎮"),
    ("role_study",  "勉強仲間",  "勉強仲間",   "📚"),
]

intents = discord.Intents.default()
intents.members = True          # メンバーの参加を検知 (特権インテント)
intents.message_content = True  # プレフィックスコマンド用
# voice_states は default() にすでに含まれています。

# ── ボタンロールのView ────────────────────
class RoleButton(discord.ui.Button):
    def __init__(self, custom_id, role_name, label, emoji):
        super().__init__(
            label=label, emoji=emoji,
            style=discord.ButtonStyle.secondary,
            custom_id=custom_id,        # 永続Viewにはcustom_idが必須!
        )
        self.role_name = role_name

    async def callback(self, interaction: discord.Interaction):
        role = discord.utils.get(interaction.guild.roles, name=self.role_name)
        if role is None:
            await interaction.response.send_message(
                f"❌ 「{self.role_name}」ロールがありません。先にサーバーで作成してください!",
                ephemeral=True)
            return
        if role in interaction.user.roles:
            await interaction.user.remove_roles(role)
            msg = f"「{role.name}」ロールを外しました。👋"
        else:
            await interaction.user.add_roles(role)
            msg = f"「{role.name}」ロールを付与しました! 🎉"
        await interaction.response.send_message(msg, ephemeral=True)

class RoleView(discord.ui.View):
    def __init__(self):
        super().__init__(timeout=None)   # 永続Viewはtimeout=None!
        for custom_id, role_name, label, emoji in BUTTON_ROLES:
            self.add_item(RoleButton(custom_id, role_name, label, emoji))

# ── ボット本体 ───────────────────────────
class MyBot(commands.Bot):
    def __init__(self):
        super().__init__(command_prefix="!", intents=intents)

    async def setup_hook(self):
        # 再起動後もボタンが動くように永続Viewを再登録
        self.add_view(RoleView())

bot = MyBot()
temp_channels = set()   # 自分たちが作った一時ボイスチャンネルのIDを保管

@bot.event
async def on_ready():
    print(f"✅ {bot.user} でログインしました!")

# 1) 参加したら自動でロールを付与
@bot.event
async def on_member_join(member: discord.Member):
    role = discord.utils.get(member.guild.roles, name=AUTO_ROLE_NAME)
    if role is not None:
        await member.add_roles(role)
        print(f"👋 {member.name} → 「{role.name}」ロールの付与が完了")

# 2) ロールボタンのパネルを表示 (管理者のみ)
@bot.command(name="역할판")
@commands.has_permissions(administrator=True)
async def role_panel(ctx: commands.Context):
    embed = discord.Embed(
        title="🎭 ロールを受け取る",
        description="ボタンを押すとロールを付与します!もう一度押すとロールが外れます。",
        color=0x5865F2,
    )
    await ctx.send(embed=embed, view=RoleView())

# 3) 指定チャンネルに入ったら個人ボイスチャンネルを自動作成 + 空になったら削除
@bot.event
async def on_voice_state_update(member, before, after):
    # (作成) 「作成」チャンネルに入ったら個人チャンネルを作って移動
    if after.channel is not None and after.channel.id == CREATOR_CHANNEL_ID:
        new_channel = await member.guild.create_voice_channel(
            name=f"🔊 {member.display_name}の部屋",
            category=after.channel.category,
        )
        temp_channels.add(new_channel.id)
        await member.move_to(new_channel)

    # (削除) 自分たちが作った一時チャンネルが空になったら削除
    if before.channel is not None and before.channel.id in temp_channels:
        if len(before.channel.members) == 0:
            await before.channel.delete()
            temp_channels.discard(before.channel.id)

bot.run(TOKEN)

📌 実行結果

ボットを起動して、サーバーでひとつずつ確認してみましょう。

  1. 新しいアカウントで参加 → 自動で「若葉」ロールがピタッと付きます。🌱
  2. 管理者が!역할판と入力 → ボタンが3つ付いた埋め込みが表示されます。ボタンを押すと「ロールを付与しました! 🎉」、もう一度押すと「ロールを外しました 👋」と、自分にだけ見えるメッセージで返事をしてくれます。
  3. 「➕ チャンネル作成」ボイスチャンネルに入る → すぐに「🔊 (自分の名前)の部屋」ができて、そこへ移動します。部屋から出ると、部屋はスーッと消えます。

📌 よくあるエラー

1. discord.errors.Forbidden: 403 Missing Permissions 原因: ボットに権限がないか、付与しようとしているロールがボットのロールより上にあります。 解決: サーバー設定 → ロールでボットのロールを上へ移動し、ボットにロールの管理 / チャンネルの管理 / メンバーを移動の権限を付けてください。Discordは「自分より上のロール」には絶対に手を出せないようにしています。

2. on_member_joinがまったく実行されない 原因: Server Members Intentがオフになっています。 解決: Developer Portal → Bot → Privileged Gateway IntentsでServer Members Intentをオンにし、コードにもintents.members = Trueを入れます。両方オンにしないと動きません。

3. ボットを再起動したらボタンが「インタラクションに失敗しました」 原因: 永続Viewの条件を守っていません。 解決: Viewはtimeout=None、すべてのボタンはcustom_idを持ち、setup_hookでadd_viewを使って再登録しているか確認してください。3つでワンセットです。

4. 一時ボイスチャンネルが消えない 原因: ボットにチャンネルの管理権限がないか、temp_channelsにIDが入っていません。 解決: チャンネルの管理権限を確認し、チャンネルを作るときにtemp_channels.add(...)でIDを保存しているか点検してください。

📌 まとめ

今回は、サーバー管理をまるごとボットに任せる3つの自動化を作ってみました。参加時の自動ロール、ボタンでのロール自己取得、ボイスチャンネルの自動作成まで — この3つがあるだけで、管理者の繰り返し作業がぐっと減ります。特に永続View(timeout=None + custom_id + add_view)は、これからどんなボタンやメニューを作るときにもずっと使うので、ぜひ身につけておいてください! 💪

次の#14では、JSONとSQLiteでボットのデータを保存する方法を扱います。ロール・設定・記録を、ボットを再起動しても失わないようにするわけです。お楽しみに! 🙌

📚 Discordボット開発シリーズ

連載 · 全32回中13回目

Discordボット開発

  1. #1Discordボット開発 - スラッシュコマンド(Slash Command)完全攻略!
  2. #2スラッシュコマンドにオプション(Arguments)を追加しよう!
  3. #3スラッシュコマンドにサブコマンド(Subcommand)を追加しよう!
  4. #4スラッシュコマンドで埋め込み(Embed)を送信しよう!
  5. #5ボタン(Button)のインタラクションを実装しよう!
  6. #6ドロップダウン(Select Menu)メニューを実装しよう!
  7. #7モーダル(Modal)で入力を受け取る方法 (Pythonサンプル付き)
  8. #8埋め込み(Embed)を画像・フィールドで飾る (Pythonサンプル付き)
  9. #9AI(Claude・ChatGPT API)と連携する! (Pythonサンプル付き)
  10. #10多言語(i18n)に対応する! (Pythonサンプル付き)
  11. #11FlaskでWebダッシュボードを作る! (Pythonサンプル付き)
  12. #12コマンドのクールダウン(Cooldown)・権限(Permission)を管理しよう! (Pythonサンプル付き)
  13. #13ロール・チャンネルの自動化でサーバーを管理しよう! (Pythonサンプル付き)現在の記事
  14. #14データを保存しよう!JSON vs SQLite (Pythonサンプル付き)
  15. #15슬래시 명령어 그룹(Group)으로 명령어 정리하기! (파이썬 예제) (韓国語)
  16. #16리마인더·알림 기능 만들기! 봇이 먼저 말 걸게 하기 (파이썬 예제) (韓国語)
  17. #17RAG로 문서 기반 답변봇 만들기! 우리 서버 전용 FAQ 봇 (파이썬 예제) (韓国語)
  18. #18투표(Poll) 기능 만들기! 다수결로 정하는 서버 규칙 (파이썬 예제) (韓国語)
  19. #19Cog로 코드 쪼개기! 1000줄 봇도 안 더러워지는 구조 (파이썬 예제) (韓国語)
  20. #20자동 환영 메시지·온보딩 만들기! 규칙 동의 버튼으로 자동 역할 부여 (파이썬 예제) (韓国語)
  21. #21키워드 자동응답·금지어 필터 만들기! 경고 누적으로 자동 제재까지 (파이썬 예제) (韓国語)
  22. #22슬래시 명령어 자동완성(Autocomplete) 만들기! 금지어를 골라서 추가·삭제 (파이썬 예제) (韓国語)
  23. #23aiosqlite로 비동기 DB 제대로 설계하기! JSON 탈출 (파이썬 예제) (韓国語)
  24. #24채널 난독화(Channel Obfuscation) 대응하기! 11/16부터 안 보이는 채널이 생겨요 (파이썬 예제) (韓国語)
  25. #25봇이 죽지 않게! 에러 핸들링·로깅 체계 잡기 (파이썬 예제) (韓国語)
  26. #26티켓(Ticket) 문의 시스템 만들기! 버튼 하나로 1:1 상담 채널 자동 생성 (파이썬 예제) (韓国語)
  27. #27티켓 담당자 배정·우선순위 만들기! 스태프가 여러 명이어도 안 꼬이게 (파이썬 예제) (韓国語)
  28. #28레벨·경험치(XP) 시스템 만들기! 채팅할수록 레벨업, 랭킹판까지 (파이썬 예제) (韓国語)
  29. #29화폐·상점(Economy) 시스템 만들기! 코인 벌어서 아이템 사기 (파이썬 예제) (韓国語)
  30. #30投票・アンケート(Poll)ボットを作ろう!ネイティブ投票+ボタンでリアルタイム集計まで(Pythonサンプル) 🗳️
  31. #32サーバーログ・監査ログ(Audit Log)チャンネルを作ろう!メッセージの削除・編集・入退室・キックまで自動記録(Pythonサンプル) 🕵️
  32. #33임시 음성방 확장, 파티방 만들기! 방장 위임·강퇴·초대 전용(비공개) 방 + 전용 채팅방 자동 생성 (파이썬 예제) 🎉 (韓国語)
全32回を表示閉じる

著者

Mojangcam

自分でコードを書き、サーバーを動かし、詰まったエラーを解決しながら学んだことを記録する開発・IT実践ブログ