commit e26de5193009963642d74da259964ef667311c53 Author: Thor77 Date: Thu Mar 5 18:48:10 2015 +0100 Initial diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..20efd1b --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/README.md b/README.md new file mode 100644 index 0000000..531d3b1 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# TeamspeakStats +A simple Teamspeak stat-generator - based on server-logs diff --git a/tsstats.py b/tsstats.py new file mode 100755 index 0000000..cf7e3ae --- /dev/null +++ b/tsstats.py @@ -0,0 +1,209 @@ +import re +import configparser +from time import mktime +from datetime import datetime, timedelta +config = configparser.ConfigParser() +config.read('config.ini') +if 'General' not in config or not ('logfile' in config['General'] and 'outputfile' in config['General']): + print('Invalid configuration!') + import sys + sys.exit() +log_path = config['General']['logfile'] +output_path = config['General']['outputfile'] +generation_start = datetime.now() +clients = {} # clid: {'nick': ..., 'onlinetime': ..., 'kicks': ..., 'pkicks': ..., 'bans': ..., 'last_connect': ..., 'connected': ...} + +cldata = re.compile(r"'(.*)'\(id:(\d*)\)") +cldata_ban = re.compile(r"by\ client\ '(.*)'\(id:(\d*)\)") +cldata_invoker = re.compile(r"invokerid=(\d*)\ invokername=(.*)\ invokeruid") + + +def add_connect(clid, nick, logdatetime): + check_client(clid, nick) + clients[clid]['last_connect'] = mktime(logdatetime.timetuple()) + clients[clid]['connected'] = True + + +def add_disconnect(clid, nick, logdatetime): + check_client(clid, nick) + connect = datetime.fromtimestamp(clients[clid]['last_connect']) + delta = logdatetime - connect + minutes = delta.seconds // 60 + increase_onlinetime(clid, minutes) + clients[clid]['connected'] = False + + +def add_ban(clid, nick): + check_client(clid, nick) + if 'bans' in clients[clid]: + clients[clid]['bans'] += 1 + else: + clients[clid]['bans'] = 1 + + +def add_kick(clid, nick): + check_client(clid, nick) + if 'kicks' in clients[clid]: + clients[clid]['kicks'] += 1 + else: + clients[clid]['kicks'] = 1 + + +def add_pkick(clid, nick): + check_client(clid, nick) + if 'pkicks' in clients[clid]: + clients[clid]['pkicks'] += 1 + else: + clients[clid]['pkicks'] = 1 + + +def increase_onlinetime(clid, onlinetime): + if 'onlinetime' in clients[clid]: + clients[clid]['onlinetime'] += onlinetime + else: + clients[clid]['onlinetime'] = onlinetime + + +def check_client(clid, nick): + if clid not in clients: + clients[clid] = {} + clients[clid]['nick'] = nick + + +with open(log_path, 'r') as f: + today = datetime.utcnow() + for line in f: + parts = line.split('|') + logdatetime = datetime.strptime(parts[0], '%Y-%m-%d %H:%M:%S.%f') + sid = int(parts[3].strip()) + data = '|'.join(parts[4:]).strip() + if data.startswith('client'): + r = cldata.findall(data)[0] + nick = r[0] + id = r[1] + if data.startswith('client connected'): + add_connect(id, nick, logdatetime) + elif data.startswith('client disconnected'): + add_disconnect(id, nick, logdatetime) + if 'invokerid' in data: + add_pkick(id, nick) + r = cldata_invoker.findall(data)[0] + nick = r[1] + id = r[0] + add_kick(id, nick) + elif data.startswith('ban added') and 'cluid' in data: + r = cldata_ban.findall(data)[0] + nick = r[0] + id = r[1] + add_ban(id, nick) + +for clid in clients: + if 'connected' not in clients[clid]: + clients[clid]['connected'] = False + if clients[clid]['connected']: + add_disconnect(clid, clients[clid]['nick'], today) + + +# helper functions +def desc(key): + r = [] + values = {} + for clid in clients: + if key in clients[clid]: + values[clid] = clients[clid][key] + for clid in sorted(values, key=values.get, reverse=True): + value = values[clid] + r.append((clients[clid]['nick'], value)) + return r + + +################# +# Generate HTML # +################# +output = [] +# head +output.append(''' + + + TeamspeakStats + + + + + + + + + +''') +# body +if len(clients) < 1: + print('No clients found!') + print('Keine Daten gefunden!') + import sys + sys.exit() + +onlinetime_desc = desc('onlinetime') +if len(onlinetime_desc) >= 1: + output.append('

Onlinezeit

') + output.append('') + +kicks_desc = desc('kicks') +if len(kicks_desc) >= 1: + output.append('

Kicks

') + output.append('') + +pkicks_desc = desc('pkicks') +if len(pkicks_desc) >= 1: + output.append('

Gekickt worden

') + output.append('') + +bans_desc = desc('bans') +if len(bans_desc) >= 1: + output.append('

Bans

') + output.append('') + +generation_end = datetime.now() +generation_delta = generation_end - generation_start +output.append('

generiert in {}.{} Sekunden am {}

'.format(generation_delta.seconds, generation_delta.microseconds, generation_end.strftime('%d.%m.%Y um %H:%M'))) +output.append('') + +with open(output_path, 'w+') as f: + f.write('\n'.join(output))