Discord.py临时禁言命令开发:实现移除原有角色并到期自动恢复功能
Discord.py 临时禁言命令(带角色自动归还)实现方案
前置准备
- 提前在服务器内创建好禁言专用角色,配置好对应频道的权限(比如禁止发送消息、连接语音等),记录该角色的ID备用
- 建议使用持久化存储(比如sqlite、json文件)保存禁言记录,避免机器人重启后待到期的禁言任务丢失
- 确保你的机器人拥有
管理角色、管理成员权限,且机器人的角色层级要高于禁言角色和所有待操作用户的角色层级
核心实现逻辑
1. 禁言执行阶段逻辑
当管理员触发禁言命令时,按以下步骤执行:
- 校验命令执行者的权限,只有拥有
管理成员权限的用户可以调用该命令 - 校验目标用户的身份:不能禁言服务器所有者、不能禁言权限比自己高的用户、不能禁言机器人本身
- 遍历目标用户当前所有的非默认角色(@everyone是默认角色不需要移除),将这些角色ID存入列表,再将列表和用户ID、禁言到期时间、服务器ID一起存入持久化存储
- 批量移除目标用户的所有上述非默认角色,再为其添加禁言角色
- 向命令执行通道发送执行成功提示,同时启动定时任务等待到期
2. 禁言到期执行逻辑
- 机器人启动时,先遍历持久化存储中所有未到期的禁言记录,为每条记录创建对应的定时任务
- 定时任务触发时,先校验目标用户是否还在服务器内、是否还持有禁言角色
- 移除用户的禁言角色,批量归还之前存储的所有原有角色
- 删除持久化存储中对应的禁言记录,操作完成
核心代码参考
以下代码基于Discord.py v2.x版本编写:
import discord from discord import app_commands from discord.ext import commands, tasks import datetime import sqlite3 import asyncio # 替换为你自己的禁言角色ID MUTE_ROLE_ID = 123456789012345678 # 初始化机器人,按需调整intents intents = discord.Intents.default() intents.members = True bot = commands.Bot(command_prefix="!", intents=intents) # 初始化数据库,存储禁言记录 def init_db(): conn = sqlite3.connect('mute_records.db') c = conn.cursor() c.execute('''CREATE TABLE IF NOT EXISTS mutes (user_id INTEGER, guild_id INTEGER, role_ids TEXT, expire_time INTEGER, PRIMARY KEY (user_id, guild_id))''') conn.commit() conn.close() # 临时禁言斜杠命令 @app_commands.command(name="tempmute", description="临时禁言用户,到期自动归还角色") @app_commands.checks.has_permissions(manage_members=True) async def tempmute(interaction: discord.Interaction, user: discord.Member, duration: str, reason: str = "无"): # 解析时长,支持s(秒)/m(分)/h(小时)/d(天) unit = duration[-1].lower() try: num = int(duration[:-1]) except: await interaction.response.send_message("时长格式错误,示例:10m 代表10分钟", ephemeral=True) return time_map = {'s':1, 'm':60, 'h':3600, 'd':86400} if unit not in time_map: await interaction.response.send_message("仅支持的时长单位:s/m/h/d", ephemeral=True) return total_seconds = num * time_map[unit] expire_time = int((datetime.datetime.now() + datetime.timedelta(seconds=total_seconds)).timestamp()) # 收集用户原有角色(排除@everyone) original_roles = [role.id for role in user.roles if role.id != interaction.guild.default_role.id] # 存储到数据库 conn = sqlite3.connect('mute_records.db') c = conn.cursor() c.execute("REPLACE INTO mutes VALUES (?, ?, ?, ?)", (user.id, interaction.guild.id, ','.join(map(str, original_roles)), expire_time)) conn.commit() conn.close() # 移除原有角色,添加禁言角色 await user.remove_roles(*[role for role in user.roles if role.id != interaction.guild.default_role.id], reason=reason) mute_role = interaction.guild.get_role(MUTE_ROLE_ID) await user.add_roles(mute_role, reason=reason) await interaction.response.send_message(f"已禁言 {user.mention},时长:{duration},到期后将自动归还所有原有角色") # 启动到期处理任务 await asyncio.sleep(total_seconds) # 到期后校验并执行归还 conn = sqlite3.connect('mute_records.db') c = conn.cursor() c.execute("SELECT role_ids FROM mutes WHERE user_id = ? AND guild_id = ?", (user.id, interaction.guild.id)) res = c.fetchone() if res: # 记录存在,执行归还 role_ids = list(map(int, res[0].split(','))) roles = [interaction.guild.get_role(rid) for rid in role_ids if interaction.guild.get_role(rid) is not None] await user.remove_roles(mute_role, reason="禁言到期") await user.add_roles(*roles, reason="禁言到期归还角色") # 删除记录 c.execute("DELETE FROM mutes WHERE user_id = ? AND guild_id = ?", (user.id, interaction.guild.id)) conn.commit() conn.close() # 兜底定时任务:每分钟检查到期禁言,避免机器人重启后任务丢失 @tasks.loop(minutes=1) async def check_expired_mutes(): now = int(datetime.datetime.now().timestamp()) conn = sqlite3.connect('mute_records.db') c = conn.cursor() c.execute("SELECT user_id, guild_id, role_ids, expire_time FROM mutes WHERE expire_time <= ?", (now,)) expired = c.fetchall() for record in expired: user_id, guild_id, role_ids_str, expire_time = record guild = bot.get_guild(guild_id) if not guild: continue user = guild.get_member(user_id) if not user: continue mute_role = guild.get_role(MUTE_ROLE_ID) if not mute_role or mute_role not in user.roles: continue # 归还角色 role_ids = list(map(int, role_ids_str.split(','))) roles = [guild.get_role(rid) for rid in role_ids if guild.get_role(rid) is not None] await user.remove_roles(mute_role, reason="禁言到期") await user.add_roles(*roles, reason="禁言到期归还角色") # 删除记录 c.execute("DELETE FROM mutes WHERE user_id = ? AND guild_id = ?", (user_id, guild_id)) conn.commit() conn.close() @bot.event async def on_ready(): init_db() check_expired_mutes.start() # 同步斜杠命令,首次运行需要执行 await bot.tree.sync() print(f"Logged in as {bot.user}") # 启动机器人,替换为你自己的机器人Token bot.run("你的机器人Token")
注意事项
- 如果你使用json做持久化存储,只需要修改数据读写部分即可,核心逻辑保持一致
- 如果禁言期间有管理员手动给用户加了其他角色,到期归还时不会覆盖这些新增的角色,只会归还禁言前记录的原有角色
- 为了避免角色操作速率限制,建议在批量移除/添加角色时增加少量延迟,或者处理Discord.py抛出的速率限制异常
- 如果你使用的是Discord.py v1.x版本,需要将斜杠命令相关的代码替换为前缀命令的写法即可
内容的提问来源于stack exchange,提问作者Justin
相关产品推荐
相关产品推荐

