From 9080c719dc0252b43fd894d71cf7d410ae8b0cb6 Mon Sep 17 00:00:00 2001
From: nico <nico@magicbroccoli.de>
Date: Sat, 7 Dec 2019 13:09:41 +0100
Subject: TeamSpeak datebased GroupAssigner

cleaned up working release

+ properly register for servernotify events
+ process only voice_clients
+ properly check if client is already a group member
+ logging
---
 .gitignore         | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 config.json.sample |   9 ++++
 config.py          |  17 +++++++
 main.py            |  72 ++++++++++++++++++++++++++++
 requirements.txt   |   1 +
 5 files changed, 237 insertions(+)
 create mode 100644 .gitignore
 create mode 100644 config.json.sample
 create mode 100644 config.py
 create mode 100644 main.py
 create mode 100644 requirements.txt

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..821dc45
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,138 @@
+# Created by .ignore support plugin (hsz.mobi)
+### Python template
+# Byte-compiled / optimized / DLL files
+__pycache__/
+*.py[cod]
+*$py.class
+
+# C extensions
+*.so
+
+# Distribution / packaging
+.Python
+build/
+develop-eggs/
+dist/
+downloads/
+eggs/
+.eggs/
+lib/
+lib64/
+parts/
+sdist/
+var/
+wheels/
+pip-wheel-metadata/
+share/python-wheels/
+*.egg-info/
+.installed.cfg
+*.egg
+MANIFEST
+
+# PyInstaller
+#  Usually these files are written by a python script from a template
+#  before PyInstaller builds the exe, so as to inject date/other infos into it.
+*.manifest
+*.spec
+
+# Installer logs
+pip-log.txt
+pip-delete-this-directory.txt
+
+# Unit test / coverage reports
+htmlcov/
+.tox/
+.nox/
+.coverage
+.coverage.*
+.cache
+nosetests.xml
+coverage.xml
+*.cover
+*.py,cover
+.hypothesis/
+.pytest_cache/
+
+# Translations
+*.mo
+*.pot
+
+# Django stuff:
+*.log
+local_settings.py
+db.sqlite3
+db.sqlite3-journal
+
+# Flask stuff:
+instance/
+.webassets-cache
+
+# Scrapy stuff:
+.scrapy
+
+# Sphinx documentation
+docs/_build/
+
+# PyBuilder
+target/
+
+# Jupyter Notebook
+.ipynb_checkpoints
+
+# IPython
+profile_default/
+ipython_config.py
+
+# pyenv
+.python-version
+
+# pipenv
+#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
+#   However, in case of collaboration, if having platform-specific dependencies or dependencies
+#   having no cross-platform support, pipenv may install dependencies that don't work, or not
+#   install all needed dependencies.
+#Pipfile.lock
+
+# PEP 582; used by e.g. github.com/David-OConnor/pyflow
+__pypackages__/
+
+# Celery stuff
+celerybeat-schedule
+celerybeat.pid
+
+# SageMath parsed files
+*.sage.py
+
+# Environments
+.env
+.venv
+env/
+venv/
+ENV/
+env.bak/
+venv.bak/
+
+# Spyder project settings
+.spyderproject
+.spyproject
+
+# Rope project settings
+.ropeproject
+
+# mkdocs documentation
+/site
+
+# mypy
+.mypy_cache/
+.dmypy.json
+dmypy.json
+
+# Pyre type checker
+.pyre/
+
+# pycharm
+.idea
+
+# project specific
+config.json
+*.log
diff --git a/config.json.sample b/config.json.sample
new file mode 100644
index 0000000..2f2eedf
--- /dev/null
+++ b/config.json.sample
@@ -0,0 +1,9 @@
+{
+  "host": "localhost",
+  "port": 10011,
+  "user": "serveradmin",
+  "password": "super_secret-Password",
+  "server_id": 1337,
+
+  "gid" : 9999
+}
\ No newline at end of file
diff --git a/config.py b/config.py
new file mode 100644
index 0000000..d0dc061
--- /dev/null
+++ b/config.py
@@ -0,0 +1,17 @@
+# -*- coding: utf-8 -*-
+import json
+
+with open("config.json", "r", encoding="utf-8") as file:
+    config = json.load(file)
+
+
+class Config(object):
+    # connection arguments
+    HOST = config['host']
+    PORT = config['port']
+    USER = config['user']
+    PW = config['password']
+    SID = config['server_id']
+
+    # assignment settings
+    GID = config['gid']
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..532e4af
--- /dev/null
+++ b/main.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+import logging
+
+import ts3
+
+from config import Config
+
+
+def watcher(conn):
+    # Register for events
+    # https://yat.qa/ressourcen/server-query-notify/#server
+    conn.servernotifyregister(event="server")
+
+    while True:
+        conn.send_keepalive()
+
+        try:
+            event = conn.wait_for_event(timeout=60)
+
+        except ts3.query.TS3TimeoutError:
+            pass
+
+        else:
+            # only parse entering clients info
+            if event.event == "notifycliententerview":
+
+                # skip query clients -- query client = 1 , voice client = 0
+                if event[0]['client_type'] == '0':
+
+                    # reasonid should be 0 not sure though
+                    if event[0]["reasonid"] == "0":
+
+                        user_grps = event.parsed[0]['client_servergroups'].split(sep=',')
+                        gid = Config.GID
+
+                        # only try to add nonmembers to group
+                        if str(gid) not in user_grps:
+
+                            cldbid = event.parsed[0]['client_database_id']
+
+                            # https://yat.qa/ressourcen/server-query-kommentare/
+                            # Usage: servergroupaddclient sgid={groupID} cldbid={clientDBID}
+                            try:
+                                cmd = conn.servergroupaddclient(sgid=gid, cldbid=cldbid)
+
+                                if cmd.error['id'] != '0':
+                                    logger.info(cmd.data[0].decode("utf-8"))
+
+                                # log process
+                                logmsg = '{client_nickname}:{client_database_id} - added to {gid}'
+                                logging.info(logmsg.format(**event[0], gid=gid))
+
+                            except KeyError:
+                                logger.error(str(event.parsed))
+                                pass
+
+
+if __name__ == "__main__":
+    # logging
+    logger = logging.getLogger()
+    logger.setLevel(logging.INFO)
+
+    logf = logging.FileHandler('info.log')
+    logf.setFormatter(logging.Formatter("%(asctime)s - %(levelname)-8s - %(message)s"))
+
+    logger.addHandler(logf)
+
+    with ts3.query.TS3Connection(Config.HOST, Config.PORT) as ts3conn:
+        ts3conn.login(client_login_name=Config.USER, client_login_password=Config.PW)
+        ts3conn.use(sid=Config.SID)
+        watcher(ts3conn)
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..ae7b420
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1 @@
+ts3>=1.0.11
-- 
cgit v1.2.3-54-g00ecf