分类: Discord 机器人开发

Discord 机器人开发#13用身份组·频道自动化管理服务器!(Python 示例)

本文译自韩语原文。

目录9 项

大家好!我是 Mojangcam 😎

上一篇 #12 我们学习了用命令冷却时间和权限来保护机器人。这一篇 #13 再往前走一步,把服务器管理员的重复劳动整个交给机器人。今天要做的自动化正好有三个。

  • 新成员加入时自动分配身份组(欢迎身份组)
  • 点一下按钮就能自助领取想要的身份组(身份组按钮面板)
  • 进入语音频道时自动创建个人房间(Join to Create)

服务器越大,"给新来的人加身份组"、"给想一起玩游戏的人分身份组"、"给大家开会议房间"这类事情就会没完没了地重复。靠人手去做,熬一整夜也做不完。交给机器人的话,它会 24 小时自动处理。那我们开始吧!🙌

📌 准备工作

首先把开发环境准备好。

  • Python 3.9 及以上(discord.py 2.7 需要 3.9+)
  • discord.py 2.x(截至 2026 年 7 月,最新稳定版本是 2.7.1)

安装库只需要在终端里敲一行。

终端
pip install -U discord.py

另外,这一篇一定要设置特权 Intent(Privileged Intents)。因为要检测新成员加入,就必须打开 Server Members Intent。

  1. 进入 Discord 开发者门户 → 选择你的机器人
  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          # 检测成员加入(特权 Intent)
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):把找到的身份组加给这个成员。核心就这一行!

现在只要先在服务器里建好一个叫"萌新"的身份组,新成员一进来就会带着萌新身份组登场。🌱

📌 第 2 步 - 一个按钮自助领取身份组

之前的 #5 按钮(Button)篇 学过按钮对吧?这次就用按钮做一个让成员自己挑选身份组的面板。管理员再也不用一个个手动加身份组了。

这里必须了解的概念是持久化 View(Persistent View)。通常机器人一重启,按钮就失灵了,但只要遵守下面两个条件,重启之后按钮也能继续工作。

  • 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)。一个按钮就能实现切换(toggle)。
  • 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。多亏这两行,即使重启机器人,之前发出的按钮也照样能用。
  • @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"。

📌 完整代码(可直接复制)

复制下面的代码,只要把 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 = "在这里输入机器人 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          # 检测成员加入(特权 Intent)
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 没有打开。 解决:在 开发者门户 → Bot → Privileged Gateway Intents 里打开 Server Members Intent,代码里也要加上 intents.members = True。两边都打开才会生效。

3. 重启机器人后按钮显示"此交互失败" 原因:没有遵守持久化 View 的条件。 解决:确认 View 设置了 timeout=None,所有按钮都有 custom_id,并且在 setup_hook 里用 add_view 重新注册过。这三样缺一不可。

4. 临时语音频道没有被删除 原因:机器人没有管理频道权限,或者 ID 没有存进 temp_channels。 解决:检查管理频道权限,并确认创建频道时用 temp_channels.add(...) 保存了 ID。

📌 结语

今天我们做了三个把服务器管理整个交给机器人的自动化:加入时自动分配身份组、按钮自助领取身份组、自动创建语音频道 —— 光有这三个,管理员的重复工作就会大大减少。尤其是持久化 View(timeout=None + custom_id + add_view),以后不管做什么按钮/菜单都会一直用到,一定要掌握!💪

下一篇 #14 我们来讲用 JSON 和 SQLite 保存机器人数据的方法。让身份组·设置·记录在机器人关掉再打开后也不会丢失。敬请期待!🙌

📚 Discord 机器人开发系列

连载 · 共 32 篇中的第 13 篇

Discord 机器人开发

  1. #1Discord 机器人开发 - Slash Command 完全攻略!
  2. #2为 Slash Command 添加选项(Arguments)!
  3. #3在 Slash Command 中添加子命令(Subcommand)!
  4. #4用 Slash Command 发送 Embed 嵌入消息!
  5. #5实现按钮(Button)交互!
  6. #6实现下拉菜单(Select Menu)!
  7. #7用模态框(Modal)接收用户输入(Python 示例)
  8. #8用嵌入消息(Embed)的图片和字段美化消息(Python 示例)
  9. #9接入 AI(Claude·ChatGPT API)!(Python 示例)
  10. #10支持多语言(i18n)!(Python 示例)
  11. #11用 Flask 制作网页仪表盘!(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 实战博客