From b248022eaa145f44e659381cd2f7bcb0882b9855 Mon Sep 17 00:00:00 2001
From: Lukas <m@hler.eu>
Date: Mon, 27 Sep 2021 01:45:08 +0200
Subject: [PATCH] =?UTF-8?q?Rewrite=20config=20to=20tuse=20oml=C3=83=20inst?=
 =?UTF-8?q?ead=20of=20.env?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 Pipfile                  |   2 +-
 Pipfile.lock             |  18 +++----
 myTS3.py                 | 102 +++++++++++++++------------------------
 gecko.py => src/gecko.py |   4 +-
 src/template.toml        |  12 +++++
 src/util.py              |  77 +++++++++++++++++++++++++++++
 util.py                  |  43 -----------------
 7 files changed, 140 insertions(+), 118 deletions(-)
 rename gecko.py => src/gecko.py (96%)
 create mode 100644 src/template.toml
 create mode 100644 src/util.py
 delete mode 100644 util.py

diff --git a/Pipfile b/Pipfile
index ee47af4..5736cd2 100644
--- a/Pipfile
+++ b/Pipfile
@@ -5,8 +5,8 @@ name = "pypi"
 
 [packages]
 ts3 = "*"
-python-dotenv = "*"
 pycoingecko = "*"
+toml = "*"
 
 [dev-packages]
 
diff --git a/Pipfile.lock b/Pipfile.lock
index 18d8970..6ca10a6 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
 {
     "_meta": {
         "hash": {
-            "sha256": "dea9fe66fc91a3adf33caec6ce10635546f9e81ec613bc3bbba14c257d57029b"
+            "sha256": "d9f13c4341b606a12b8b0ef95f6bce67911d3f7866a6c4552c342c4a5ae15fdf"
         },
         "pipfile-spec": 6,
         "requires": {
@@ -47,14 +47,6 @@
             "index": "pypi",
             "version": "==2.2.0"
         },
-        "python-dotenv": {
-            "hashes": [
-                "sha256:aae25dc1ebe97c420f50b81fb0e5c949659af713f31fdb63c749ca68748f34b1",
-                "sha256:f521bc2ac9a8e03c736f62911605c5d83970021e3fa95b37d769e2bbbe9b6172"
-            ],
-            "index": "pypi",
-            "version": "==0.19.0"
-        },
         "requests": {
             "hashes": [
                 "sha256:6c1246513ecd5ecd4528a0906f910e8f0f9c6b8ec72030dc9fd154dc1a6efd24",
@@ -63,6 +55,14 @@
             "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5'",
             "version": "==2.26.0"
         },
+        "toml": {
+            "hashes": [
+                "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b",
+                "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"
+            ],
+            "index": "pypi",
+            "version": "==0.10.2"
+        },
         "ts3": {
             "hashes": [
                 "sha256:5c7ddee40f4446d4b6c541665cc536d270481f82c27adfe1c2e371426ddbd0d7",
diff --git a/myTS3.py b/myTS3.py
index 04142dc..d1b2b9f 100644
--- a/myTS3.py
+++ b/myTS3.py
@@ -3,24 +3,21 @@ TBD
 """
 
 __author__ = "Lukas Mahler"
-__version__ = "0.3.0"
-__date__ = "25.09.2021"
+__version__ = "0.0.0"
+__date__ = "27.09.2021"
 __email__ = "m@hler.eu"
 __status__ = "Development"
 
 # Default
-import os
 import sys
 import time
 import json
 
 # Custom
 import ts3
-import dotenv  # python-dotenv
 
 # Self
-import gecko
-import util
+from src import util, gecko
 
 
 class TSbot:
@@ -31,12 +28,15 @@ class TSbot:
         start the event loop function.
         """
 
-        self.host = conf["host"]
-        self.port = conf["port"]
-        self.user = conf["user"]
-        self.pwd = conf["pwd"]
-        self.sid = conf["sid"]
-        self.nickname = conf["name"]
+        self.host = conf["Connection"]["host"]
+        self.port = conf["Connection"]["port"]
+        self.sid = conf["Connection"]["sid"]
+
+        self.user = conf["Authentication"]["user"]
+        self.pwd = conf["Authentication"]["pwd"]
+
+        self.nickname = conf["Misc"]["nickname"]
+        self.whitelisted = conf["Misc"]["whitelisted"]
 
         self.gecko = gecko.GeckoAPI()
         self.log = log
@@ -73,8 +73,8 @@ class TSbot:
         if len(me) == 1:
             self.myid = me[0]["clid"]
         else:
-            self.pipeOut("Can't find my own client id.", lvl="critical")
-            raise ValueError("Can't find my own client id.")
+            self.pipeOut("Can't find my own client id. terminating...", lvl="critical")
+            exit(1)
 
         ''' if you want to move the Bot to a certain channel (instead of the defualt channel, you can do: '''
         # self.bot.clientmove(clid=self.myid,cid=129)
@@ -95,7 +95,9 @@ class TSbot:
         # Subscribe to channel movement events
         # self.bot.servernotifyregister(event="channel", id_=0)
 
-        time.sleep(5)  # This can be removed if the Query Client is Whitelisted
+        if not self.whitelisted:
+            time.sleep(5)
+
         # Notify connected admins
         self.notifyAdmin()
 
@@ -168,23 +170,15 @@ class TSbot:
                 else:
                     self.pipeOut(f"Unknown Event: {event.__dict__}", lvl="warning")
 
-    def pipeOut(self, msg, lvl="info", reprint=True):
+    def pipeOut(self, msg, lvl="INFO", reprint=True):
         """
         All output pipes through this function.
         It's possible to print the output to console [set reprint to True]
         or run in silent log mode. [set reprint to False]
         """
 
-        if lvl.lower() == "debug":
-            self.log.debug(msg)
-        elif lvl.lower() == "info":
-            self.log.info(msg)
-        elif lvl.lower() == "warn":
-            self.log.warning(msg)
-        elif lvl.lower() == "error":
-            self.log.error(msg)
-        else:
-            self.log.critical(msg)
+        lvln = int(getattr(util.logging, lvl.upper()))
+        self.log.log(lvln, msg)
 
         if reprint:
             print(f"[{time.strftime('%H:%M:%S')}][{lvl.upper()}] {msg}")
@@ -211,7 +205,9 @@ class TSbot:
             clid = client["clid"]
             if self.isadmin(cldbid):
                 self.bot.sendtextmessage(targetmode=1, target=clid, msg=self.intro)
-            time.sleep(1)  # This can be removed if the Query Client is Whitelisted
+
+            if not self.whitelisted:
+                time.sleep(1)
 
     def kickall(self, msg):
         """
@@ -253,10 +249,10 @@ class TSbot:
             i = 0
             while n == -1 or i < n:
                 for clid in clients:
-                    self.pipeOut(f"{clid}", lvl="debug")
                     try:
                         self.bot.clientpoke(msg=msg, clid=clid)
-                    except:
+                    except Exception as e:
+                        self.pipeOut(e, lvl="warning")
                         pass
                 time.sleep(delay)
                 i += 1
@@ -297,8 +293,8 @@ class TSbot:
         Message the invoker of the function either a list of channels or a list of clients.
         """
 
+        mydict = {}
         if what == "channel":
-            mydict = {}
             channels = self.bot.channellist()
             for channel in channels:
                 order = channel["channel_order"]
@@ -308,8 +304,11 @@ class TSbot:
             msg = json.dumps(mydict)
 
         elif what == "clients":
-            msg = self.bot.clientlist()
-            # TODO
+            clients = self.bot.clientlist()
+            for client in clients:
+                mydict[client["client_nickname"]] = {"clid": client["clid"], "cldbid": client["client_database_id"]}
+            mydict = dict(sorted(mydict.items()))
+            msg = json.dumps(mydict)
         else:
             msg = None
 
@@ -360,6 +359,7 @@ class TSbot:
         .test
         .btc / .eth
         .dot / .ada
+        .info                   clid
         .list                   channel/clients
         .follow
         .rename                 nickname
@@ -407,6 +407,9 @@ class TSbot:
             except ts3.query.TS3QueryError:
                 pass
 
+        elif command == ".info":
+            pass  # TODO
+
         elif command == ".list":
             try:
                 self.list(parameter[0], invkr_id)
@@ -453,17 +456,7 @@ class TSbot:
 
         usrd = {}
         info = self.bot.clientinfo(clid=clid)
-        temp = info._data[0].split()
-        for t1 in temp:
-            t2 = t1.decode("utf-8")
-            t3 = t2.split("=")
-            try:
-                t3[1]
-            except Exception:
-                t3.append("None")
-                pass
-
-            usrd[t3[0]] = t3[1]
+        usrd = info.__dict__
 
         return usrd
 
@@ -472,7 +465,7 @@ class TSbot:
         Print all assigned groups for every connected client.
         """
 
-        clients = self.bot.clientlist()
+        # clients = self.bot.clientlist()
         # clients_cldbid = [client["client_database_id"] for client in clients if client["client_type"] != "1"]
         clients = self.bot.clientlist(groups=True)
         clients_groups = [client["client_servergroups"] for client in clients if client["client_type"] != "1"]
@@ -489,25 +482,8 @@ def main():
     # Log unhandled exception
     sys.excepthook = util.unhandledException
 
-    # Load Dotenv
-    dotenv_file = dotenv.find_dotenv()
-    if not dotenv_file:
-        log.critical("could not locate .env file")
-        raise FileNotFoundError("could not locate .env file")
-    dotenv.load_dotenv(dotenv_file)
-    dotend_keys = dotenv.dotenv_values()
-
-    if not {"_HOST", "_PORT", "_USER", "_PWD", "_SID"} <= dotend_keys.keys():
-        log.critical("missing keys in your .env file.")
-        raise ValueError("missing keys in your .env file.")
-
-    # Config
-    conf = dict(host=os.getenv("_HOST"),
-                port=os.getenv("_PORT"),
-                user=os.getenv("_USER"),
-                pwd=os.getenv("_PWD"),
-                sid=os.getenv("_SID"),
-                name=os.getenv("_NAME"))
+    # Load toml config
+    conf = util.getconf("prod.toml")
 
     # Start the Bot Instance
     TSbot(conf, log)
diff --git a/gecko.py b/src/gecko.py
similarity index 96%
rename from gecko.py
rename to src/gecko.py
index 7651a2c..9530760 100644
--- a/gecko.py
+++ b/src/gecko.py
@@ -3,8 +3,8 @@ TBD
 """
 
 __author__ = "Lukas Mahler"
-__version__ = "0.0.1"
-__date__ = "23.09.2021"
+__version__ = "0.0.0"
+__date__ = "27.09.2021"
 __email__ = "m@hler.eu"
 __status__ = "Development"
 
diff --git a/src/template.toml b/src/template.toml
new file mode 100644
index 0000000..be36ef8
--- /dev/null
+++ b/src/template.toml
@@ -0,0 +1,12 @@
+[Connection]
+host = "example.com"
+port = 10011
+sid  = 1
+
+[Authentication]
+user = "exampleuser"
+pwd  = "password"
+
+[Misc]
+nickname = "myTS3-Bot"
+whitelisted = false
\ No newline at end of file
diff --git a/src/util.py b/src/util.py
new file mode 100644
index 0000000..362e1c6
--- /dev/null
+++ b/src/util.py
@@ -0,0 +1,77 @@
+"""
+TBD
+"""
+
+__author__ = "Lukas Mahler"
+__version__ = "0.0.0"
+__date__ = "27.09.2021"
+__email__ = "m@hler.eu"
+__status__ = "Development"
+
+
+# Default
+import sys
+import shutil
+import os.path
+import logging
+from datetime import date
+
+# Custom
+import toml
+
+
+def unhandledException(exc_type, exc_value, exc_traceback):
+    """
+    src = https://stackoverflow.com/a/16993115/5593051
+    """
+
+    if issubclass(exc_type, KeyboardInterrupt):
+        sys.__excepthook__(exc_type, exc_value, exc_traceback)
+        return
+
+    log.critical("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
+
+
+def getconf(fname):
+    """
+
+    """
+    if fname.endswith(".toml"):
+        if os.path.isfile(fname):
+            try:
+                data = toml.load(fname)
+                return data
+            except ValueError as e:
+                log.critical(e)
+                # log.critical("The provided '.toml' seems to be invalid.")
+                exit(1)
+        else:
+            log.critical(f"Couldn't locate the '.toml' file [{fname}].")
+            log.info("Creating a new '.toml' file from template, please edit and restart.")
+            shutil.copy("src/template.toml", fname)
+            exit(1)
+    else:
+        log.critical("The provided config file is not a '.toml' file.")
+        log.info("Creating a new '.toml' file from template, please edit and restart.")
+        shutil.copy("src/template.toml", "prod.toml")
+        exit(1)
+
+
+def setupLogger(lvl="DEBUG"):
+    """
+
+    """
+    global log  # Needed to log exceptions in src\util
+    log = logging.getLogger()
+    now = date.today().strftime("%Y-%m-%d")
+    handler = logging.FileHandler(f"myTS3_{now}.log", encoding="utf-8")
+    logformat = logging.Formatter("%(asctime)s %(levelname)7s %(message)s", "%d-%m-%Y %H:%M:%S")
+    handler.setFormatter(logformat)
+    log.addHandler(handler)
+    log.setLevel(lvl)
+
+    return log
+
+
+if __name__ == "__main__":
+    exit()
diff --git a/util.py b/util.py
deleted file mode 100644
index 09e4d9d..0000000
--- a/util.py
+++ /dev/null
@@ -1,43 +0,0 @@
-"""
-TBD
-"""
-
-__author__ = "Lukas Mahler"
-__version__ = "0.0.0"
-__date__ = "25.09.2021"
-__email__ = "m@hler.eu"
-__status__ = "Development"
-
-
-# Default
-import sys
-import logging
-from datetime import date
-
-
-def unhandledException(exc_type, exc_value, exc_traceback):
-    if issubclass(exc_type, KeyboardInterrupt):
-        sys.__excepthook__(exc_type, exc_value, exc_traceback)
-        return
-
-    log.critical("Uncaught exception", exc_info=(exc_type, exc_value, exc_traceback))
-
-
-def setupLogger():
-    """
-
-    """
-    global log
-    log = logging.getLogger()
-    now = date.today().strftime("%Y-%m-%d")
-    handler = logging.FileHandler(f"myTS3_{now}.log", encoding="utf-8")
-    logformat = logging.Formatter("%(asctime)s %(levelname)7s %(message)s", "%d-%m-%Y %H:%M:%S")
-    handler.setFormatter(logformat)
-    log.addHandler(handler)
-    log.setLevel(logging.DEBUG)
-
-    return log
-
-
-if __name__ == "__main__":
-    exit()