first commit
This commit is contained in:
201
bot/cogs/commands/jail.py
Normal file
201
bot/cogs/commands/jail.py
Normal file
@@ -0,0 +1,201 @@
|
||||
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/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<hours>\d+)h)?((?P<minutes>\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:** <t:{int(datetime.utcnow().timestamp())}:f>"
|
||||
)
|
||||
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:** <t:{int(datetime.utcnow().timestamp())}:f>"
|
||||
)
|
||||
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:** <t:{int(jailed_time.timestamp())}:f>\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))
|
||||
Reference in New Issue
Block a user