#!/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 import logging 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(timestamp, format="%d.%m.%Y"): return datetime.datetime.fromtimestamp(timestamp).strftime(format) def display_age(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]) app.jinja_env.filters["display_time"] = display_time app.jinja_env.filters["display_age"] = display_age 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", ], current_time=time.time(), ) @app.route("/prematefinder", methods=["GET", "POST"]) def premates(): steamids = [] prematedata = 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 prematedata = steam.getDataForPrematefinder(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 prematedata[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( "premates_html.jinja", steamids=steamids, profiles=prematedata, current_time=time.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, ) if __name__ == "__main__": # print(steam.getFriends("76561197963063991")) # print(steam.getFriends("76561197963882989")) logging.basicConfig(filename="./main.log", level=logging.INFO) 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