#!/usr/bin/env python3 from mcstatus import JavaServer from os.path import join as pathjoin from bottle import route, run, static_file, template from random import random, choice from mcrcon import MCRcon from os import listdir, sysconf import minecraft_data import configparser import platform import requests import hashlib import base64 import json import re # echo -n "["; ls | xargs -I{} echo -n \"{}\",; echo "]" BG_IMAGES = listdir("./static/img/background") @route("/") def index(): print(server_details()) players = currentPlayerData() return template("html/index.html", **{"player_count": players["count"]["current"] , "max_players": players["count"]["max"], "news": news(), "donations": donations(), "server_details": server_details(), "map_url": CONFIG["external"]["map"], "map_download_url": CONFIG["external"]["map_download"]}) @route("/getPlayerSkins") def getPlayerSkins(): players = currentPlayerData() skins = [] for p in players["players"]: hash = fetchSkin(p["uuid"]) skins.append(hash) return {"skins": skins} @route("/img/bg.png") def random_bg_image(): bg_file = choice(BG_IMAGES) print(bg_file) response = static_file(bg_file, root="static/img/background") response.set_header("Cache-Control", "no-cache") response.set_header("Cache-Control", "no-store") response.set_header("Pragma-Directive", "no-cache") response.set_header("Cache-Directive", "no-cache") response.set_header("Pragma", "no-cache") response.set_header("Expires", "0") return response @route("/") def callback(path): return static_file(path, root="static") def currentPlayerData(): status = server_status() player = {} player["count"] = {} player["count"]["current"] = status.players.online player["count"]["max"] = status.players.max player["players"] = [] try: for p in status.players.sample: player["players"].append({"name": p.name, "uuid": p.id}) except TypeError: # If nobody is online, status.players.sample is None pass return player def fetchSkin(uuid): profile = requests.get("https://sessionserver.mojang.com/session/minecraft/profile/{}".format(uuid)).json() properties = profile.get("properties") skin = list(filter(lambda x: x.get("name", "") == "textures", properties))[0] skin_json = json.loads(base64.b64decode(skin.get("value"))) skin_url = skin_json.get("textures").get("SKIN").get("url") skin_req = requests.get(skin_url) player_hash = hashlib.sha256(uuid.encode()).hexdigest() with open('./static/img/skins/{}'.format(player_hash), 'wb') as f: f.write(skin_req.content) return player_hash def parseConfig(): config = configparser.ConfigParser() config.read("config.ini") return config def donations(): with open("donations.txt", "r") as donation_file: donations = donation_file.read() return donations def news(): with open("news.txt", "r") as news_file: news = news_file.read() return news def hardware_info(): processor = get_cpuinfo() mem_bytes = sysconf('SC_PAGE_SIZE') * sysconf('SC_PHYS_PAGES') mem_gib = mem_bytes/(1024.**3) mem_pretty = round(mem_gib, 1) return {"cpu": {"cores": processor[0]["cpu cores"], "threads": len(processor), "name": processor[0]["model name"]}, "ram": mem_pretty} def get_cpuinfo(): cpus = [] with open("/proc/cpuinfo", "r") as fp: cpuinfo = fp.read() thread_info = cpuinfo.split("\n\n") thread_info = list(filter(lambda x: x!="", thread_info)) for thread in thread_info: thread_dict = {} field_lines = thread.split("\n") for field in field_lines: key, value = field.split(":") key = key.strip() value = value.strip() try: thread_dict.update({key: int(value)}) except ValueError: thread_dict.update({key: value}) cpus.append(thread_dict) return cpus def paper_version(): status = server_status() paper_version = status.version.name protocol_version = status.version.protocol possible_versions = list(filter(lambda x: x.get("version", 0) == protocol_version, minecraft_data.common().protocolVersions)) try: mc_version = possible_versions[0].get("minecraftVersion", "Unknown") except: mc_version = "Unknown" return {"paper": paper_version, "minecraft": mc_version} def datapack_info(): try: with MCRcon(CONFIG["mcrcon"]["host"], CONFIG["mcrcon"]["password"], port=int(CONFIG["mcrcon"]["port"])) as mcr: resp = mcr.command("datapack list") match = re.match("There are [0-9]* data packs enabled: (\[.*\])*.*", resp) datapacks = [] datapacks_string = match.group(1) for datapack in datapacks_string.split(","): match = re.match("\[(.*)\(.*\)\]", datapack.strip()) datapack_name = match.group(1) datapack_name = datapack_name.strip() datapack_name = datapack_name.replace("file/", "") datapack_name = datapack_name.replace(".zip", "") if not datapack_name in ["vanilla", "bukkit"]: datapacks.append(datapack_name) except ConnectionRefusedError: datapacks = [] return datapacks def server_details(): hardware = hardware_info() versions = paper_version() datapacks = datapack_info() return {"hardware": hardware, "versions": versions, "datapacks": datapacks} def server_status(): server = JavaServer.lookup(CONFIG["mcrcon"]["host"]) try: status = server.status() except ConnectionRefusedError: status = "UNKNOWN" return status CONFIG = parseConfig() run(host="localhost", port=8080)