""" █▀▀ ▄▀█ █▄▀ █▀▀ █▀ ▀█▀ █░█░█ █ ▀▄▀ █▄▄ █▀█ █░█ ██▄ ▄█ ░█░ ▀▄▀▄▀ █ █░█ Copyleft 2022 t.me/CakesTwix This program is free software; you can redistribute it and/or modify """ __version__ = (1, 4, 5) # requires: psutil py-cpuinfo # meta pic: https://img.icons8.com/external-xnimrodx-lineal-color-xnimrodx/512/000000/external-pc-computer-xnimrodx-lineal-color-xnimrodx.png # meta developer: @cakestwix_mods # scope: inline # scope: hikka_min 1.1.2 # For version info import telethon import aiogram import git import datetime import logging from typing import Union import platform import cpuinfo import psutil from aiogram.utils.markdown import quote_html from os.path import exists from .. import loader, utils logger = logging.getLogger(__name__) # https://www.adamsmith.haus/python/answers/how-to-remove-empty-lines-from-a-string-in-python def remove_empty_lines(string_with_empty_lines): lines = string_with_empty_lines.split("\n") non_empty_lines = [line for line in lines if line.strip() != ""] return "".join(line + "\n" for line in non_empty_lines) backslash = "\n" def get_os_release(): if not exists("/etc/os-release"): return False list_ = [] with open("/etc/os-release") as f: list_.extend(item.split("=") for item in f.readlines()) return {item[0]: item[1].replace(backslash, "").replace('"', "") for item in list_} # https://stackoverflow.com/questions/2756737/check-linux-distribution-name def get_distro(): """ Name of your Linux distro """ if not exists("/etc/issue"): return False with open("/etc/issue") as f: return f.read().split()[0] def progressbar(iteration: int, length: int) -> str: percent = ("{0:." + str(1) + "f}").format(100 * (iteration / float(100))) filledLength = int(length * iteration // 100) return "█" * filledLength + "▒" * (length - filledLength) # From Hikka https://github.com/hikariatama/Hikka/blob/master/hikka/utils.py#L459-L461 def chunks(_list: Union[list, tuple, set], n: int, /) -> list: """Split provided `_list` into chunks of `n`""" return [_list[i : i + n] for i in range(0, len(_list), n)] def bytes2human(n): # http://code.activestate.com/recipes/578019 # >>> bytes2human(10000) # '9.8K' # >>> bytes2human(100001221) # '95.4M' symbols = ("K", "M", "G", "T", "P", "E", "Z", "Y") prefix = {s: 1 << (i + 1) * 10 for i, s in enumerate(symbols)} for s in reversed(symbols): if n >= prefix[s]: value = float(n) / prefix[s] return "%.1f%s" % (value, s) return "%sB" % n @loader.tds class InlineSystemInfoMod(loader.Module): """🖥 Get detailed information about your server""" strings = { "name": "🖥 InlineSystemInfo", } AddressFamily = { 2: "IPv4", 10: "IPv6", 28: "IPv6", 17: "Link", 18: "Link", } def menu_keyboard(self) -> list: keyboard = [ [ { "text": "😼 General", "callback": self.change_stuff, "args": ("General",), } ], ] keyboard.extend( iter( chunks( [ { "text": "🧠 CPU", "callback": self.change_stuff, "args": ("CPU",), }, { "text": "🐧 Linux", "callback": self.change_stuff, "args": ("Linux",), }, { "text": "🗄 Memory", "callback": self.change_stuff, "args": ("Memory",), }, { "text": "🌐 Network Address", "callback": self.change_stuff, "args": ("Network Address",), }, { "text": "🌐 Network Stats", "callback": self.change_stuff, "args": ("Network Stats",), }, { "text": "💽 Disk", "callback": self.change_stuff, "args": ("Disk",), }, { "text": "🌡 Sensors", "callback": self.change_stuff, "args": ("Sensors",), }, { "text": "🐍 Python", "callback": self.change_stuff, "args": ("Python",), } ], 3, ) ) ) keyboard.append([{"text": "🚫 Close", "callback": self.inline__close}]) try: return keyboard[3].remove([]) except ValueError: return keyboard def general_info(self): string = "😼 System Info\n" string += f" ├──CPU Name: {self.cpu_info.get('brand_raw', 'Undetermined.')} {self.cpu_count_logic}/{self.cpu_count} ({self.cpu_info['arch_string_raw']})\n" string += f" ├──RAM: {progressbar(self.virtual_memory.percent, 10)} ({bytes2human(self.virtual_memory.used)}/{bytes2human(self.virtual_memory.total)})\n" string += f" └──Swap: {progressbar(self.swap_memory.percent, 10)} ({bytes2human(self.swap_memory.used)}/{bytes2human(self.swap_memory.total)})\n\n" string += "🐧 Linux Info\n" string += f" ├──Name: {get_distro()}\n" string += f" └──Kernel: {platform.release()}\n\n" if hasattr(self, "disk_partitions"): for disk_root in psutil.disk_partitions("/"): if disk_root.mountpoint == '/': disk_usage = psutil.disk_usage(disk_root.mountpoint) string += "💽 Disk Info (/)\n" string += f" └──{disk_root.device}\n" string += f" ├── Mount {disk_root.mountpoint}\n" string += f" ├── FS {disk_root.fstype}\n" string += f" ├── Disk Usage {disk_usage.percent}% ({bytes2human(disk_usage.used)}/{bytes2human(disk_usage.total)})\n" string += f" │ └──{progressbar(disk_usage.percent, 10)}\n" string += f" └── Options {disk_root.opts}\n\n" return string def cpu_string(self): string = "🧠 CPU Info\n" string += f"⦁ Name: {self.cpu_info.get('brand_raw', 'Undetermined.')} ({self.cpu_info['arch_string_raw']})\n" string += f"⦁ Count: {self.cpu_count_logic} ({self.cpu_count})\n" string += ( f"⦁ Freq: {self.cpu_freq[0]} (max: {self.cpu_freq[2]} / min: {self.cpu_freq[1]})\n" if hasattr(self, "cpu_freq") and self.cpu_freq else "" ) string += f"⦁ Flags: {' '.join(self.cpu_info.get('flags', 'No flags'))}\n" string += ( f"⦁ Load avg: {self.loadavg[0]} {self.loadavg[1]} {self.loadavg[2]}\n" if hasattr(self, "loadavg") else "" ) return string def disks_string(self): if not hasattr(self, "disk_partitions"): return None string = "💽 Disk Info\n" for disk in self.disk_partitions: disk_usage = psutil.disk_usage(disk.mountpoint) string += f"{disk.device}\n" string += f"├── Mount {disk.mountpoint}\n" string += f"├── FS {disk.fstype}\n" string += f"├── Disk Usage {disk_usage.percent}% ({bytes2human(disk_usage.used)}/{bytes2human(disk_usage.total)})\n" string += f"│ └──{progressbar(disk_usage.percent, 10)}\n" string += f"└── Options {disk.opts}\n\n" return string def network_addr_string(self): if not hasattr(self, "net_if_addrs"): return None string = "🌐 Network Info\n" string += "Address:\n" for interf in self.net_if_addrs: interface = self.net_if_addrs[interf] string += f"{interf}\n" for addr in interface: attr = [ a for a in dir(psutil.net_if_addrs()[interf][0]) if not a.startswith("__") and not a.startswith("_") and not callable(getattr(psutil.net_if_addrs()[interf][0], a)) ] string += f"{self.AddressFamily[getattr(addr, 'family')]}\n" for item in attr[:-1]: string += f"├── {item}: {getattr(addr, item)}\n" string += f"└── {attr[-1]}: {getattr(addr, attr[-1])}\n" string += "\n" return string def memory_string(self): string = "🗄 Memory Info\n" string += f"RAM: {progressbar(self.virtual_memory.percent, 10)} ({bytes2human(self.virtual_memory.used)}/{bytes2human(self.virtual_memory.total)})\n" string += f"Swap: {progressbar(self.swap_memory.percent, 10)} ({bytes2human(self.swap_memory.used)}/{bytes2human(self.swap_memory.total)})\n" return string def sensors_string(self): string = None if hasattr(self, "sensors_temperatures"): string = "🌡 Sensors Info\n" + "Temperature:\n" for sensor_name in self.sensors_temperatures: sensor = self.sensors_temperatures[sensor_name] string += f"{sensor_name}\n" for sensor_info in sensor: attr = [ a for a in dir(sensor_info) if not a.startswith("__") and not a.startswith("_") and not callable(getattr(sensor_info, a)) ] for item in attr[:-1]: string += f"├── {item}: {getattr(sensor_info, item)}\n" string += f"└── {attr[-1]}: {getattr(sensor_info, attr[-1])}\n" string += "\n" if hasattr(self, "sensors_fans"): string += "Fans:" for sensor_name in self.sensors_fans: sensor = self.sensors_fans[sensor_name] string += f"{sensor_name}\n" for sensor_info in sensor: attr = [ a for a in dir(sensor_info) if not a.startswith("__") and not a.startswith("_") and not callable(getattr(sensor_info, a)) ] for item in attr[:-1]: string += f"├── {item}: {getattr(sensor_info, item)}\n" string += f"└── {attr[-1]}: {getattr(sensor_info, attr[-1])}\n" return string def network_stats_string(self): if not hasattr(self, "net_if_stats"): return None string = "🌐 Network Info\n" + "Stats:\n" for interf in self.net_if_stats: interface = self.net_if_stats[interf] string += f"{interf}\n" string += f"└── {quote_html(interface)}\n\n" return string def linux_string(self): os_release = get_os_release() string = f"""🐧 Linux Info Name: {get_distro()} Kernel: {platform.release()} Hostname: {platform.node()} {f'glibc ver: {platform.glibc()[1]}' if hasattr(platform, 'glibc') else ''} Boot time: {self.boot_time if hasattr(self, "boot_time") else "Termux moment"} """ if os_release: string += f"""\n /etc/os-releases Info: Pretty Name: {os_release["PRETTY_NAME"]} Name: {os_release["NAME"]} Version: {os_release.get("VERSION", "Not available")} Documentation: {os_release.get("DOCUMENTATION_URL", "Not available")} Support: {os_release.get("SUPPORT_URL", "Not available")} Bug Report: {os_release["BUG_REPORT_URL"]} """ return remove_empty_lines(string) def python_string(self): string = "🐍 Python Info\n" string += f" ├──Version: {platform.python_version()}\n" string += f" ├──Version (More details): {cpuinfo.get_cpu_info()['python_version']}\n" string += f" └──Python Packages version\n" string += f" ├──Telethon: {telethon.__version__}\n" string += f" ├──AIOgram: {aiogram.__version__}\n" string += f" ├──Cpuinfo: {cpuinfo.get_cpu_info()['cpuinfo_version_string']}\n" string += f" ├──psutil: {psutil.__version__}\n" string += f" └──git: {git.__version__}\n" return string def __init__(self): # CPU stuff self.cpu_count_logic = psutil.cpu_count() self.cpu_count = psutil.cpu_count(logical=False) try: self.cpu_freq = psutil.cpu_freq() except FileNotFoundError: pass self.cpu_info = cpuinfo.get_cpu_info() # Network try: self.net_if_addrs = psutil.net_if_addrs() self.net_if_stats = psutil.net_if_stats() except PermissionError: pass def update_data(self): # Memory self.virtual_memory = psutil.virtual_memory() self.swap_memory = psutil.swap_memory() # Sensors if hasattr(psutil, "sensors_temperatures"): self.sensors_temperatures = psutil.sensors_temperatures() if hasattr(psutil, "sensors_fans"): self.sensors_fans = psutil.sensors_fans() # self.sensors_battery = psutil.sensors_battery() # CPU stuff self.cpu_percent = psutil.cpu_percent(interval=None) # Linux stuff self.loadavg = psutil.getloadavg() # Disks try: self.disk_partitions = psutil.disk_partitions() except PermissionError: pass # Other self.boot_time = datetime.datetime.fromtimestamp(psutil.boot_time()).strftime( "%Y-%m-%d %H:%M:%S" ) # Generate string self.info_string = { "General": self.general_info(), "CPU": self.cpu_string(), "Disk": self.disks_string(), "Network Address": self.network_addr_string(), "Network Stats": self.network_stats_string(), "Memory": self.memory_string(), "Sensors": self.sensors_string(), "Linux": self.linux_string(), "Python": self.python_string(), } async def client_ready(self, client, db) -> None: if utils.get_platform_name() == '🕶 Termux': raise loader.LoadError("No Termux support. Change your host") async def systeminfocmd(self, message): """Get information about your server""" await utils.run_sync(self.update_data) await self.inline.form( text=self.general_info(), message=message, reply_markup=self.menu_keyboard(), ) # Inline callback async def change_stuff(self, call, stuff): if self.info_string[stuff] != None: await call.edit(text=self.info_string[stuff], reply_markup=self.menu_keyboard()) else: await call.answer("No data :(") async def inline__close(self, call) -> None: await call.delete()