""" Comprehensive system information – code block with emoji + aligned columns. All blocking calls run in thread pool. """ import logging, platform, os, asyncio, psutil, socket, datetime, subprocess import simplematrixbotlib as botlib from plugins.common import collapsible_summary, html_escape, code_block async def _run_blocking(func, *args, **kwargs): loop = asyncio.get_running_loop() return await loop.run_in_executor(None, lambda: func(*args, **kwargs)) # ---------- Data collectors (unchanged) ---------- def _system_overview(): boot = datetime.datetime.fromtimestamp(psutil.boot_time()) uptime_delta = datetime.datetime.now() - boot uptime_str = str(datetime.timedelta(seconds=int(uptime_delta.total_seconds()))) return { "hostname": socket.gethostname(), "os": f"{platform.system()} {platform.release()}", "architecture": platform.architecture()[0], "machine": platform.machine(), "processor": platform.processor(), "boot_time": boot.strftime("%Y-%m-%d %H:%M:%S"), "uptime": uptime_str, "users": len(psutil.users()) } def _cpu_info(): cpu_freq = psutil.cpu_freq() load = os.getloadavg() if hasattr(os, "getloadavg") else (0,0,0) return { "physical_cores": psutil.cpu_count(logical=False), "logical_cores": psutil.cpu_count(logical=True), "max_freq": f"{cpu_freq.max:.0f} MHz" if cpu_freq else "N/A", "current_freq": f"{cpu_freq.current:.0f} MHz" if cpu_freq else "N/A", "usage": f"{psutil.cpu_percent(interval=1)}%", "load_avg": f"{load[0]:.2f} {load[1]:.2f} {load[2]:.2f}" } def _memory_info(): mem = psutil.virtual_memory() swap = psutil.swap_memory() return { "total_ram": f"{mem.total / (1024**3):.1f} GB", "used_ram": f"{mem.used / (1024**3):.1f} GB", "ram_percent": f"{mem.percent}%", "available_ram": f"{mem.available / (1024**3):.1f} GB", "total_swap": f"{swap.total / (1024**3):.1f} GB" if swap.total > 0 else "N/A", "used_swap": f"{swap.used / (1024**3):.1f} GB" if swap.total > 0 else "N/A", "swap_percent": f"{swap.percent}%" if swap.total > 0 else "N/A" } def _disk_info(): partitions = psutil.disk_partitions() mounted = [] for p in partitions: try: usage = psutil.disk_usage(p.mountpoint) mounted.append({ "mount": p.mountpoint, "used": f"{usage.used / (1024**3):.1f} GB", "total": f"{usage.total / (1024**3):.1f} GB", "percent": usage.percent }) except: pass io = psutil.disk_io_counters() io_read = f"{io.read_bytes / (1024**3):.2f} GB" if io else "0 GB" io_write = f"{io.write_bytes / (1024**3):.2f} GB" if io else "0 GB" return mounted, io_read, io_write def _network_info(): ifaces = psutil.net_if_addrs() io_counters = psutil.net_io_counters(pernic=True) net = [] for name, addrs in ifaces.items(): if name == "lo": continue ip4 = next((a.address for a in addrs if a.family == socket.AF_INET), None) if ip4: stats = io_counters.get(name) sent = f"{stats.bytes_sent / (1024**2):.1f} MB" if stats else "0 MB" recv = f"{stats.bytes_recv / (1024**2):.1f} MB" if stats else "0 MB" net.append((name, ip4, sent, recv)) return net def _top_processes(): procs = [] for p in psutil.process_iter(['pid', 'name', 'cpu_percent', 'memory_percent']): try: procs.append(p.info) except (psutil.NoSuchProcess, psutil.AccessDenied): pass top_cpu = sorted(procs, key=lambda x: x['cpu_percent'] or 0, reverse=True)[:5] return top_cpu, len(procs) def _gpu_info(): info = {} try: res = subprocess.run( ['nvidia-smi', '--query-gpu=name,memory.used,memory.total,temperature.gpu,utilization.gpu', '--format=csv,noheader,nounits'], capture_output=True, text=True ) if res.returncode == 0: gpus = [] for line in res.stdout.strip().split('\n'): parts = [p.strip() for p in line.split(',')] if len(parts) >= 5: gpus.append({ "name": parts[0], "mem_used": f"{parts[1]} MB", "mem_total": f"{parts[2]} MB", "temp": f"{parts[3]}°C", "usage": f"{parts[4]}%" }) if gpus: info["nvidia"] = gpus except: pass try: res = subprocess.run(['lspci'], capture_output=True, text=True) if res.returncode == 0: lines = [l for l in res.stdout.split('\n') if 'VGA' in l or '3D' in l] if lines: info["detected"] = lines[:2] except: pass return info def _docker_info(): try: ver = subprocess.run(['docker', '--version'], capture_output=True, text=True) if ver.returncode != 0: return None ps_res = subprocess.run( ['docker', 'ps', '--format', '{{.Names}}|{{.Status}}'], capture_output=True, text=True ) containers = [] for line in ps_res.stdout.strip().split('\n'): if line: parts = line.split('|') if len(parts) >= 2: containers.append({"name": parts[0], "status": parts[1]}) return containers except: return None def _sensor_info(): temps = psutil.sensors_temperatures() fans = psutil.sensors_fans() battery = psutil.sensors_battery() data = {"temps": [], "fans": [], "battery": None} if temps: for chip, entries in temps.items(): for e in entries[:2]: data["temps"].append(f"{e.label or chip}: {e.current}°C") if fans: for chip, entries in fans.items(): for e in entries[:2]: data["fans"].append(f"{e.label or chip}: {e.current} RPM") if battery: rem = "" if battery.secsleft != psutil.POWER_TIME_UNLIMITED and battery.secsleft > 0: h = battery.secsleft // 3600 m = (battery.secsleft % 3600) // 60 rem = f" ({h}h {m}m left)" plugged = " 🔌" if battery.power_plugged else "" data["battery"] = f"{battery.percent}%{plugged}{rem}" return data # ------------------------------------------------------------------- # Main builder # ------------------------------------------------------------------- async def get_system_info(room, bot): await bot.api.send_text_message(room.room_id, "🔍 Gathering system information...") system = await _run_blocking(_system_overview) cpu = await _run_blocking(_cpu_info) mem = await _run_blocking(_memory_info) disks, io_read, io_write = await _run_blocking(_disk_info) net = await _run_blocking(_network_info) top_procs, total_procs = await _run_blocking(_top_processes) gpu = await _run_blocking(_gpu_info) docker = await _run_blocking(_docker_info) sensors = await _run_blocking(_sensor_info) sections = [] # System Overview sys_rows = [ ("💻", "Hostname", system["hostname"]), ("🖥️", "OS", system["os"]), ("📐", "Architecture", system["architecture"]), ("⚙️", "Machine", system["machine"]), ("🔧", "Processor", system["processor"]), ("⏰", "Uptime", system["uptime"]), ("📅", "Boot Time", system["boot_time"]), ("👥", "Users", str(system["users"])) ] sections.append({"title": "🖥️ System Overview", "rows": sys_rows}) # CPU cpu_rows = [ ("⚡", "CPU Cores", f"{cpu['physical_cores']} physical, {cpu['logical_cores']} logical"), ("📈", "Freq (Max/Cur)", f"{cpu['max_freq']} / {cpu['current_freq']}"), ("📊", "CPU Usage", cpu["usage"]), ("⚖️", "Load Avg", cpu["load_avg"]) ] sections.append({"title": "⚡ CPU", "rows": cpu_rows}) # Memory mem_rows = [ ("🧠", "RAM", f"{mem['used_ram']} / {mem['total_ram']} ({mem['ram_percent']})") ] if mem["total_swap"] != "N/A": mem_rows.append(("💾", "Swap", f"{mem['used_swap']} / {mem['total_swap']} ({mem['swap_percent']})")) sections.append({"title": "🧠 Memory", "rows": mem_rows}) # Storage disk_rows = [] for d in disks[:5]: disk_rows.append(("💽", d['mount'], f"{d['used']} / {d['total']} ({d['percent']}%)")) disk_rows.append(("📀", "Disk I/O", f"Read {io_read} / Write {io_write}")) sections.append({"title": "💾 Storage", "rows": disk_rows}) # Network net_rows = [] if net: for idx, (name, ip, sent, recv) in enumerate(net[:3]): emoji = "🌐" if idx == 0 else "" label = "Network" if idx == 0 else "" net_rows.append((emoji, label, f"{name} - {ip} | ↓{recv} ↑{sent}")) else: net_rows.append(("🌐", "Network", "No active interfaces")) sections.append({"title": "🌐 Network", "rows": net_rows}) # GPU gpu_rows = [] if "nvidia" in gpu: for g in gpu["nvidia"]: gpu_rows.append(("🎮", "GPU", f"{g['name']} | {g['mem_used']}/{g['mem_total']} | {g['temp']} | {g['usage']} util")) elif "detected" in gpu: for line in gpu["detected"]: gpu_rows.append(("🎮", "GPU", line)) else: gpu_rows.append(("🎮", "GPU", "No dedicated GPU detected")) sections.append({"title": "🎮 GPU", "rows": gpu_rows}) # Processes proc_rows = [("🔄", "Processes", f"Total: {total_procs}")] for p in top_procs: name = p.get('name', '?') cpu_p = p.get('cpu_percent') or 0 mem_p = p.get('memory_percent') or 0 proc_rows.append(("", "", f"{name} - CPU {cpu_p:.1f}% / RAM {mem_p:.1f}%")) sections.append({"title": "🔄 Top Processes", "rows": proc_rows}) # Docker docker_rows = [] if docker is not None: if docker: for c in docker[:5]: docker_rows.append(("🐳", "Docker", f"{c['name']} - {c['status']}")) else: docker_rows.append(("🐳", "Docker", "No containers running")) else: docker_rows.append(("🐳", "Docker", "Docker not available")) sections.append({"title": "🐳 Docker", "rows": docker_rows}) # Sensors sensor_rows = [] if sensors["temps"]: sensor_rows.append(("🌡️", "Temperature", ", ".join(sensors["temps"]))) if sensors["fans"]: sensor_rows.append(("🌀", "Fans", ", ".join(sensors["fans"]))) if sensors["battery"]: sensor_rows.append(("🔋", "Battery", sensors["battery"])) if sensor_rows: sections.append({"title": "🌡️ Sensors", "rows": sensor_rows}) block = code_block(f"💻 System Info: {system['hostname']}", sections) output = collapsible_summary(f"💻 System Info – {html_escape(system['hostname'])}", block) await bot.api.send_markdown_message(room.room_id, output) logging.info("Sent system information") async def handle_command(room, message, bot, prefix, config): match = botlib.MessageMatch(room, message, bot, prefix) if match.is_not_from_this_bot() and match.prefix() and match.command("sysinfo"): if match.args() and match.args()[0].lower() == 'help': usage = """ 💻 System Information !sysinfo – display comprehensive system info in a clean code block. """ await bot.api.send_markdown_message(room.room_id, usage) return await get_system_info(room, bot) # --------------------------------------------------------------------------- # Plugin Metadata # --------------------------------------------------------------------------- __version__ = "1.3.1" __author__ = "Funguy Bot" __description__ = "System information plugin" __help__ = """
!sysinfo – System information

Displays CPU, RAM, storage, network, GPU, sensors, top processes, and more in a clean, aligned code block.

"""