分类: Discord 机器人开发
Discord 机器人开发#13用身份组·频道自动化管理服务器!(Python 示例)
本文译自韩语原文。
目录9 项
大家好!我是 Mojangcam 😎
上一篇 #12 我们学习了用命令冷却时间和权限来保护机器人。这一篇 #13 再往前走一步,把服务器管理员的重复劳动整个交给机器人。今天要做的自动化正好有三个。
- 新成员加入时自动分配身份组(欢迎身份组)
- 点一下按钮就能自助领取想要的身份组(身份组按钮面板)
- 进入语音频道时自动创建个人房间(Join to Create)
服务器越大,"给新来的人加身份组"、"给想一起玩游戏的人分身份组"、"给大家开会议房间"这类事情就会没完没了地重复。靠人手去做,熬一整夜也做不完。交给机器人的话,它会 24 小时自动处理。那我们开始吧!🙌
📌 准备工作
首先把开发环境准备好。
- Python 3.9 及以上(
discord.py2.7 需要 3.9+) discord.py2.x(截至 2026 年 7 月,最新稳定版本是 2.7.1)
安装库只需要在终端里敲一行。
pip install -U discord.py另外,这一篇一定要设置特权 Intent(Privileged Intents)。因为要检测新成员加入,就必须打开 Server Members Intent。
- 进入 Discord 开发者门户 → 选择你的机器人
- 左侧 Bot 标签页 → Privileged Gateway Intents
- 打开 Server Members Intent ✅(如果要用消息命令,也把 Message Content Intent 打开)
最后,还要在服务器里确认机器人身份组的权限。要让自动化正常工作,机器人需要下面这些权限。
- 管理身份组(Manage Roles) —— 添加和移除身份组
- 管理频道(Manage Channels) —— 创建和删除语音频道
- 移动成员(Move Members) —— 把成员移到新建的房间
💡 最重要的一点! 机器人要分配的身份组,必须位于机器人自己的身份组下方。请在 服务器设置 → 身份组 列表里,把机器人的身份组拖到上面去。漏掉这一步,就会出现后面要讲的
Forbidden错误。
📌 第 1 步 - 新成员加入时自动分配身份组
先从最基础的自动欢迎身份组做起。有人加入服务器的那一刻,on_member_join 事件就会执行。在这里找到身份组并加上去就完成了。
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
# 按钮身份组列表(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:就是"只有自己能看到的消息"。不会打扰别人的界面,悄悄告诉你结果。
接下来加上把这个面板发到聊天里的命令,以及让按钮在重启后依然有效的代码。
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 事件来检测。
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 改成适合你服务器的值,就能直接运行。
# 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)📌 运行结果
启动机器人,在服务器里逐个确认一下吧。
- 用新账号加入 → 自动带上"萌新"身份组。🌱
- 管理员输入
!역할판→ 出现一条带 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 机器人开发系列
- #5 - 按钮(Button)交互
- #6 - 下拉菜单(Select Menu)
- #7 - 用模态框(Modal)接收输入
- #8 - 美化嵌入消息(Embed)
- #9 - 接入 AI(Claude·ChatGPT API)
- #10 - 支持多语言(i18n)
- #11 - 用 Flask 制作网页仪表盘
- #12 - 管理命令冷却时间·权限
- #13 - 身份组·频道自动化 ← 就是这一篇!
连载 · 共 32 篇中的第 13 篇
Discord 机器人开发
- #1Discord 机器人开发 - Slash Command 完全攻略!
- #2为 Slash Command 添加选项(Arguments)!
- #3在 Slash Command 中添加子命令(Subcommand)!
- #4用 Slash Command 发送 Embed 嵌入消息!
- #5实现按钮(Button)交互!
- #6实现下拉菜单(Select Menu)!
- #7用模态框(Modal)接收用户输入(Python 示例)
- #8用嵌入消息(Embed)的图片和字段美化消息(Python 示例)
- #9接入 AI(Claude·ChatGPT API)!(Python 示例)
- #10支持多语言(i18n)!(Python 示例)
- #11用 Flask 制作网页仪表盘!(Python 示例)
- #12管理命令冷却时间(Cooldown)与权限(Permission)!(Python 示例)
- #13用身份组·频道自动化管理服务器!(Python 示例)当前文章
- #14保存数据!JSON vs SQLite(Python 示例)
- #15슬래시 명령어 그룹(Group)으로 명령어 정리하기! (파이썬 예제) (韩语)
- #16리마인더·알림 기능 만들기! 봇이 먼저 말 걸게 하기 (파이썬 예제) (韩语)
- #17RAG로 문서 기반 답변봇 만들기! 우리 서버 전용 FAQ 봇 (파이썬 예제) (韩语)
- #18투표(Poll) 기능 만들기! 다수결로 정하는 서버 규칙 (파이썬 예제) (韩语)
- #19Cog로 코드 쪼개기! 1000줄 봇도 안 더러워지는 구조 (파이썬 예제) (韩语)
- #20자동 환영 메시지·온보딩 만들기! 규칙 동의 버튼으로 자동 역할 부여 (파이썬 예제) (韩语)
- #21키워드 자동응답·금지어 필터 만들기! 경고 누적으로 자동 제재까지 (파이썬 예제) (韩语)
- #22슬래시 명령어 자동완성(Autocomplete) 만들기! 금지어를 골라서 추가·삭제 (파이썬 예제) (韩语)
- #23aiosqlite로 비동기 DB 제대로 설계하기! JSON 탈출 (파이썬 예제) (韩语)
- #24채널 난독화(Channel Obfuscation) 대응하기! 11/16부터 안 보이는 채널이 생겨요 (파이썬 예제) (韩语)
- #25봇이 죽지 않게! 에러 핸들링·로깅 체계 잡기 (파이썬 예제) (韩语)
- #26티켓(Ticket) 문의 시스템 만들기! 버튼 하나로 1:1 상담 채널 자동 생성 (파이썬 예제) (韩语)
- #27티켓 담당자 배정·우선순위 만들기! 스태프가 여러 명이어도 안 꼬이게 (파이썬 예제) (韩语)
- #28레벨·경험치(XP) 시스템 만들기! 채팅할수록 레벨업, 랭킹판까지 (파이썬 예제) (韩语)
- #29화폐·상점(Economy) 시스템 만들기! 코인 벌어서 아이템 사기 (파이썬 예제) (韩语)
- #30打造投票·问卷(Poll)机器人!原生投票 + 按钮实时统计(Python 示例)🗳️
- #32打造服务器日志·审核日志(Audit Log)频道!自动记录消息删除、编辑、成员进出和踢出(Python 示例)🕵️
- #33임시 음성방 확장, 파티방 만들기! 방장 위임·강퇴·초대 전용(비공개) 방 + 전용 채팅방 자동 생성 (파이썬 예제) 🎉 (韩语)
