#!/usr/bin/env python3 from flask import Flask, flash, redirect, render_template, request, session, url_for # from wsgiref.util import setup_testing_defaults from collections import OrderedDict from concurrent.futures import ThreadPoolExecutor import json import os import re import sys import time, datetime # import traceback from SteamAPI import SteamAPI from QueryServer import QueryServer # Set some directories THIS_DIR = os.path.dirname(os.path.abspath(__file__)) TEMPLATES = os.path.join(THIS_DIR, 'templates') ASSETS = os.path.join(THIS_DIR, 'assets') CONFIG = os.path.join(THIS_DIR, 'config') app = Flask(__name__) steam = None with open(os.path.join(CONFIG, 'secrets.json'), 'rt') as secretjson: secrets = json.load(secretjson) app.secret_key = secrets["flask_secret"] steam = SteamAPI(secrets["steam_token"]) ################################################## ## Helper functions ################################################## intervals = ( ('w', 604800), # 60 * 60 * 24 * 7 ('d', 86400), # 60 * 60 * 24 ('h', 3600), # 60 * 60 ('m', 60), ('s', 1), ) def display_time(seconds, granularity=2): result = [] for name, count in intervals: value = seconds // count if value: seconds -= value * count if value == 1: name = name.rstrip('s') result.append("{}{}".format(int(value), name)) return ' '.join(result[:granularity]) def grepSteamids(text): steamids = [] SteamIDfromText = re.findall(r'STEAM_\d:(\d):(\d+)', text) for steamid in SteamIDfromText: steam64id = 76561197960265728 + int(steamid[0]) + (int(steamid[1]) * 2) steamids.append(steam64id) return steamids ################################################## ## Functions for different pages ################################################## @app.route("/") def main(): return redirect(url_for('lobby')) @app.errorhandler(404) def not_found(e): return render_template('error.jinja', error = 'Die angeforderte Seite konnte nicht gefunden werden.') @app.route("/lobby") def lobby(): steamids = dict() friends = request.args.get('friends') if friends: friendList = friends.split(',') profiles = steam.getMultipleFriends(friendList) for steamid, profile in profiles.items(): for friendid in profile['friends']: if friendid in steamids: steamids[friendid]['main'] += ', ' + profile['personaname'] else: steamids[friendid] = {'main': profile['personaname']} else: # Load config (steamids, names) with open(os.path.join(CONFIG, 'lobby.json'), 'rt') as config: steamids = json.load(config) gamelist = steam.getGames() profiledata = steam.getProfiles(steamids.keys()) # Merge new data in loaded config for steamid in profiledata: if steamid not in steamids: continue for key, value in profiledata[steamid].items(): steamids[steamid][key] = value serverinfo = dict() for steamid, playerdata in steamids.items(): if 'gameid' not in playerdata \ or 'gameserverip' not in playerdata: continue if ':' not in playerdata['gameserverip']: continue gameserver = playerdata['gameserverip'] if gameserver not in serverinfo: ip, port = gameserver.split(':') port = int(port) gameid = playerdata['gameid'] # print('Query Server:', ip, port, gameid) server = QueryServer.QueryServer(ip, port, gameid) # print('Response:', server) if server: serverinfo[gameserver] = server # Sort steamids to be more appealing steamids = OrderedDict(sorted(steamids.items(), key = lambda player: player[1]['personaname'].lower())) steamids = OrderedDict(sorted(steamids.items(), reverse=True, key = lambda player: int(player[1]['lastlogoff']))) steamids = OrderedDict(sorted(steamids.items(), key = lambda player: (player[1]['personastate'] > 0) and player[1]['personastate'] or 10)) steamids = OrderedDict(sorted(steamids.items(), key = lambda player: ('gameid' in player[1] and player[1]['gameid'] or "zzz"))) return render_template('lobby_html.jinja', steamids = steamids, serverinfo = serverinfo, gamelist = gamelist, states = ['Offline', 'Online', 'Busy', 'Away', 'Snooze', 'Looking to trade', 'Looking to play'], display_time = display_time, current_time = time.time()) @app.route("/premadefinder", methods=['GET', 'POST']) def premades(): steamids = [] premadedata = dict() connections = set() if request.method == 'POST': postdata = request.form['statustext'] steamids = grepSteamids(postdata) steamids = [str(x) for x in steamids] if len(steamids) > 50: return render_template( 'error.jinja', error='Es sind maximal 50 Steamids erlaubt.' ) # Ask steam about profiles premadedata = steam.getDataForPremadefinder(steamids) # Add connection between friends. # Friends are always bidirectional, so we use set and min/max to avoid duplicates for steamid in steamids: for friend in premadedata[steamid]['_friends']: if friend in steamids: friend_a = min(steamid, friend) friend_b = max(steamid, friend) connections.add((friend_a, friend_b)) return render_template('premades_html.jinja', steamids=steamids, profiles=premadedata, display_time=display_time, connections=connections ) @app.route("/server", methods=['GET']) def server(): with open(os.path.join(CONFIG, 'server.json'), 'rt') as config: servers = json.load(config) executor = ThreadPoolExecutor(max_workers=10) serverdata_ = dict() for _, serverdata in servers.items(): name = "{0}{1:d}{2:d}".format(serverdata['ip'], serverdata['port'], serverdata['gameid']) serverdata_[name] = executor.submit( QueryServer.QueryServer, serverdata['ip'], serverdata['port'], serverdata['gameid'] ) for _, serverdata in servers.items(): name = "{0}{1:d}{2:d}".format(serverdata['ip'], serverdata['port'], serverdata['gameid']) serverdata['data'] = serverdata_[name].result() if serverdata['data']: serverdata_['u' + name] = executor.submit( steam.getGameUpdateState, serverdata['data']['gameid'], serverdata['data']['gameversion'], ) for _, serverdata in servers.items(): name = "{0}{1:d}{2:d}".format(serverdata['ip'], serverdata['port'], serverdata['gameid']) if 'u' + name in serverdata_: serverdata['update'] = serverdata_['u' + name].result() return render_template('server_html.jinja', servers=servers, display_time = display_time ) if __name__ == '__main__': # print(steam.getFriends("76561197963063991")) # print(steam.getFriends("76561197963882989")) app.config['TEMPLATES_AUTO_RELOAD'] = True app.run(threaded=True) # Changelog ########### # Internals changed to use Flask framework # Some design changes # ?friends=steamid,steamid,... # FLASK_APP=main.py FLASK_DEBUG=1 python -m flask run # TODO ###### # Better cache handling (Key + TTL) # Cache cleanup