TeamspeakStats/tsstats.py

234 lines
6.9 KiB
Python
Executable File

import re
import sys
import glob
import json
import configparser
from os.path import exists
from telnetlib import Telnet
from time import mktime, sleep
from datetime import datetime, timedelta
from jinja2 import Environment, FileSystemLoader
def exit(error):
print('FATAL ERROR:', error)
import sys
sys.exit(1)
# get path
arg = sys.argv[0]
arg_find = arg.rfind('/')
if arg_find == -1:
path = '.'
else:
path = arg[:arg_find]
path += '/'
config_path = path + 'config.ini'
id_map_path = path + 'id_map.json'
# exists config-file
if not exists(config_path):
exit('Couldn\'t find config-file at {}'.format(config_path))
# parse config
config = configparser.ConfigParser()
config.read(config_path)
# check keys
if 'General' not in config or 'HTML' not in config:
exit('Invalid config!')
general = config['General']
html = config['HTML']
if ('logfile' not in general or 'outputfile' not in general) or ('title' not in html):
exit('Invalid config!')
log_path = general['logfile']
if not exists(log_path):
exit('Couldn\'t access log-file!')
output_path = general['outputfile']
title = html['title']
show_onlinetime = html.get('onlinetime', True)
show_kicks = html.get('kicks', True)
show_pkicks = html.get('pkicks', True)
show_bans = html.get('bans', True)
show_pbans = html.get('pbans', True)
if exists(id_map_path):
# read id_map
id_map = json.load(open(path + 'id_map.json'))
else:
id_map = {}
generation_start = datetime.now()
clients = {} # clid: {'nick': ..., 'onlinetime': ..., 'kicks': ..., 'pkicks': ..., 'bans': ..., 'last_connect': ..., 'connected': ...}
kicks = {}
cldata = re.compile(r"'(.*)'\(id:(\d*)\)")
cldata_ban = re.compile(r"by\ client\ '(.*)'\(id:(\d*)\)")
cldata_invoker = re.compile(r"invokerid=\d*\ invokername=(.*)\ invokeruid=(.*)\ reasonmsg")
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, set_connected=True):
check_client(clid, nick)
connect = datetime.fromtimestamp(clients[clid]['last_connect'])
delta = logdatetime - connect
minutes = delta.seconds // 60
increase_onlinetime(clid, minutes)
if set_connected:
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_pban(clid, nick):
check_client(clid, nick)
if 'pbans' in clients[clid]:
clients[clid]['pbans'] += 1
else:
clients[clid]['pbans'] = 1
####
#
#
# TODO
#
#
###
def add_kick(cluid, nick):
if cluid not in kicks:
kicks[cluid] = {}
if 'kicks' in kicks[cluid]:
kicks[cluid]['kicks'] += 1
else:
kicks[cluid]['kicks'] = 1
kicks[cluid]['nick'] = nick
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
log_files = [file_name for file_name in glob.glob(log_path)]
log_lines = []
for log_file in log_files:
for line in open(log_file, 'r'):
log_lines.append(line)
today = datetime.utcnow()
for line in log_lines:
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]
clid = r[1]
if clid in id_map:
clid = id_map[clid]
if data.startswith('client connected'):
add_connect(clid, nick, logdatetime)
elif data.startswith('client disconnected'):
add_disconnect(clid, nick, logdatetime)
if 'bantime' in data:
add_pban(clid, nick)
elif 'invokerid' in data:
add_pkick(clid, nick)
r = cldata_invoker.findall(data)[0]
nick = r[0]
cluid = r[1]
add_kick(cluid, nick)
elif data.startswith('ban added') and 'cluid' in data:
r = cldata_ban.findall(data)[0]
nick = r[0]
clid = r[1]
add_ban(clid, 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, set_connected=False)
generation_end = datetime.now()
generation_delta = generation_end - generation_start
# helper functions
def desc(key, data_dict=clients):
r = []
values = {}
for clid in data_dict:
if key in data_dict[clid]:
values[clid] = data_dict[clid][key]
for clid in sorted(values, key=values.get, reverse=True):
value = values[clid]
r.append((clid, data_dict[clid]['nick'], value))
return r
def render_template():
env = Environment(loader=FileSystemLoader(path))
template = env.get_template('template.html')
# format onlinetime
onlinetime_desc = desc('onlinetime')
for idx, (clid, nick, onlinetime) in enumerate(onlinetime_desc):
if onlinetime > 60:
onlinetime_str = str(onlinetime // 60) + 'h'
m = onlinetime % 60
if m > 0:
onlinetime_str += ' ' + str(m) + 'm'
else:
onlinetime_str = str(onlinetime) + 'm'
onlinetime_desc[idx] = (clid, nick, onlinetime_str, clients[clid]['connected'])
kicks_desc = desc('kicks', data_dict=kicks)
pkicks_desc = desc('pkicks')
bans_desc = desc('bans')
pbans_desc = desc('pbans')
show_kicks = len(kicks_desc) > 0
show_pkicks = len(pkicks_desc) > 0
show_bans = len(bans_desc) > 0
show_pbans = len(pbans_desc) > 0
with open(output_path, 'w+') as f:
f.write(template.render(title=title, onlinetime=onlinetime_desc, kicks=kicks_desc, pkicks=pkicks_desc, bans=bans_desc, pbans=pbans_desc, seconds='{}.{}'.format(generation_delta.seconds, generation_delta.microseconds),
date=generation_end.strftime('%d.%m.%Y %H:%M'),
show_onlinetime=show_onlinetime,
show_kicks=show_kicks,
show_pkicks=show_pkicks,
show_bans=show_bans,
show_pbans=show_pbans))
if len(clients) < 1:
print('Not enough data!')
else:
render_template()