import re import sys import json import configparser from time import mktime from os.path import exists 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) 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': ...} 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, 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_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] clid = r[1] #print(clid, nick, sep=' | ') 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 'invokerid' in data: add_pkick(clid, nick) r = cldata_invoker.findall(data)[0] nick = r[1] clid = r[0] if clid in id_map: clid = id_map[clid] add_kick(clid, 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): 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((clid, clients[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']) with open(output_path, 'w+') as f: f.write(template.render(title=title, onlinetime=onlinetime_desc, kicks=desc('kicks'), pkicks=desc('pkicks'), bans=desc('bans'), seconds='{}.{}'.format(generation_delta.seconds, generation_delta.microseconds), date=generation_end.strftime('%d.%m.%Y um %H:%M'), show_onlinetime=show_onlinetime, show_kicks=show_kicks, show_pkicks=show_pkicks, show_bans=show_bans)) if len(clients) < 1: print('Not enough data!') else: render_template()