import re import sys 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 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] 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()