1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
#!/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
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
raise DateException('target date is too far in the future')
def __notifycliententerview(self, data: dict):
# return all non voice clients without reasonid 0
if data['client_type'] != '0' or data['reasonid'] != '0':
return
# check if the current date is still eligible
self.__datecheck()
cldbid = data['client_database_id']
user_grps = data['client_servergroups'].split(sep=',')
msg = '{client_nickname}:{client_database_id} connected - member of {client_servergroups}'
logging.debug(msg.format(**data))
# only try to add nonmembers to group
if str(self.gid) not in user_grps:
try:
# Usage: servergroupaddclient sgid={groupID} cldbid={clientDBID}
# cmd = self.conn.servergroupaddclient(sgid=self.gid, cldbid=cldbid)
cmd = self.conn.clientdbinfo(cldbid=cldbid)
if cmd.error['id'] != '0':
logging.error(cmd.data[0].decode("utf-8"))
# log process
logging.info('{client_nickname}:{client_database_id} added to {gid}'.format(**data, gid=self.gid))
# log possible key errors while the teamspeak 5 client is not fully working
except KeyError as err:
logging.error([err, data])
def __eventhandler(self, event: str, data: dict):
"""
event handler which separates events to their specific handlers
"""
# client enter events
if event == "notifycliententerview":
self.__notifycliententerview(data)
# all other events return to main
else:
return
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:
# handle incoming events
self.__eventhandler(event.event, event.parsed[0])
|