Red-DiscordBot/redbot/core/_debuginfo.py

171 lines
5.5 KiB
Python

from __future__ import annotations
import getpass
import os
import platform
import sys
from typing import Optional
import discord
import pip
import psutil
from redbot import __version__
from redbot.core import data_manager
from redbot.core.bot import Red
from redbot.core.utils.chat_formatting import box
def noop_box(text: str, **kwargs) -> str:
return text
def _datasize(num: int):
for unit in ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB"]:
if abs(num) < 1024.0:
return "{0:.1f}{1}".format(num, unit)
num /= 1024.0
return "{0:.1f}{1}".format(num, "YB")
class DebugInfoSection:
def __init__(self, section_name: str, *section_parts: str) -> None:
self.section_name = section_name
self.section_parts = section_parts
def get_command_text(self) -> str:
parts = [box(f"## {self.section_name}:", lang="md")]
for part in self.section_parts:
parts.append(box(part))
return "".join(parts)
def get_cli_text(self) -> str:
parts = [f"\x1b[32m## {self.section_name}:\x1b[0m"]
for part in self.section_parts:
parts.append(part)
return "\n".join(parts)
class DebugInfo:
def __init__(self, bot: Optional[Red] = None) -> None:
self.bot = bot
async def get_text(self) -> str:
if self.bot is None:
return await self.get_cli_text()
else:
return await self.get_command_text()
async def get_cli_text(self) -> str:
parts = ["\x1b[31m# Debug Info for Red:\x1b[0m"]
for section in (
self._get_system_metadata_section(),
self._get_os_variables_section(),
await self._get_red_vars_section(),
):
parts.append("")
parts.append(section.get_cli_text())
return "\n".join(parts)
async def get_command_text(self) -> str:
parts = [box("# Debug Info for Red:", lang="md")]
for section in (
self._get_system_metadata_section(),
self._get_os_variables_section(),
await self._get_red_vars_section(),
):
parts.append("\n")
parts.append(section.get_command_text())
return "".join(parts)
def _get_system_metadata_section(self) -> DebugInfoSection:
memory_ram = psutil.virtual_memory()
ram_string = "{used}/{total} ({percent}%)".format(
used=_datasize(memory_ram.used),
total=_datasize(memory_ram.total),
percent=memory_ram.percent,
)
return DebugInfoSection(
"System Metadata",
f"CPU Cores: {psutil.cpu_count()} ({platform.machine()})\nRAM: {ram_string}",
)
def _get_os_variables_section(self) -> DebugInfoSection:
IS_WINDOWS = os.name == "nt"
IS_MAC = sys.platform == "darwin"
IS_LINUX = sys.platform == "linux"
python_version = ".".join(map(str, sys.version_info[:3]))
pyver = f"{python_version} ({platform.architecture()[0]})"
pipver = pip.__version__
redver = __version__
dpy_version = discord.__version__
if IS_WINDOWS:
os_info = platform.uname()
osver = f"{os_info.system} {os_info.release} (version {os_info.version})"
elif IS_MAC:
os_info = platform.mac_ver()
osver = f"Mac OSX {os_info[0]} {os_info[2]}"
elif IS_LINUX:
import distro
osver = f"{distro.name()} {distro.version()}".strip()
else:
osver = "Could not parse OS, report this on Github."
user_who_ran = getpass.getuser()
resp_os = f"OS version: {osver}\nUser: {user_who_ran}\n" # Ran where off to?!
resp_py_metadata = (
f"Python executable: {sys.executable}\n"
f"Python version: {pyver}\n"
f"Pip version: {pipver}\n"
)
resp_red_metadata = f"Red version: {redver}\nDiscord.py version: {dpy_version}"
return DebugInfoSection(
"OS variables",
resp_os,
resp_py_metadata,
resp_red_metadata,
)
async def _get_red_vars_section(self) -> DebugInfoSection:
if data_manager.instance_name is None:
return DebugInfoSection(
"Red variables",
f"Metadata file: {data_manager.config_file}",
)
parts = [f"Instance name: {data_manager.instance_name}"]
if self.bot is not None:
owners = []
for uid in self.bot.owner_ids:
try:
u = await self.bot.get_or_fetch_user(uid)
owners.append(f"{u.id} ({u})")
except discord.HTTPException:
owners.append(f"{uid} (Unresolvable)")
owners_string = ", ".join(owners) or "None"
parts.append(f"Owner(s): {', '.join(owners) or 'None'}")
if self.bot is not None:
disabled_intents = (
", ".join(
intent_name.replace("_", " ").title()
for intent_name, enabled in self.bot.intents
if not enabled
)
or "None"
)
parts.append(f"Disabled intents: {disabled_intents}")
parts.append(f"Storage type: {data_manager.storage_type()}")
parts.append(f"Data path: {data_manager.basic_config['DATA_PATH']}")
parts.append(f"Metadata file: {data_manager.config_file}")
return DebugInfoSection(
"Red variables",
"\n".join(parts),
)