diff options
Diffstat (limited to 'pycrctrl.py')
| -rw-r--r-- | pycrctrl.py | 181 |
1 files changed, 130 insertions, 51 deletions
diff --git a/pycrctrl.py b/pycrctrl.py index e53a722..6935512 100644 --- a/pycrctrl.py +++ b/pycrctrl.py @@ -15,29 +15,30 @@ import sys -if sys.version_info < (3,6): - class ModuleNotFoundError(ImportError): - pass - -import socket +#import socket import subprocess import urllib.request import urllib.error import urllib.parse from _thread import start_new_thread -import inspect +#import inspect import queue -import traceback import os import re -import signal +#import signal import json import base64 +import supybot.log import supybot.ircmsgs as ircmsgs from pickle import Pickler, Unpickler from PyQt5.QtCore import * +from time import sleep +from platform import architecture +from io import BytesIO +#iostream import + try: from iostream import * except ModuleNotFoundError: @@ -47,6 +48,8 @@ except ModuleNotFoundError: from iostream import * import random +import tarfile +from gzip import GzipFile from time import sleep from enum import IntEnum @@ -57,38 +60,44 @@ from enum import IntEnum class QString(str): pass -class QStringList(QObject): +class QList(QObject): """ QStringList """ - typeclass = str - strlist = list() + list = list() + typeclass = None def __init__(self, *args): QObject.__init__(self) - self.strlist = list(args) + self.list = list(args) def __getitem__(self, name): if type(name) != int: raise ValueError("Wrong datatype for name!") else: - return self.strlist[name] + return self.list[name] def __lshift__(self, other): - if type(other) != self.typeclass: + if self.typeclass and not isinstance(other, self.typeclass): raise ValueError("Wrong datatype") else: - self.strlist.append(other) + self.list.append(other) def __call__(self): - return self.strlist + return self.list def __len__(self): - return len(self.strlist) + return len(self.list) + + def __repr__(self): + return "{}({})".format(self.__name__, self.list) # Methods def isEmpty(self): - return len(self.strlist) == 0 + return len(self.list) == 0 + +class QStringList(QList): + typeclass = str class CmdResult(IntEnum): UnknownCommand = -1 @@ -103,6 +112,96 @@ class Capability(IntEnum): Admin = 1 Moderator = 2 +class Updater(object): + parent = None + __current_revision = "" + lookuptable = {"64bit" : "amd64", "32bit" : "i386"} + + def __init__(self, parent): + self.parent = parent + with open(os.path.join(self.parent.path, "snapshot.id"), "rb") as fobj: + self.__current_revision = fobj.read().decode("utf-8") + start_new_thread(self.checkForUpdates, ()) + + @property + def current_revision(self): + return self.__current_revision + + @current_revision.setter + def current_revision(self, other): + self.__current_revision = other + if type(other) in [QString, str]: + try: + other = other.encode("utf-8") + except Exception: + raise TypeError("Wrong datatype!") from None + + with open(os.path.join(self.parent.path, "snapshot.id"), "wb") as fobj: + fobj.write(other) + + def checkForUpdates(self): + while True: + try: + site = urllib.request.urlopen("http://openclonk.org/nightly-builds").read().decode("utf-8").split("<a href='/builds/nightly/snapshots/") + site.remove(site[0]) + site = [i.split("' title")[0] for i in site] + + x = None + for i in site: + x = re.match(r"openclonk-snapshot-(.*)-(.*)-{}-{}-.*".format(sys.platform, self.lookuptable[architecture()[0]]), i) + if x: + rev = x.group(2) + + if self.current_revision != rev: + self.current_revision = rev + self.loadNewSnapshot(x) + + break + if not x: + cout << "Regex didn't match" << endl + + except Exception as e: + supybot.log.exception(str(e)) + finally: + sleep(10) + + def loadNewSnapshot(self, reg): + supybot.log.info("Downloading snapshot...") + with open(os.path.join(self.parent.path, "snapshot"), "wb") as fobj: + fobj.write(urllib.request.urlopen("http://openclonk.org/builds/nightly/snapshots/{}".format(reg.group(0).split("' title")[0])).read()) + + #extract the snapshot + tar = tarfile.open(os.path.join(self.parent.path, "snapshot"), mode="r:bz2") + tar.extractall(path=self.parent.path) + supybot.log.info("New snapshot has been extracted.") + + #get the openclonk-server autobuild + site = json.loads(urllib.request.urlopen("https://autobuild.openclonk.org/api/v1/jobs").read().decode("utf-8")) + + for commit in site: + for build in commit["builds"]: + if re.match(r"{}-{}-.*".format(sys.platform, self.lookuptable[architecture()[0]]), build["platform"]["triplet"]): + for b in build["components"]: + reg = re.match(r".*/openclonk-server-(.*)-(.*)-(.*)-.*", str(b["path"])) #skip the engine check as the only useful one is openclonk-server + #if reg: + # cout << str(reg) << " " + # cout << reg.group(1) << " " << reg.group(2) << " " << reg.group(3) << " " << " :: " + # cout << self.current_revision << sys.platform << self.lookuptable[architecture()[0]] << endl + # cout << endl + if reg and (reg.group(1), reg.group(2), reg.group(3)) == (self.current_revision[:-3], sys.platform, self.lookuptable[architecture()[0]]): + supybot.log.info("Downloading openclonk-server build...") + buffer = BytesIO() + buffer.write(urllib.request.urlopen("https://autobuild.openclonk.org/static/binaries/{}".format(b["path"])).read()) + buffer.seek(0) + with open(os.path.join(self.parent.path, "openclonk-server"), "wb") as fobj: + fobj.write(GzipFile(fileobj=buffer).read()) + + supybot.log.info("New openclonk-server build has been extracted.") + os.chmod(os.path.join(self.parent.path, "openclonk-server"), os.stat(os.path.join(self.parent.path, "openclonk-server")).st_mode | 64) + return True + + + class PyCRCtrl(object): """Server control""" @@ -112,45 +211,37 @@ class PyCRCtrl(object): config = {} scenario = "" - #prefix = "@" commands = {} - #engine = "clonk" codec = None - #commandline = "/fullscreen /lobby:300 /config:config.txt /record /faircrew" - #path = QString() + path = QString() scenlist = QStringList() league_scenlist = QStringList() - #channels = {} topic = "Kein laufendes Spiel." league = True __state = "Lobby" __ingamechat = "aktiviert" - #RegExps = { - # "lobbyStartExp" : "((?:Los geht's!|Action go!)\s*)", - # "startExp" : r"^Start!\s*$", - # "joinExp" : r"^Client (.+) (?:verbunden|connected)\.\s*$", - # "leaveExp" : "^Client (.+) (?:entfernt|removed)(.*)", - # "shutdownExp" : r"^Internetspiel ausgewertet(.*)" - # } - capabilities = {} _capa_old = {} + updater = None + def __init__(self, irc=None, path=None, config="pycrctrl.conf"): if sys.platform == "win32": - raise NotImplementedError("{} wird nicht unterstützt!".format(sys.platform)) + raise NotImplementedError("{} wird nicht unterstützt!".format(sys.platform)) self.irc = irc self.path = path self.loadConfigFile(config) self.loadCapabilities() self.loadScenarioList() - self.codec = codec = QTextCodec.codecForName(self.config.get("encoding")) + self.codec = QTextCodec.codecForName(self.config.get("encoding")) self.queue = queue.Queue(5) + if self.config.get("engine") == "openclonk-server": + self.updater = Updater(self) def __ostream__(self, ostream): return "PyCRCtrl: commandline: {}, channel: {}, scenario: {}".format(self.commandline, self.channels["ingame"], (self.scenario if self.scenario != "" else "None")) @@ -181,10 +272,10 @@ class PyCRCtrl(object): return False with open(os.path.join(self.path,"scenlist"), "rb") as fobj: - self.scenlist.strlist = Unpickler(fobj).load() + self.scenlist.list = Unpickler(fobj).load() with open(os.path.join(self.path, "scenlist.league"), "rb") as fobj: - self.league_scenlist.strlist = Unpickler(fobj).load() + self.league_scenlist.list = Unpickler(fobj).load() return True @@ -192,7 +283,7 @@ class PyCRCtrl(object): if self.path == None: return False - _capa_old = json.load(open(os.path.join(self.path, "capabilities.conf"), "r")) + self._capa_old = json.load(open(os.path.join(self.path, "capabilities.conf"), "r")) return True @@ -241,7 +332,7 @@ class PyCRCtrl(object): if self.queue.full() == False: self.queue.put(scenario) - return (CmdResult.Success, 'Szenario "{}" wurde der Warteschlange hinzugefügt.'.format(scenario)) + return (CmdResult.Success, 'Szenario "{}" wurde der Warteschlange hinzugefügt.'.format(scenario)) else: return (CmdResult.RuntimeError, "Die Warteschlange ist voll!") @@ -256,7 +347,7 @@ class PyCRCtrl(object): self.capabilities = self._capa_old self.clonk = subprocess.Popen( - './{} {} "{}"'.format(self.config.get("engine"), self.config.get("commandline") + (" --" if type(self).__name__ == "PyOCCtrl" else " /") + ("league" if self.scenario in self.league_scenlist() else "noleague"),self.scenario), + './{} {} "{}"'.format(self.config.get("engine"), self.config.get("commandline") + (" --" if self.config.get("engine") == "openclonk-server" else " /") + ("league" if self.scenario in self.league_scenlist() else "noleague"),self.scenario), 0, None, subprocess.PIPE, @@ -345,19 +436,7 @@ class PyCRCtrl(object): self.clonk.stdin.close() except Exception as e: -#ifdef DEBUG - if self.clonk and __debug__ and not self.clonk.stdin.closed: - self.writeToServer(b"Fehler:") -#endif - tb = traceback.format_exc() - for line in tb.splitlines(): -#ifdef DEBUG - self.writeToServer(line) -#endif - try: - cerr << line << endl - except: - pass + cerr << str(e) << endl continue return (CmdResult.Success, "") |
