import time import sys import json import logging import pyscape import pyscape.utils import pyscape.ui import pyscape.game # Настройка логирования logging.basicConfig(level=logging.INFO, format="%(asctime)s %(levelname)s: %(message)s") def launch_game_with_token(token, id_token): """ Запускает игру с переданным токеном и id_token. """ try: id_token_payload = pyscape.parse_id_token(id_token) except Exception as e: raise e # Получаем данные пользователя по sub из id_token sub = id_token_payload["sub"] user = pyscape.get_user_details(sub, token["access_token"]) logging.info("You are logged in as: {}#{}".format(user.display_name, user.suffix)) # Проверка поля login_provider в JWT if ( "login_provider" not in id_token_payload or id_token_payload["login_provider"] != pyscape.LOGIN_PROVIDER ): logging.info( "No known login_provider found in JWT token, using standard login..." ) code, new_id_token = pyscape.standard_login(id_token) # tup предполагается как кортеж, где второй элемент – обновлённый id_token pyscape.utils.write_to_config_file(new_id_token, "id_token") session_id = pyscape.get_game_session(new_id_token) accounts = pyscape.get_accounts(session_id) logging.info("Found {} accounts".format(len(accounts))) try: account = pyscape.ui.get_chosen_account(user, accounts) except pyscape.NoAccountChosenError: logging.info("No account was chosen") return pyscape.game.launch_game(session_id, account) def handle_social_auth(payload, refresh: bool = False): """ Обрабатывает social_auth: обменивает код на токен, сохраняет данные и запускает игру. """ if not refresh: try: token = pyscape.exchange(payload["code"]) except Exception as e: raise e else: print("REFRESH") token = payload # Если метод extra недоступен, возможно, токен представлен как словарь id_token = token.get("id_token") if not id_token: raise Exception("id_token not found in token response") pyscape.utils.write_to_config_file(id_token, "id_token") # Вычисляем время истечения токена как текущее время + время жизни токена (в секундах) expiry = int(time.time() + token["expires_in"] * 1000) token["expiry"] = expiry # Сериализуем OAuth токен в JSON token_json = json.dumps(token) pyscape.utils.write_to_config_file(token_json, "token") launch_game_with_token(token, id_token) def get_cached_tokens(): """ Читает сохранённые токены из конфигурационного файла. Если срок действия токена истёк (или почти истёк), возбуждает исключение. """ token_json = pyscape.utils.read_from_config_file("token") try: token = json.loads(token_json) except json.JSONDecodeError as e: raise Exception("Invalid token JSON") from e import datetime current_time = datetime.datetime.now() # Если до истечения токена осталось менее 30 минут, считаем его недействительным. if ( datetime.datetime.fromtimestamp(token["expiry"]) - current_time ) <= datetime.timedelta(minutes=30): raise Exception("cached token has expired") id_token = pyscape.utils.read_from_config_file("id_token") return token, id_token def handle_regular_launch(): """ Пытается использовать сохранённые токены для запуска игры, иначе инициирует стандартный процесс аутентификации. """ try: token, id_token = get_cached_tokens() try: refresh_result = pyscape.refresh_token(token["refresh_token"]) handle_social_auth(refresh_result, True) except Exception as e: print(str(e)) pyscape.login() return except Exception as e: logging.info( f"Could not load cached tokens, initiating regular login flow: {e}" ) pyscape.login() return logging.info("Loaded cached tokens") try: launch_game_with_token(token, id_token) except Exception as e: print(e) logging.info( "Could not login with cached tokens, initiating regular login flow" ) pyscape.login() def main(): """ Основная функция приложения. Если аргументы командной строки не заданы – используется регулярный запуск, иначе выполняется разбор intent из payload. """ # Если отсутствуют дополнительные аргументы, инициируем регулярный запуск. if len(sys.argv) < 2: try: handle_regular_launch() except Exception as e: logging.error(e) sys.exit(1) sys.exit(0) # Если передан аргумент, то он рассматривается как payload для intent. payload = pyscape.utils.parse_intent_payload(sys.argv[1]) if "intent" not in payload: logging.fatal("No intent found in payload: {}".format(payload)) sys.exit(1) if payload["intent"] == "social_auth": try: handle_social_auth(payload) except Exception as e: pyscape.utils.die(e) raise input("Press Enter to exit...") if __name__ == "__main__": main()