From d9b2422111cb6bd41ddfdb28bd5f694de70f8b18 Mon Sep 17 00:00:00 2001 From: Lilac-Rose Date: Fri, 10 Oct 2025 12:45:36 +0200 Subject: [PATCH] Finally fixed muting, role gets removed after duration --- moderation/mute.py | 88 ++++++++++++++++++++++++++++++++++--------- moderation/unmmute.py | 17 ++++++--- 2 files changed, 81 insertions(+), 24 deletions(-) diff --git a/moderation/mute.py b/moderation/mute.py index f0267e7..e3e987e 100644 --- a/moderation/mute.py +++ b/moderation/mute.py @@ -1,25 +1,32 @@ import discord -from discord.ext import commands +from discord.ext import commands, tasks from discord.ui import View, Button import asyncio import re -from datetime import timedelta +import sqlite3 +import os +from datetime import timedelta, datetime from .loader import ModerationBase MUTE_ROLE_ID = 982702037517090836 class MuteCommand(ModerationBase): + def __init__(self, bot): + super().__init__(bot) + self.bot = bot + self.check_mutes.start() + + def cog_unload(self): + self.check_mutes.cancel() + super().cog_unload() @commands.command(name="mute") @ModerationBase.is_admin() async def mute(self, ctx, user: discord.Member, duration: str, *, reason: str = None): - """Mute a user for a duration with confirmation and log infraction""" - # Parse duration - match = re.match(r"(\d+)([wdh])", duration.lower()) + match = re.match(r"(\d+)([wdhm])", duration.lower()) if not match: - await ctx.send("Invalid duration format. Use **1w**, **5d**, **48h**, etc.") + await ctx.send("Invalid duration format. Use **1w**, **5d**, **12h**, **30m**, etc.") return - value, unit = match.groups() value = int(value) if unit == "w": @@ -28,6 +35,8 @@ class MuteCommand(ModerationBase): delta = timedelta(days=value) elif unit == "h": delta = timedelta(hours=value) + elif unit == "m": + delta = timedelta(minutes=value) view = View(timeout=30) confirmed = {"value": False} @@ -52,7 +61,6 @@ class MuteCommand(ModerationBase): no_button = Button(label="No", style=discord.ButtonStyle.red) yes_button.callback = yes_callback no_button.callback = no_callback - view.add_item(yes_button) view.add_item(no_button) @@ -67,7 +75,6 @@ class MuteCommand(ModerationBase): return await user.add_roles(mute_role, reason=reason) - try: await user.send(f"You have been muted in **{ctx.guild.name}** for **{duration}**.\nReason: {reason or 'No reason provided'}") except: @@ -76,16 +83,61 @@ class MuteCommand(ModerationBase): await self.log_infraction(ctx.guild.id, user.id, ctx.author.id, "mute", reason) await ctx.send(f"{user.mention} has been muted for **{duration}**.") - # Schedule unmute - async def unmute_later(): - await asyncio.sleep(delta.total_seconds()) - try: - await user.remove_roles(mute_role, reason="Mute duration expired") - await ctx.send(f"{user.mention} has been unmuted (duration expired).") - except: - pass + db_path = os.path.join(os.path.dirname(__file__), "moderation.db") + conn = sqlite3.connect(db_path) + c = conn.cursor() + c.execute(""" + CREATE TABLE IF NOT EXISTS mutes ( + user_id INTEGER, + guild_id INTEGER, + channel_id INTEGER, + unmute_time TEXT + ) + """) + unmute_time = (datetime.utcnow() + delta).isoformat() + c.execute("INSERT INTO mutes (user_id, guild_id, channel_id, unmute_time) VALUES (?, ?, ?, ?)", + (user.id, ctx.guild.id, ctx.channel.id, unmute_time)) + conn.commit() + conn.close() - asyncio.create_task(unmute_later()) + asyncio.create_task(self.schedule_unmute(user.id, ctx.guild.id, ctx.channel.id, delta.total_seconds())) + + async def schedule_unmute(self, user_id, guild_id, channel_id, delay): + await asyncio.sleep(delay) + db_path = os.path.join(os.path.dirname(__file__), "moderation.db") + conn = sqlite3.connect(db_path) + c = conn.cursor() + c.execute("DELETE FROM mutes WHERE user_id = ? AND guild_id = ?", (user_id, guild_id)) + conn.commit() + conn.close() + guild = self.bot.get_guild(guild_id) + if not guild: + return + member = guild.get_member(user_id) + if not member: + return + mute_role = guild.get_role(MUTE_ROLE_ID) + if not mute_role: + return + try: + await member.remove_roles(mute_role, reason="Mute duration expired") + channel = guild.get_channel(channel_id) + if channel: + await channel.send(f"{member.mention} has been unmuted (duration expired).") + except: + pass + + @tasks.loop(minutes=1) + async def check_mutes(self): + db_path = os.path.join(os.path.dirname(__file__), "moderation.db") + conn = sqlite3.connect(db_path) + c = conn.cursor() + now = datetime.utcnow().isoformat() + c.execute("SELECT user_id, guild_id, channel_id, unmute_time FROM mutes WHERE unmute_time <= ?", (now,)) + expired = c.fetchall() + for user_id, guild_id, channel_id, _ in expired: + asyncio.create_task(self.schedule_unmute(user_id, guild_id, channel_id, 0)) + conn.close() async def setup(bot: commands.Bot): await bot.add_cog(MuteCommand(bot)) diff --git a/moderation/unmmute.py b/moderation/unmmute.py index 54a9534..738a6c8 100644 --- a/moderation/unmmute.py +++ b/moderation/unmmute.py @@ -1,7 +1,8 @@ import discord from discord.ext import commands from discord.ui import View, Button -import asyncio +import sqlite3 +import os from .loader import ModerationBase MUTE_ROLE_ID = 982702037517090836 @@ -11,7 +12,6 @@ class UnmuteCommand(ModerationBase): @commands.command(name="unmute") @ModerationBase.is_admin() async def unmute(self, ctx, user: discord.Member): - """Unmute a user with confirmation and log infraction""" view = View(timeout=30) confirmed = {"value": False} @@ -31,11 +31,10 @@ class UnmuteCommand(ModerationBase): await interaction.response.edit_message(content="❌ Cancelled.", view=None) view.stop() - yes_button = Button(label="Yes", style=discord.ButtonStyle.green) - no_button = Button(label="No", style=discord.ButtonStyle.red) + yes_button = discord.ui.Button(label="Yes", style=discord.ButtonStyle.green) + no_button = discord.ui.Button(label="No", style=discord.ButtonStyle.red) yes_button.callback = yes_callback no_button.callback = no_callback - view.add_item(yes_button) view.add_item(no_button) @@ -50,7 +49,13 @@ class UnmuteCommand(ModerationBase): return if mute_role in user.roles: - await user.remove_roles(mute_role, reason="Unmute issued by command") + await user.remove_roles(mute_role, reason="Manual unmute issued") + db_path = os.path.join(os.path.dirname(__file__), "moderation.db") + conn = sqlite3.connect(db_path) + c = conn.cursor() + c.execute("DELETE FROM mutes WHERE user_id = ? AND guild_id = ?", (user.id, ctx.guild.id)) + conn.commit() + conn.close() try: await user.send(f"You have been **unmuted** in **{ctx.guild.name}**.") except: