aboutsummaryrefslogtreecommitdiffstats
path: root/TSGroupAssigner/group_assign.py
diff options
context:
space:
mode:
Diffstat (limited to 'TSGroupAssigner/group_assign.py')
-rw-r--r--TSGroupAssigner/group_assign.py183
1 files changed, 183 insertions, 0 deletions
diff --git a/TSGroupAssigner/group_assign.py b/TSGroupAssigner/group_assign.py
new file mode 100644
index 0000000..073ba45
--- /dev/null
+++ b/TSGroupAssigner/group_assign.py
@@ -0,0 +1,183 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+import datetime as dt
+import logging
+import sys
+import time
+
+import ts3
+
+
+class DateException(Exception):
+ """raise this if the date delta exceeds the configured range"""
+
+
+class GroupAssigner:
+ def __init__(self, host: str, port: (str, int), user: str, password: str, sid: (str, int), gid: (str, int),
+ date: dt.date, delta: dt.timedelta):
+ # host config
+ self.host = host
+ self.port = port
+ self.user = user
+ self.pw = password
+ self.sid = sid
+
+ # group
+ self.gid = gid
+
+ # start date and delta
+ self.sleepstart = date - dt.timedelta(days=1)
+ self.startdate = date
+ self.enddate = date + delta
+ self.delta = delta
+
+ def __connect(self):
+ """
+ establish query connection and return connection handler
+ """
+ try:
+ # connect to the telnet interface
+ self.conn = ts3.query.TS3Connection(self.host, self.port)
+
+ # login
+ self.conn.login(client_login_name=self.user, client_login_password=self.pw)
+
+ # select specified sid
+ self.conn.use(sid=self.sid)
+
+ # break if credentials are invalid
+ except ts3.query.TS3QueryError as TS3QueryError:
+ # log error line and reraise
+ logging.error(TS3QueryError)
+ raise TS3QueryError
+
+ def __disconnect(self):
+ """
+ method to gracefully logout and disconnect the connection
+ this should only be called if the exit is intentional
+ """
+ try:
+ self.conn.logout()
+ self.conn.quit()
+
+ # attribute error if disconnect is called prior to the connection being established
+ except AttributeError:
+ pass
+
+ # broad exception if something unexpected happens
+ except ts3.TS3Error as TS3Error:
+ # log error and reraise exception
+ logging.error(TS3Error)
+ raise TS3Error
+
+ # exit
+ sys.exit()
+
+ def __datecheck(self):
+ """
+ method to check if the current date is still in the configured date range
+ """
+ now = dt.date.today()
+
+ # check if target date is in the configured range
+ if self.startdate <= now <= self.enddate:
+ logging.debug('target date within configured date range')
+ return True
+
+ # if date range is exceeded shutdown gracefully
+ else:
+ # terminate possible open connections
+ logging.info('the current date exceeds the configured date range -- exiting')
+ self.__disconnect()
+
+ def __sleepstart(self):
+ """
+ method to check if the process is eligible to sleepstart
+ """
+ now = dt.date.today()
+
+ # start date already reached proceed
+ if self.startdate <= now:
+ logging.debug('start date is already reached -- not eligible to sleepstart continue')
+ return
+
+ # if startdate within the next 24h proceed to sleepstart
+ elif now >= self.startdate - dt.timedelta(days=1):
+ # convert both dates to datetime
+ starttime = dt.datetime.combine(self.startdate, dt.time(hour=0, minute=0, second=0))
+ now = dt.datetime.now()
+
+ # calculate remaining time delta
+ remaindelta = starttime - now
+ logging.debug('target date will be reached in {sec} seconds -- sleeping'.format(sec=remaindelta.seconds))
+ time.sleep(remaindelta.seconds + 1)
+
+ # if the date is too far back raise DateException
+ else:
+ raise DateException('target date is too far in the future')
+
+ def start(self):
+ # eol to start process ahead of time
+ self.__sleepstart()
+
+ # proceed only if target date is inside the date range
+ if self.__datecheck():
+ # init connection
+ self.__connect()
+
+ # start processing
+ self.__main()
+
+ def __main(self):
+ # register for "server" notify event
+ self.conn.servernotifyregister(event="server")
+
+ # start listening and processing
+ while True:
+ self.conn.send_keepalive()
+
+ try:
+ # wait for an event to be thrown
+ event = self.conn.wait_for_event(timeout=60)
+
+ # process TeamSpeak Telnet timeout
+ except ts3.query.TS3TimeoutError:
+ pass
+
+ else:
+ # only parse entering clients info
+ if event.event == "notifycliententerview":
+ # is the event still eligible
+ self.__datecheck()
+
+ # skip query clients -- query client = 1 , voice client = 0
+ if event[0]['client_type'] == '0':
+
+ # reasonid should be 0 not sure why though
+ if event[0]["reasonid"] == "0":
+
+ cldbid = event.parsed[0]['client_database_id']
+ user_grps = event.parsed[0]['client_servergroups'].split(sep=',')
+
+ msg = '{client_nickname}:{client_database_id} connected - member of {client_servergroups}'
+ logging.debug(msg.format(**event[0]))
+
+ # only try to add nonmembers to group
+ if str(self.gid) not in user_grps:
+
+ # https://yat.qa/ressourcen/server-query-kommentare/
+ # Usage: servergroupaddclient sgid={groupID} cldbid={clientDBID}
+ try:
+ cmd = self.conn.servergroupaddclient(sgid=self.gid, cldbid=cldbid)
+
+ if cmd.error['id'] != '0':
+ logging.error(cmd.data[0].decode("utf-8"))
+
+ # log process
+ msg = '{client_nickname}:{client_database_id} added to {gid}'
+ logging.info(msg.format(**event[0], gid=self.gid))
+
+ # log possible key errors while the teamspeak 5 client is not fully working
+ except KeyError:
+ logging.error(str(event.parsed))
+ pass