mirror of
https://github.com/Lilac-Rose/Lacie.git
synced 2026-07-30 15:09:26 -05:00
Updated rank.py to make the message estimator take multiplier into account
This commit is contained in:
parent
f8fc664a93
commit
d243293ad9
|
|
@ -1,7 +1,8 @@
|
|||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ext import commands, tasks
|
||||
import os
|
||||
import sqlite3
|
||||
import asyncio
|
||||
from dotenv import load_dotenv
|
||||
from datetime import datetime
|
||||
|
||||
|
|
@ -18,12 +19,16 @@ class ModerationBase(commands.Cog):
|
|||
self.conn.row_factory = sqlite3.Row
|
||||
self.c = self.conn.cursor()
|
||||
self.initialize_db()
|
||||
self.check_mutes.start()
|
||||
print(f"✅ ModerationBase initialized. DB path: {self.db_path}")
|
||||
|
||||
def cog_unload(self):
|
||||
"""Ensure database connection closes when the cog unloads."""
|
||||
self.check_mutes.cancel()
|
||||
self.conn.close()
|
||||
|
||||
def initialize_db(self):
|
||||
# Infractions table
|
||||
self.c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS infractions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
|
|
@ -35,6 +40,16 @@ class ModerationBase(commands.Cog):
|
|||
timestamp TEXT NOT NULL
|
||||
)
|
||||
""")
|
||||
# Mutes table
|
||||
self.c.execute("""
|
||||
CREATE TABLE IF NOT EXISTS mutes (
|
||||
user_id INTEGER NOT NULL,
|
||||
guild_id INTEGER NOT NULL,
|
||||
unmute_time TEXT NOT NULL,
|
||||
channel_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (user_id, guild_id)
|
||||
)
|
||||
""")
|
||||
self.conn.commit()
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -55,5 +70,134 @@ class ModerationBase(commands.Cog):
|
|||
""", (user_id, guild_id, type_, reason, mod_id, datetime.utcnow().isoformat()))
|
||||
self.conn.commit()
|
||||
|
||||
@tasks.loop(seconds=30)
|
||||
async def check_mutes(self):
|
||||
"""Periodically check for expired mutes."""
|
||||
if not self.bot.is_ready():
|
||||
print("⏳ Bot not ready yet, skipping mute check")
|
||||
return
|
||||
|
||||
now = datetime.utcnow()
|
||||
|
||||
print(f"\n🔍 Checking mutes at {now.isoformat()}")
|
||||
print(f" Bot is in {len(self.bot.guilds)} guild(s)")
|
||||
|
||||
self.c.execute("SELECT * FROM mutes")
|
||||
mutes = self.c.fetchall()
|
||||
|
||||
if not mutes:
|
||||
print(" No active mutes in database")
|
||||
return
|
||||
|
||||
print(f" Found {len(mutes)} active mute(s)")
|
||||
|
||||
for mute in mutes:
|
||||
print(f"\n Checking mute for user {mute['user_id']} in guild {mute['guild_id']}")
|
||||
|
||||
try:
|
||||
unmute_time = datetime.fromisoformat(mute["unmute_time"])
|
||||
print(f" Unmute time: {unmute_time.isoformat()}")
|
||||
print(f" Current time: {now.isoformat()}")
|
||||
print(f" Should unmute: {now >= unmute_time}")
|
||||
except ValueError as e:
|
||||
print(f" ❌ Error parsing unmute time: {e}")
|
||||
continue
|
||||
|
||||
if now >= unmute_time:
|
||||
print(f" ⏰ Time to unmute user {mute['user_id']}")
|
||||
|
||||
# List all guilds bot can see
|
||||
print(f" Bot can see guilds: {[g.id for g in self.bot.guilds]}")
|
||||
|
||||
guild = self.bot.get_guild(mute["guild_id"])
|
||||
if not guild:
|
||||
print(f" ❌ Guild {mute['guild_id']} not found in bot's guild cache")
|
||||
print(f" This likely means the bot left the server or has incorrect intents")
|
||||
# Remove stale mute
|
||||
self.c.execute("DELETE FROM mutes WHERE user_id = ? AND guild_id = ?",
|
||||
(mute["user_id"], mute["guild_id"]))
|
||||
self.conn.commit()
|
||||
continue
|
||||
|
||||
print(f" ✅ Found guild: {guild.name}")
|
||||
|
||||
# Fetch member, even if offline
|
||||
try:
|
||||
member = guild.get_member(mute["user_id"])
|
||||
if not member:
|
||||
print(f" Member not in cache, fetching...")
|
||||
member = await guild.fetch_member(mute["user_id"])
|
||||
print(f" ✅ Fetched member {member}")
|
||||
else:
|
||||
print(f" ✅ Found member {member}")
|
||||
except discord.NotFound:
|
||||
print(f" ❌ Member {mute['user_id']} not found in guild (may have left)")
|
||||
# Remove from DB since user left
|
||||
self.c.execute("DELETE FROM mutes WHERE user_id = ? AND guild_id = ?",
|
||||
(mute["user_id"], mute["guild_id"]))
|
||||
self.conn.commit()
|
||||
continue
|
||||
except Exception as e:
|
||||
print(f" ❌ Error fetching member: {e}")
|
||||
continue
|
||||
|
||||
mute_role = guild.get_role(982702037517090836)
|
||||
if not mute_role:
|
||||
print(f" ❌ Mute role (982702037517090836) not found in guild")
|
||||
continue
|
||||
|
||||
print(f" ✅ Found mute role: {mute_role.name}")
|
||||
print(f" Member has mute role: {mute_role in member.roles}")
|
||||
print(f" Member's roles: {[r.name for r in member.roles]}")
|
||||
|
||||
if mute_role in member.roles:
|
||||
print(f" Attempting to remove mute role...")
|
||||
try:
|
||||
await member.remove_roles(mute_role, reason="Mute duration expired")
|
||||
print(f" ✅ Successfully removed mute role")
|
||||
|
||||
await self.log_infraction(
|
||||
guild.id, member.id, self.bot.user.id,
|
||||
"unmute", "Automatic unmute (duration expired)"
|
||||
)
|
||||
print(f" ✅ Logged infraction")
|
||||
|
||||
# Send message in the original channel
|
||||
channel = guild.get_channel(mute["channel_id"])
|
||||
if channel:
|
||||
try:
|
||||
await channel.send(f"{member.mention} has been automatically unmuted (duration expired).")
|
||||
print(f" ✅ Sent unmute message in #{channel.name}")
|
||||
except Exception as e:
|
||||
print(f" ⚠️ Error sending message: {e}")
|
||||
else:
|
||||
print(f" ⚠️ Channel {mute['channel_id']} not found")
|
||||
|
||||
except discord.Forbidden:
|
||||
print(f" ❌ Permission denied - bot cannot remove roles")
|
||||
except Exception as e:
|
||||
print(f" ❌ Error unmuting {member}: {type(e).__name__}: {e}")
|
||||
# Don't remove from DB if unmute failed
|
||||
continue
|
||||
else:
|
||||
print(f" ⚠️ User doesn't have mute role (maybe already manually unmuted?)")
|
||||
|
||||
# Remove from DB only if we got this far
|
||||
print(f" Removing from database...")
|
||||
self.c.execute("DELETE FROM mutes WHERE user_id = ? AND guild_id = ?",
|
||||
(mute["user_id"], mute["guild_id"]))
|
||||
self.conn.commit()
|
||||
print(f" ✅ Removed mute record from database")
|
||||
else:
|
||||
time_left = unmute_time - now
|
||||
print(f" ⏳ Mute still active. Time remaining: {time_left}")
|
||||
|
||||
@check_mutes.before_loop
|
||||
async def before_check_mutes(self):
|
||||
await self.bot.wait_until_ready()
|
||||
# Wait an extra 5 seconds for guild cache to fully populate
|
||||
await asyncio.sleep(5)
|
||||
print("✅ check_mutes task started (after cache warmup)")
|
||||
|
||||
async def setup(bot: commands.Bot):
|
||||
await bot.add_cog(ModerationBase(bot))
|
||||
await bot.add_cog(ModerationBase(bot))
|
||||
|
|
@ -1,40 +1,46 @@
|
|||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ui import View, Button
|
||||
import asyncio
|
||||
import re
|
||||
from datetime import timedelta
|
||||
from datetime import datetime, timedelta
|
||||
from .loader import ModerationBase
|
||||
|
||||
MUTE_ROLE_ID = 982702037517090836
|
||||
|
||||
class MuteCommand(ModerationBase):
|
||||
|
||||
@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())
|
||||
"""Mute a user for a duration with confirmation and log infraction."""
|
||||
|
||||
# Parse duration (1w, 5d, 48h, 30m)
|
||||
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**, **48h**, **30m**, etc.")
|
||||
return
|
||||
|
||||
value, unit = match.groups()
|
||||
value = int(value)
|
||||
|
||||
if unit == "w":
|
||||
delta = timedelta(weeks=value)
|
||||
elif unit == "d":
|
||||
delta = timedelta(days=value)
|
||||
elif unit == "h":
|
||||
delta = timedelta(hours=value)
|
||||
elif unit == "m":
|
||||
delta = timedelta(minutes=value)
|
||||
else:
|
||||
await ctx.send("Invalid time unit. Use w/d/h/m.")
|
||||
return
|
||||
|
||||
# Confirmation view
|
||||
view = View(timeout=30)
|
||||
confirmed = {"value": False}
|
||||
|
||||
async def yes_callback(interaction: discord.Interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You can’t confirm this action.", ephemeral=True)
|
||||
await interaction.response.send_message("You can't confirm this action.", ephemeral=True)
|
||||
return
|
||||
confirmed["value"] = True
|
||||
await interaction.response.edit_message(content="✅ Confirmed.", view=None)
|
||||
|
|
@ -42,7 +48,7 @@ class MuteCommand(ModerationBase):
|
|||
|
||||
async def no_callback(interaction: discord.Interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You can’t cancel this action.", ephemeral=True)
|
||||
await interaction.response.send_message("You can't cancel this action.", ephemeral=True)
|
||||
return
|
||||
confirmed["value"] = False
|
||||
await interaction.response.edit_message(content="❌ Cancelled.", view=None)
|
||||
|
|
@ -52,12 +58,16 @@ 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)
|
||||
|
||||
await ctx.send(f"Are you sure you want to mute {user.mention} for **{duration}**? Reason: {reason or 'No reason provided'}", view=view)
|
||||
await ctx.send(
|
||||
f"Are you sure you want to mute {user.mention} for **{duration}**? "
|
||||
f"Reason: {reason or 'No reason provided'}",
|
||||
view=view
|
||||
)
|
||||
await view.wait()
|
||||
|
||||
if not confirmed["value"]:
|
||||
return
|
||||
|
||||
|
|
@ -66,26 +76,35 @@ class MuteCommand(ModerationBase):
|
|||
await ctx.send("Mute role not found in server.")
|
||||
return
|
||||
|
||||
# Add mute role
|
||||
await user.add_roles(mute_role, reason=reason)
|
||||
|
||||
# Try to DM the user
|
||||
try:
|
||||
await user.send(f"You have been muted in **{ctx.guild.name}** for **{duration}**.\nReason: {reason or 'No reason provided'}")
|
||||
await user.send(
|
||||
f"You have been muted in **{ctx.guild.name}** for **{duration}**.\n"
|
||||
f"Reason: {reason or 'No reason provided'}"
|
||||
)
|
||||
except:
|
||||
await ctx.send("Could not DM the user.")
|
||||
|
||||
# Log infraction
|
||||
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
|
||||
|
||||
asyncio.create_task(unmute_later())
|
||||
# Store mute in DB for auto-unmute (fixed to use consistent data types)
|
||||
unmute_time = datetime.utcnow() + delta
|
||||
try:
|
||||
self.c.execute("""
|
||||
INSERT OR REPLACE INTO mutes (user_id, guild_id, unmute_time, channel_id)
|
||||
VALUES (?, ?, ?, ?)
|
||||
""", (user.id, ctx.guild.id, unmute_time.isoformat(), ctx.channel.id))
|
||||
self.conn.commit()
|
||||
print(f"✅ Stored mute for {user} until {unmute_time.isoformat()}")
|
||||
except Exception as e:
|
||||
print(f"❌ Failed to insert mute into DB: {e}")
|
||||
await ctx.send(f"⚠️ Warning: Auto-unmute may not work. Error: {e}")
|
||||
|
||||
async def setup(bot: commands.Bot):
|
||||
await bot.add_cog(MuteCommand(bot))
|
||||
await bot.add_cog(MuteCommand(bot))
|
||||
|
|
@ -1,23 +1,23 @@
|
|||
import discord
|
||||
from discord.ext import commands
|
||||
from discord.ui import View, Button
|
||||
import asyncio
|
||||
from .loader import ModerationBase
|
||||
|
||||
MUTE_ROLE_ID = 982702037517090836
|
||||
|
||||
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"""
|
||||
|
||||
# Confirmation view
|
||||
view = View(timeout=30)
|
||||
confirmed = {"value": False}
|
||||
|
||||
async def yes_callback(interaction: discord.Interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You can’t confirm this action.", ephemeral=True)
|
||||
await interaction.response.send_message("You can't confirm this action.", ephemeral=True)
|
||||
return
|
||||
confirmed["value"] = True
|
||||
await interaction.response.edit_message(content="✅ Confirmed.", view=None)
|
||||
|
|
@ -25,7 +25,7 @@ class UnmuteCommand(ModerationBase):
|
|||
|
||||
async def no_callback(interaction: discord.Interaction):
|
||||
if interaction.user != ctx.author:
|
||||
await interaction.response.send_message("You can’t cancel this action.", ephemeral=True)
|
||||
await interaction.response.send_message("You can't cancel this action.", ephemeral=True)
|
||||
return
|
||||
confirmed["value"] = False
|
||||
await interaction.response.edit_message(content="❌ Cancelled.", view=None)
|
||||
|
|
@ -35,12 +35,12 @@ class UnmuteCommand(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)
|
||||
|
||||
await ctx.send(f"Are you sure you want to unmute {user.mention}?", view=view)
|
||||
await view.wait()
|
||||
|
||||
if not confirmed["value"]:
|
||||
return
|
||||
|
||||
|
|
@ -51,14 +51,22 @@ class UnmuteCommand(ModerationBase):
|
|||
|
||||
if mute_role in user.roles:
|
||||
await user.remove_roles(mute_role, reason="Unmute issued by command")
|
||||
|
||||
try:
|
||||
await user.send(f"You have been **unmuted** in **{ctx.guild.name}**.")
|
||||
except:
|
||||
await ctx.send("Could not DM the user.")
|
||||
|
||||
# Remove from mutes table (using inherited connection from ModerationBase)
|
||||
self.c.execute("DELETE FROM mutes WHERE user_id = ? AND guild_id = ?",
|
||||
(user.id, ctx.guild.id))
|
||||
self.conn.commit()
|
||||
print(f"✅ Removed mute record for {user}")
|
||||
|
||||
await self.log_infraction(ctx.guild.id, user.id, ctx.author.id, "unmute", "Manual unmute issued")
|
||||
await ctx.send(f"{user.mention} has been unmuted.")
|
||||
else:
|
||||
await ctx.send(f"{user.mention} is not currently muted.")
|
||||
|
||||
async def setup(bot: commands.Bot):
|
||||
await bot.add_cog(UnmuteCommand(bot))
|
||||
await bot.add_cog(UnmuteCommand(bot))
|
||||
|
|
@ -67,8 +67,8 @@ class Rank(commands.Cog):
|
|||
bar = "█" * filled + "░" * (bar_length - filled)
|
||||
|
||||
# estimate messages left
|
||||
min_msgs = math.ceil(needed / 100)
|
||||
max_msgs = math.ceil(needed / 50)
|
||||
min_msgs = math.ceil(math.ceil(needed / 100) / multiplier)
|
||||
max_msgs = math.ceil(math.ceil(needed / 50) / multiplier)
|
||||
|
||||
embed = discord.Embed(
|
||||
description=f"✨ **XP** `{xp:,}` (lv. {level})\n"
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user