# ╔══════════════════════════════════════════════════════════════════╗ # ║ ║ # ║ +-+-+-+-+-+-+-+-+ ║ # ║ |H|e|x|a|H|o|s|t| ║ # ║ +-+-+-+-+-+-+-+-+ ║ # ║ ║ # ║ © 2026 HexaHost — All Rights Reserved ║ # ║ ║ # ║ discord ── https://discord.gg/hexahost ║ # ║ github ── https://github.com/theoneandonlymace ║ # ║ ║ # ╚══════════════════════════════════════════════════════════════════╝ from utils.db_paths import db_path import discord from discord.ext import commands, tasks import sqlite3 from datetime import datetime, timedelta import re from utils.cv2 import CV2 DB_FILE = db_path("jail.db") class Jail(commands.Cog): def __init__(self, bot): self.bot = bot self.conn = sqlite3.connect(DB_FILE) self.conn.execute(""" CREATE TABLE IF NOT EXISTS jailed ( guild_id TEXT, user_id TEXT, mod_id TEXT, reason TEXT, jailed_at TEXT, duration INTEGER, roles TEXT, PRIMARY KEY (guild_id, user_id) ); """) self.conn.execute(""" CREATE TABLE IF NOT EXISTS jail_settings ( guild_id TEXT PRIMARY KEY, jail_role TEXT, jail_channel TEXT, mod_role TEXT, log_channel TEXT ); """) # Ensure roles column exists (safe migration) try: self.conn.execute("SELECT roles FROM jailed LIMIT 1;") except sqlite3.OperationalError: self.conn.execute("ALTER TABLE jailed ADD COLUMN roles TEXT;") self.conn.commit() self.jail_check_loop.start() def cog_unload(self): self.jail_check_loop.cancel() self.conn.close() def get_setting(self, guild_id, field): cursor = self.conn.execute(f"SELECT {field} FROM jail_settings WHERE guild_id = ?", (str(guild_id),)) row = cursor.fetchone() return row[0] if row else None def set_setting(self, guild_id, field, value): self.conn.execute(f""" INSERT INTO jail_settings (guild_id, {field}) VALUES (?, ?) ON CONFLICT(guild_id) DO UPDATE SET {field} = excluded.{field} """, (str(guild_id), str(value))) self.conn.commit() def parse_duration(self, duration_str: str): pattern = re.compile(r'((?P\d+)h)?((?P\d+)m)?') match = pattern.fullmatch(duration_str.lower()) if not match: return None hours = int(match.group('hours') or 0) minutes = int(match.group('minutes') or 0) return (hours * 60 + minutes) * 60 if (hours or minutes) else None @tasks.loop(minutes=1) async def jail_check_loop(self): now = datetime.utcnow() cursor = self.conn.execute("SELECT guild_id, user_id, duration, jailed_at, roles FROM jailed") for guild_id, user_id, duration, jailed_at, roles in cursor.fetchall(): if not duration: continue jailed_time = datetime.fromisoformat(jailed_at) if (now - jailed_time).total_seconds() >= duration: guild = self.bot.get_guild(int(guild_id)) if guild: member = guild.get_member(int(user_id)) if member: await self.unjail_member(guild, member) async def unjail_member(self, guild, member): jail_role_id = self.get_setting(guild.id, "jail_role") if jail_role_id: jail_role = guild.get_role(int(jail_role_id)) if jail_role in member.roles: await member.remove_roles(jail_role, reason="Unjailed") # Restore old roles cursor = self.conn.execute("SELECT roles FROM jailed WHERE guild_id = ? AND user_id = ?", (str(guild.id), str(member.id))) row = cursor.fetchone() if row and row[0]: role_ids = map(int, row[0].split(",")) roles = [guild.get_role(rid) for rid in role_ids if guild.get_role(rid)] await member.add_roles(*roles, reason="Restored previous roles after jail") self.conn.execute("DELETE FROM jailed WHERE guild_id = ? AND user_id = ?", (str(guild.id), str(member.id))) self.conn.commit() try: await member.send(f"🔓 You have been unjailed in **{guild.name}**.") except: pass log_channel_id = self.get_setting(guild.id, "log_channel") if log_channel_id: log_channel = guild.get_channel(int(log_channel_id)) if log_channel: desc = ( f"**User:** {member.mention}\n" f"**Time:** " ) await log_channel.send(view=CV2("🔓 Member Unjailed", desc)) @commands.command(name="jail") @commands.has_permissions(manage_roles=True) async def jail(self, ctx, member: discord.Member, duration: str = None, *, reason="No reason provided"): jail_role_id = self.get_setting(ctx.guild.id, "jail_role") jail_channel_id = self.get_setting(ctx.guild.id, "jail_channel") log_channel_id = self.get_setting(ctx.guild.id, "log_channel") if not jail_role_id or not jail_channel_id: return await ctx.send(view=CV2("⚠️ Configuration Error", "Jail system not fully configured.")) jail_role = ctx.guild.get_role(int(jail_role_id)) if not jail_role: return await ctx.send(view=CV2("⚠️ Configuration Error", "Jail role does not exist.")) duration_secs = self.parse_duration(duration) if duration else None jailed_at = datetime.utcnow().isoformat() roles_str = ",".join(str(r.id) for r in member.roles if r != ctx.guild.default_role) self.conn.execute("DELETE FROM jailed WHERE guild_id = ? AND user_id = ?", (str(ctx.guild.id), str(member.id))) self.conn.execute(""" INSERT INTO jailed (guild_id, user_id, mod_id, reason, jailed_at, duration, roles) VALUES (?, ?, ?, ?, ?, ?, ?) """, (str(ctx.guild.id), str(member.id), str(ctx.author.id), reason, jailed_at, duration_secs, roles_str)) self.conn.commit() try: await member.edit(roles=[jail_role], reason="Jailed") except discord.Forbidden: return await ctx.send(view=CV2("❌ Permission Error", "I don't have permission to change roles.")) jail_channel = ctx.guild.get_channel(int(jail_channel_id)) if jail_channel: await jail_channel.set_permissions(member, view_channel=True, send_messages=True, read_message_history=True) try: await member.send(f"🔒 You were jailed in **{ctx.guild.name}**.\n📝 Reason: {reason}\n⏰ Duration: {duration or 'Permanent'}") except: pass await ctx.send(view=CV2("✅ Success", f"🔒 {member.mention} has been jailed {'for ' + duration if duration else 'permanently'}.")) if log_channel_id: log_channel = ctx.guild.get_channel(int(log_channel_id)) if log_channel: desc = ( f"**User:** {member.mention}\n" f"**Moderator:** {ctx.author.mention}\n" f"**Reason:** {reason}\n" f"**Duration:** {duration or 'Permanent'}\n" f"**Time:** " ) await log_channel.send(view=CV2("🔒 Member Jailed", desc)) @commands.command(name="unjail") @commands.has_permissions(manage_roles=True) async def unjail(self, ctx, member: discord.Member): await self.unjail_member(ctx.guild, member) await ctx.send(view=CV2("✅ Success", f"{member.mention} has been unjailed.")) @commands.command(name="jailhistory") async def jailhistory(self, ctx, member: discord.Member): cursor = self.conn.execute(""" SELECT reason, jailed_at, duration, mod_id FROM jailed WHERE guild_id = ? AND user_id = ? """, (str(ctx.guild.id), str(member.id))) row = cursor.fetchone() if row: reason, jailed_at, duration, mod_id = row mod = ctx.guild.get_member(int(mod_id)) jailed_time = datetime.fromisoformat(jailed_at) desc = ( f"**User:** {member.mention}\n" f"**Reason:** {reason}\n" f"**Jailed At:** \n" f"**Duration:** {f'{duration // 60} minutes' if duration else 'Permanent'}\n" f"**Jailed By:** {mod.mention if mod else 'Unknown'}" ) await ctx.send(view=CV2("📄 Jail Record", desc)) else: await ctx.send(view=CV2("❌ Error", f"No jail record found for {member.mention}")) async def setup(bot): await bot.add_cog(Jail(bot))