Refactor the system status view to enhance readability and organization. - Introduce `_create_aligned_block` helper for consistent, aligned text formatting. - Consolidate bot, system, and hardware information into new aligned blocks. - Remove redundant separators and improve overall view structure. - Adjust progress bar length for better fit. - Optimize user count gathering using a set for unique users. - Improve Windows version detection and Linux motherboard info fallback.
275 lines
9.9 KiB
Python
275 lines
9.9 KiB
Python
import discord
|
|
from discord.ext import commands
|
|
from discord import app_commands, ui
|
|
import time
|
|
import psutil
|
|
import platform
|
|
import GPUtil
|
|
import distro
|
|
|
|
# Import wmi for Windows motherboard info
|
|
try:
|
|
import wmi
|
|
WMI_AVAILABLE = True
|
|
except ImportError:
|
|
WMI_AVAILABLE = False
|
|
|
|
|
|
def create_progress_bar(value: float, total: float, length: int = 10) -> str:
|
|
"""Creates a text-based progress bar."""
|
|
if total == 0:
|
|
percentage = 0
|
|
else:
|
|
percentage = value / total
|
|
|
|
filled_length = int(length * percentage)
|
|
bar = '▓' * filled_length + '░' * (length - filled_length)
|
|
return f"[{bar}] {percentage:.1%}"
|
|
|
|
|
|
class SystemStatusView(ui.LayoutView):
|
|
"""
|
|
A view that displays system and bot statistics in a visually appealing way.
|
|
It uses components v2 for a modern look.
|
|
"""
|
|
def __init__(
|
|
self,
|
|
bot_user: discord.User,
|
|
guild_count: int,
|
|
user_count: int,
|
|
os_info: str,
|
|
distro_info: str,
|
|
hostname: str,
|
|
uptime: str,
|
|
motherboard_info: str,
|
|
cpu_name: str,
|
|
cpu_usage: float,
|
|
ram_used: int,
|
|
ram_total: int,
|
|
gpu_info: str,
|
|
requester: discord.User,
|
|
) -> None:
|
|
super().__init__(timeout=None)
|
|
|
|
# --- Store all data for the view ---
|
|
self.bot_user = bot_user
|
|
self.guild_count = guild_count
|
|
self.user_count = user_count
|
|
self.os_info = os_info
|
|
self.distro_info = distro_info
|
|
self.hostname = hostname
|
|
self.uptime = uptime
|
|
self.motherboard_info = motherboard_info
|
|
self.cpu_name = cpu_name
|
|
self.cpu_usage = cpu_usage
|
|
self.ram_used = ram_used
|
|
self.ram_total = ram_total
|
|
self.gpu_info = gpu_info
|
|
self.requester = requester
|
|
|
|
# --- Build the UI ---
|
|
self._build_ui()
|
|
|
|
def _create_aligned_block(self, data: dict) -> str:
|
|
"""Creates a neatly aligned text block inside a markdown code block."""
|
|
max_key_len = max(len(k) for k in data.keys())
|
|
lines = []
|
|
for key, value in data.items():
|
|
lines.append(f"{key.ljust(max_key_len)} : {value}")
|
|
return "```\n" + "\n".join(lines) + "\n```"
|
|
|
|
def _build_ui(self):
|
|
"""Constructs the UI elements of the view."""
|
|
# Main container with Discord's "Blurple" color
|
|
container = ui.Container(accent_colour=discord.Color.from_rgb(88, 101, 242))
|
|
|
|
# --- Header ---
|
|
header = ui.Section(accessory=ui.Thumbnail(media=self.bot_user.display_avatar.url))
|
|
header.add_item(ui.TextDisplay("**📊 System Status**"))
|
|
container.add_item(header)
|
|
|
|
# --- Bot & System Info ---
|
|
self._add_bot_system_info(container)
|
|
|
|
# --- Hardware Info ---
|
|
self._add_hardware_info(container)
|
|
|
|
# --- Footer ---
|
|
self._add_footer(container)
|
|
|
|
self.add_item(container)
|
|
|
|
def _add_bot_system_info(self, container: ui.Container):
|
|
"""Adds bot and system information fields."""
|
|
container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
|
|
|
bot_data = {
|
|
"Servers": self.guild_count,
|
|
"Users": self.user_count
|
|
}
|
|
container.add_item(ui.TextDisplay("**🤖 Bot Info**" + self._create_aligned_block(bot_data)))
|
|
|
|
system_data = {
|
|
"OS": f"{self.os_info}{self.distro_info}",
|
|
"Hostname": self.hostname,
|
|
"Uptime": self.uptime
|
|
}
|
|
container.add_item(ui.TextDisplay("**🖥️ System Info**" + self._create_aligned_block(system_data)))
|
|
|
|
def _add_hardware_info(self, container: ui.Container):
|
|
"""Adds hardware information with progress bars."""
|
|
container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
|
|
|
ram_usage_text = f"{self.ram_used // (1024**2):,}MB / {self.ram_total // (1024**2):,}MB"
|
|
cpu_bar = create_progress_bar(self.cpu_usage, 100.0)
|
|
ram_bar = create_progress_bar(self.ram_used, self.ram_total)
|
|
|
|
hardware_data = {
|
|
"CPU Usage": cpu_bar,
|
|
"RAM Usage": f"{ram_bar}\n{''.ljust(len('RAM Usage'))} └ {ram_usage_text}",
|
|
"Board": self.motherboard_info,
|
|
"CPU": self.cpu_name,
|
|
"GPU": self.gpu_info
|
|
}
|
|
|
|
container.add_item(ui.TextDisplay("**⚙️ Hardware Info**" + self._create_aligned_block(hardware_data)))
|
|
|
|
|
|
def _add_footer(self, container: ui.Container):
|
|
"""Adds the footer with timestamp and requester info."""
|
|
container.add_item(ui.Separator(spacing=discord.SeparatorSpacing.small))
|
|
timestamp = discord.utils.format_dt(discord.utils.utcnow(), style="R")
|
|
footer_text = f"Updated: {timestamp} | Requested by: {self.requester.display_name}"
|
|
container.add_item(ui.TextDisplay(f"_{footer_text}_"))
|
|
|
|
|
|
class SystemCheckCog(commands.Cog):
|
|
def __init__(self, bot):
|
|
self.bot = bot
|
|
|
|
async def _build_system_check_view(self, context_or_interaction) -> SystemStatusView:
|
|
"""Gathers all system data and returns the constructed view."""
|
|
# Bot information
|
|
guild_count = len(self.bot.guilds)
|
|
# A more efficient way to get unique users, avoiding large member lists in memory
|
|
user_ids = {member.id for guild in self.bot.guilds for member in guild.members if not member.bot}
|
|
user_count = len(user_ids)
|
|
|
|
# System information
|
|
system = platform.system()
|
|
os_info = f"{system} {platform.release()}"
|
|
hostname = platform.node()
|
|
distro_info_str = ""
|
|
|
|
if system == "Linux":
|
|
try:
|
|
distro_name = distro.name(pretty=True)
|
|
distro_info_str = f" ({distro_name})"
|
|
except Exception:
|
|
distro_info_str = "" # Fail silently
|
|
elif system == "Windows":
|
|
try:
|
|
# Use a more reliable way to get Windows version
|
|
win_ver = platform.win32_ver()
|
|
os_info = f"Windows {win_ver[0]} {win_ver[2]}"
|
|
except Exception:
|
|
pass # Fail silently
|
|
|
|
uptime_seconds = time.time() - psutil.boot_time()
|
|
days, rem = divmod(uptime_seconds, 86400)
|
|
hours, rem = divmod(rem, 3600)
|
|
minutes, _ = divmod(rem, 60)
|
|
uptime_str = f"{int(days)}d {int(hours)}h {int(minutes)}m"
|
|
|
|
# Hardware information
|
|
cpu_usage = psutil.cpu_percent(interval=0.1)
|
|
|
|
try:
|
|
cpu_name_base = "N/A"
|
|
if system == "Linux":
|
|
with open("/proc/cpuinfo") as f:
|
|
for line in f:
|
|
if line.startswith("model name"):
|
|
cpu_name_base = line.split(":")[1].strip()
|
|
break
|
|
else: # Windows or fallback
|
|
cpu_name_base = platform.processor() or "N/A"
|
|
|
|
physical_cores = psutil.cpu_count(logical=False)
|
|
total_threads = psutil.cpu_count(logical=True)
|
|
cpu_name = f"{cpu_name_base} ({physical_cores}C/{total_threads}T)"
|
|
except Exception:
|
|
cpu_name = "N/A"
|
|
|
|
motherboard_info = self._get_motherboard_info()
|
|
memory = psutil.virtual_memory()
|
|
|
|
# GPU Information
|
|
try:
|
|
gpus = GPUtil.getGPUs()
|
|
if gpus:
|
|
# Format multi-GPU info on new lines for readability
|
|
gpu_info_lines = [f"{gpu.name} ({gpu.load*100:.1f}% Load)" for gpu in gpus]
|
|
gpu_info = "\n".join(gpu_info_lines)
|
|
else:
|
|
gpu_info = "No dedicated GPU detected"
|
|
except Exception:
|
|
gpu_info = "N/A"
|
|
|
|
# Determine user based on context type
|
|
user = context_or_interaction.author if isinstance(context_or_interaction, commands.Context) else context_or_interaction.user
|
|
|
|
return SystemStatusView(
|
|
bot_user=self.bot.user,
|
|
guild_count=guild_count,
|
|
user_count=user_count,
|
|
os_info=os_info,
|
|
distro_info=distro_info_str,
|
|
hostname=hostname,
|
|
uptime=uptime_str,
|
|
motherboard_info=motherboard_info,
|
|
cpu_name=cpu_name,
|
|
cpu_usage=cpu_usage,
|
|
ram_used=memory.used,
|
|
ram_total=memory.total,
|
|
gpu_info=gpu_info,
|
|
requester=user,
|
|
)
|
|
|
|
def _get_motherboard_info(self) -> str:
|
|
"""Get motherboard information based on the operating system."""
|
|
system = platform.system()
|
|
try:
|
|
if system == "Windows" and WMI_AVAILABLE:
|
|
w = wmi.WMI()
|
|
board = w.Win32_BaseBoard()[0]
|
|
return f"{board.Manufacturer} {board.Product}"
|
|
elif system == "Linux":
|
|
# Check for product_name first, then fallback to board_name
|
|
try:
|
|
with open("/sys/devices/virtual/dmi/id/product_name", "r") as f:
|
|
return f.read().strip()
|
|
except FileNotFoundError:
|
|
with open("/sys/devices/virtual/dmi/id/board_name", "r") as f:
|
|
return f.read().strip()
|
|
return "N/A"
|
|
except Exception:
|
|
return "N/A"
|
|
|
|
@commands.command(name="systemcheck", aliases=["status"])
|
|
async def system_check(self, ctx: commands.Context):
|
|
"""Check the bot and system status."""
|
|
view = await self._build_system_check_view(ctx)
|
|
await ctx.reply(view=view, mention_author=False)
|
|
|
|
@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."""
|
|
await interaction.response.defer(thinking=True, ephemeral=False)
|
|
view = await self._build_system_check_view(interaction)
|
|
await interaction.followup.send(view=view)
|
|
|
|
|
|
async def setup(bot):
|
|
await bot.add_cog(SystemCheckCog(bot))
|