You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.24 07:24:03