From f0fc33050e6c5265fae375eb11709c64bc2ece6c Mon Sep 17 00:00:00 2001 From: Lilac-Rose Date: Mon, 27 Oct 2025 19:02:18 +0100 Subject: [PATCH] Made it so stats communicates with the website and inf search will say no infractions if there a no infractions --- commands/stats.py | 118 +++++++++++++++++++++++++++++++++++++-- moderation/infraction.py | 5 ++ 2 files changed, 117 insertions(+), 6 deletions(-) diff --git a/commands/stats.py b/commands/stats.py index 62c5861..44a7634 100644 --- a/commands/stats.py +++ b/commands/stats.py @@ -4,14 +4,19 @@ from discord import app_commands from datetime import datetime, timezone import sqlite3 import os +import aiohttp +from aiohttp import web +import json +import asyncio +import aiofiles BASE_DIR = os.path.dirname(__file__) DB_PATH = os.path.join(BASE_DIR, "stats.db") - class Stats(commands.Cog): def __init__(self, bot): self.bot = bot + self.guild_id = 876772600704020530 # Your guild ID # Bot start time if not hasattr(self.bot, "start_time"): @@ -19,6 +24,13 @@ class Stats(commands.Cog): # Initialize the database self.init_db() + + # Stats file path (same directory as stats.py) + self.stats_file = os.path.join(BASE_DIR, "bot_stats.json") + + # Start background tasks + self.bot.loop.create_task(self.update_stats_file()) + self.bot.loop.create_task(self.start_mini_api()) # ----------------------------- # Database methods @@ -51,15 +63,109 @@ class Stats(commands.Cog): return total # ----------------------------- - # Prefix command listener + # Stats file management + # ----------------------------- + async def cleanup_old_stats_files(self): + """Remove any old stats files to prevent accumulation""" + try: + stats_dir = BASE_DIR + for filename in os.listdir(stats_dir): + if filename.startswith("bot_stats") and filename.endswith(".json"): + file_path = os.path.join(stats_dir, filename) + # Keep only the current stats file + if file_path != self.stats_file: + os.remove(file_path) + print(f"Removed old stats file: {filename}") + except Exception as e: + print(f"Error cleaning up old stats files: {e}") + + async def update_stats_file(self): + """Update stats JSON file every 30 seconds""" + await self.bot.wait_until_ready() + + # Clean up old files on startup + await self.cleanup_old_stats_files() + + while not self.bot.is_closed(): + try: + stats = await self.gather_stats() + async with aiofiles.open(self.stats_file, 'w') as f: + await f.write(json.dumps(stats, indent=2, default=str)) + print(f"Updated stats file at {datetime.now()}") + except Exception as e: + print(f"Error updating stats file: {e}") + + await asyncio.sleep(30) # Update every 30 seconds + + async def gather_stats(self): + """Gather real-time statistics""" + guild = self.bot.get_guild(self.guild_id) + + if not guild: + return {'error': 'Guild not found', 'guild_id': self.guild_id} + + # Calculate uptime + uptime = datetime.now(timezone.utc) - self.bot.start_time + hours, remainder = divmod(int(uptime.total_seconds()), 3600) + minutes, seconds = divmod(remainder, 60) + + # Count channels + text_channels = len([c for c in guild.channels if isinstance(c, discord.TextChannel)]) + voice_channels = len([c for c in guild.channels if isinstance(c, discord.VoiceChannel)]) + categories = len([c for c in guild.channels if isinstance(c, discord.CategoryChannel)]) + + return { + 'server': { + 'memberCount': guild.member_count, + 'createdDate': guild.created_at.strftime('%b %d, %Y'), + 'textChannels': text_channels, + 'voiceChannels': voice_channels, + 'categories': categories, + 'boostLevel': guild.premium_tier, + 'boostCount': guild.premium_subscription_count, + 'iconUrl': str(guild.icon.url) if guild.icon else None, + 'bannerUrl': str(guild.banner.url) if guild.banner else None, + 'guildName': guild.name + }, + 'bot': { + 'uptime': f"{hours}h {minutes}m {seconds}s", + 'totalCommands': self.get_usage(), + 'serverCount': len(self.bot.guilds), + 'botUsers': len(self.bot.users), + 'latency': round(self.bot.latency * 1000), + 'developer': 'Lilac Aria Rose', + 'lastUpdated': datetime.now(timezone.utc).isoformat() + } + } + + # ----------------------------- + # Mini API Server + # ----------------------------- + async def start_mini_api(self): + """Start a simple HTTP server for real-time stats""" + await self.bot.wait_until_ready() + + app = web.Application() + + async def handle_stats(request): + stats = await self.gather_stats() + return web.json_response(stats) + + app.router.add_get('/stats', handle_stats) + + runner = web.AppRunner(app) + await runner.setup() + site = web.TCPSite(runner, 'localhost', 8765) + await site.start() + print("Bot stats API running on port 8765") + + # ----------------------------- + # Discord event listeners # ----------------------------- @commands.Cog.listener() async def on_command(self, ctx): self.increment_usage() - # ----------------------------- - # Slash command listener - # ----------------------------- @commands.Cog.listener() async def on_interaction(self, interaction: discord.Interaction): # Only count application commands (slash commands) @@ -67,7 +173,7 @@ class Stats(commands.Cog): self.increment_usage() # ----------------------------- - # Slash command + # Discord slash command # ----------------------------- @app_commands.command(name="stats", description="Shows server and bot statistics") async def stats(self, interaction: discord.Interaction): diff --git a/moderation/infraction.py b/moderation/infraction.py index a5b9120..e842a32 100644 --- a/moderation/infraction.py +++ b/moderation/infraction.py @@ -33,6 +33,11 @@ class InfractionCommand(ModerationBase): await ctx.send(f"Database error: {e}") return + if not results: + await ctx.send("This user has no infractions.") + return + + elif action == "list": try: self.c.execute("""