discordbot/cogs/system_check_cog.py
2025-04-25 14:03:49 -06:00

216 lines
8.5 KiB
Python

import discord
from discord.ext import commands
from discord import app_commands
import time
import psutil
import platform
import GPUtil
from cpuinfo import get_cpu_info
import distro # Ensure this is installed
import subprocess
# Import wmi for Windows motherboard info
try:
import wmi
WMI_AVAILABLE = True
except ImportError:
WMI_AVAILABLE = False
class SystemCheckCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
async def _system_check_logic(self, context_or_interaction):
"""Return detailed bot and system information as a Discord embed."""
# Bot information
bot_user = self.bot.user
guild_count = len(self.bot.guilds)
# Efficiently count unique non-bot members across guilds
user_ids = set()
for guild in self.bot.guilds:
try:
# Fetch members if needed, handle potential exceptions
async for member in guild.fetch_members(limit=None): # Fetch all members
if not member.bot:
user_ids.add(member.id)
except discord.Forbidden:
print(f"Missing permissions to fetch members in guild: {guild.name} ({guild.id})")
except discord.HTTPException as e:
print(f"HTTP error fetching members in guild {guild.name}: {e}")
except Exception as e:
print(f"Unexpected error fetching members in guild {guild.name}: {e}")
user_count = len(user_ids)
# System information
system = platform.system()
os_info = f"{system} {platform.release()}"
hostname = platform.node()
distro_info_str = "" # Renamed variable
if system == "Linux":
try:
# Use distro library for better Linux distribution detection
distro_name = distro.name(pretty=True)
distro_info_str = f"\n**Distro:** {distro_name}"
except ImportError:
distro_info_str = "\n**Distro:** (Install 'distro' package for details)"
except Exception as e:
distro_info_str = f"\n**Distro:** (Error getting info: {e})"
elif system == "Windows":
# Add Windows version details if possible
try:
win_ver = platform.version() # e.g., '10.0.19041'
win_build = platform.win32_ver()[1] # e.g., '19041'
os_info = f"Windows {win_ver} (Build {win_build})"
except Exception as e:
print(f"Could not get detailed Windows version: {e}")
# Keep the basic os_info
uptime_seconds = time.time() - psutil.boot_time()
days, remainder = divmod(uptime_seconds, 86400)
hours, remainder = divmod(remainder, 3600)
minutes, seconds = divmod(remainder, 60)
uptime_str = ""
if days > 0:
uptime_str += f"{int(days)}d "
uptime_str += f"{int(hours):02}:{int(minutes):02}:{int(seconds):02}"
uptime = uptime_str.strip()
# Hardware information
cpu_usage = psutil.cpu_percent(interval=0.5) # Shorter interval might be okay
try:
cpu_info_dict = get_cpu_info() # Renamed variable
cpu_name = cpu_info_dict.get('brand_raw', 'N/A')
except Exception as e:
print(f"Error getting CPU info: {e}")
cpu_name = "N/A"
# Get motherboard information
motherboard_info = self._get_motherboard_info()
memory = psutil.virtual_memory()
ram_usage = f"{memory.used // (1024 ** 2)} MB / {memory.total // (1024 ** 2)} MB ({memory.percent}%)"
# GPU Information (using GPUtil for cross-platform consistency if available)
gpu_info_lines = []
try:
gpus = GPUtil.getGPUs()
if gpus:
for gpu in gpus:
gpu_info_lines.append(
f"{gpu.name} ({gpu.load*100:.1f}% Load, {gpu.memoryUsed:.0f}/{gpu.memoryTotal:.0f} MB VRAM)"
)
gpu_info = "\n".join(gpu_info_lines)
else:
gpu_info = "No dedicated GPU detected by GPUtil."
except ImportError:
gpu_info = "GPUtil library not installed. Cannot get detailed GPU info."
except Exception as e:
print(f"Error getting GPU info via GPUtil: {e}")
gpu_info = f"Error retrieving GPU info: {e}"
# Determine user and avatar URL based on context type
if isinstance(context_or_interaction, commands.Context):
user = context_or_interaction.author
avatar_url = user.display_avatar.url
elif isinstance(context_or_interaction, discord.Interaction):
user = context_or_interaction.user
avatar_url = user.display_avatar.url
else: # Fallback or handle error if needed
user = self.bot.user # Or some default
avatar_url = self.bot.user.display_avatar.url if self.bot.user else None
# Create embed
embed = discord.Embed(title="📊 System Status", color=discord.Color.blue())
if bot_user:
embed.set_thumbnail(url=bot_user.display_avatar.url)
# Bot Info Field
if bot_user:
embed.add_field(
name="🤖 Bot Information",
value=f"**Name:** {bot_user.name}\n"
f"**ID:** {bot_user.id}\n"
f"**Servers:** {guild_count}\n"
f"**Unique Users:** {user_count}",
inline=False
)
else:
embed.add_field(
name="🤖 Bot Information",
value="Bot user information not available.",
inline=False
)
# System Info Field
embed.add_field(
name="🖥️ System Information",
value=f"**OS:** {os_info}{distro_info_str}\n" # Use renamed variable
f"**Hostname:** {hostname}\n"
f"**Uptime:** {uptime}",
inline=False
)
# Hardware Info Field
embed.add_field(
name="⚙️ Hardware Information",
value=f"**Device Model:** {motherboard_info}\n"
f"**CPU:** {cpu_name}\n"
f"**CPU Usage:** {cpu_usage}%\n"
f"**RAM Usage:** {ram_usage}\n"
f"**GPU Info:**\n{gpu_info}",
inline=False
)
if user:
embed.set_footer(text=f"Requested by: {user.display_name}", icon_url=avatar_url)
embed.timestamp = discord.utils.utcnow()
return embed
# --- Prefix Command ---
@commands.command(name="systemcheck")
async def system_check(self, ctx: commands.Context):
"""Check the bot and system status."""
embed = await self._system_check_logic(ctx) # Pass context
await ctx.reply(embed=embed)
# --- Slash Command ---
@app_commands.command(name="systemcheck", description="Check the bot and system status")
async def system_check_slash(self, interaction: discord.Interaction):
"""Slash command version of system check."""
embed = await self._system_check_logic(interaction) # Pass interaction
await interaction.response.send_message(embed=embed)
def _get_motherboard_info(self):
"""Get motherboard information based on the operating system."""
system = platform.system()
try:
if system == "Windows":
if WMI_AVAILABLE:
w = wmi.WMI()
for board in w.Win32_BaseBoard():
return f"{board.Manufacturer} {board.Product}"
return "WMI module not available"
elif system == "Linux":
# Read motherboard product name from sysfs
try:
with open("/sys/devices/virtual/dmi/id/product_name", "r") as f:
product_name = f.read().strip()
return product_name if product_name else "Unknown motherboard"
except FileNotFoundError:
return "/sys/devices/virtual/dmi/id/product_name not found"
except Exception as e:
return f"Error reading motherboard info: {e}"
except Exception as e:
return f"Error: {str(e)}"
else:
return f"Unsupported OS: {system}"
except Exception as e:
print(f"Error getting motherboard info: {e}")
return "Error retrieving motherboard info"
async def setup(bot):
await bot.add_cog(SystemCheckCog(bot))