Skip to content

Commit

Permalink
version 0.1
Browse files Browse the repository at this point in the history
  • Loading branch information
FriendsOfGalaxy committed Oct 17, 2019
1 parent 40647f6 commit 99ac6c3
Show file tree
Hide file tree
Showing 11 changed files with 452 additions and 0 deletions.
3 changes: 3 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[pytest]
python_paths = src
addopts = src tests --flakes
4 changes: 4 additions & 0 deletions requirements/app.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
galaxy.plugin.api==0.53
python-dateutil==2.8.0
requests==2.21.0
psutil==5.6.1
9 changes: 9 additions & 0 deletions requirements/dev.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
-r app.txt
invoke==1.2.0
pytest==4.3.0
pytest-flakes==4.0.0
pytest-pythonpath==0.7.3
pytest-asyncio==0.10.0
pytest-mock==1.10.4
pip-tools==3.6.1
aiohttp==3.5.4
48 changes: 48 additions & 0 deletions src/backend.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import logging as log
import asyncio


class ParadoxClient:
def __init__(self, http_client):
self.http_client = http_client
self.paradox_launcher_skus = None

async def prepare_sku(self):
data = {'token': self.http_client.token}
log.info('Starting skus retrieve')
response = await self.http_client.do_request('GET', 'https://accounts.paradoxplaza.com/api/skus', headers=data)
response = await response.json()
log.info('Finished skus retrieve')
paradox_launcher_skus = set()
for sku in response:
await asyncio.sleep(0.01)
if 'paradoxLauncher' in response[sku]['platform']:
paradox_launcher_skus.add(sku)
self.paradox_launcher_skus = paradox_launcher_skus

async def get_account_id(self):
data = {'Authorization': f'{{"session":{{"token":"{self.http_client.token}"}}}}',
'content-type': 'application/json'}
response = await self.http_client.do_request('GET', 'https://api.paradox-interactive.com/accounts', headers=data)
response = await response.json()
return response['id']

async def get_owned_games(self):
data = {'Authorization': f'{{"session":{{"token":"{self.http_client.token}"}}}}',
'content-type': 'application/json'}
response = await self.http_client.do_request('GET', 'https://api.paradox-interactive.com/inventory/products',
headers=data)

response = await response.json()
owned_products = []
if 'products' in response:
for platforms in response['products']:
for platform in platforms:
for game in platforms[platform]:
log.info(game)
if game['sku'] and game['title'] and game['product_type']:
owned_products.append({'sku': game['sku'],
'title': game['title'],
'type': game['product_type']})
log.info(owned_products)
return owned_products
23 changes: 23 additions & 0 deletions src/consts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@

import re


AUTH_URL = r"https://accounts.paradoxplaza.com/login"
AUTH_REDIRECT_URL = r"api/accounts/connections/"

REGISTRY_LAUNCHER_PATH = r"SOFTWARE\WOW6432Node\Paradox Interactive\Paradox Launcher\LauncherPath"
PARADOX_LAUNCHER_EXE = "Paradox Launcher.exe"


def regex_pattern(regex):
return ".*" + re.escape(regex) + ".*"

AUTH_PARAMS = {
"window_title": "Login to Paradox\u2122",
"window_width": 700,
"window_height": 800,
"start_uri": AUTH_URL,
"end_uri_regex": regex_pattern(AUTH_REDIRECT_URL)
}


74 changes: 74 additions & 0 deletions src/http_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@

from galaxy.http import HttpClient

import aiohttp
import logging as log
from yarl import URL
import pickle

class CookieJar(aiohttp.CookieJar):
def __init__(self):
super().__init__()
self._cookies_updated_callback = None

def set_cookies_updated_callback(self, callback):
self._cookies_updated_callback = callback

def update_cookies(self, cookies, url=URL()):
super().update_cookies(cookies, url)
if cookies and self._cookies_updated_callback:
self._cookies_updated_callback(list(self))


class AuthenticatedHttpClient(HttpClient):

def __init__(self, store_credentials):
self._store_credentials = store_credentials
self.token = None

self.bearer = None
self.user = None
self._cookie_jar = CookieJar()
self._auth_lost_callback = None

super().__init__(cookie_jar=self._cookie_jar)

def set_cookies_updated_callback(self, callback):
self._cookie_jar.set_cookies_updated_callback(callback)

def update_cookies(self, cookies):
self._cookie_jar.update_cookies(cookies)

def set_auth_lost_callback(self, callback):
self._auth_lost_callback = callback


def get_credentials(self):
creds = {}
creds['cookie_jar'] = pickle.dumps([c for c in self._cookie_jar]).hex()
return creds

async def do_request(self, method, *args, **kwargs):
try:
return await self.request(method, *args, **kwargs)
except Exception as e:
log.warning(f"Request failed with {repr(e)}, attempting to refresh credentials")
#await self.refresh_credentials()
return await self.request(method, *args, **kwargs)

def authenticate_with_cookies(self, cookies):
cookiez = {}
for cookie in cookies:
if 'value' in cookie:
cookiez[cookie['name']] = cookie['value']
else:
cookiez[cookie.key] = cookie.value
self.update_cookies(cookiez)
self.token = cookiez['SESSION_TOKEN']
self._store_credentials(self.get_credentials())






85 changes: 85 additions & 0 deletions src/local.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import sys
if sys.platform == 'win32':
import winreg

import os
import logging as log
import asyncio
from consts import REGISTRY_LAUNCHER_PATH, PARADOX_LAUNCHER_EXE
from dataclasses import dataclass
from galaxy.proc_tools import process_iter, ProcessInfo

@dataclass
class RunningGame:
name: str
process: ProcessInfo


class LocalClient(object):
def __init__(self):
self._local_client_path = None
self._local_client_exe = None
self._local_games_path = None


@property
def installed(self):
if self._local_client_exe and os.access(self._local_client_exe, os.F_OK):
return True
else:
self.refresh_local_client_state()
return self._local_client_exe and os.access(self._local_client_exe, os.F_OK)

@property
def local_client_exe(self):
if self.installed:
return self._local_client_exe

@property
def local_client_path(self):
if self.installed:
return self._local_client_path

@property
def bootstraper_exe(self):
if self.installed:
paradox_root = self._local_client_path[:-len('\\launcher')]
bootstrapper = os.path.join(paradox_root, 'bootstrapper')
return os.path.join(bootstrapper,'Bootstrapper.exe')

@property
def games_path(self):
if self.installed:
if self._local_games_path:
return self._local_games_path
else:
paradox_root = self._local_client_path[:-len('\\launcher')]
paradox_games = os.path.join(paradox_root, 'games')
if not os.access(paradox_games, os.F_OK):
return None
self._local_games_path = paradox_games
return paradox_games

def refresh_local_client_state(self):
if sys.platform == 'win32':
try:
with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE,REGISTRY_LAUNCHER_PATH, 0, winreg.KEY_READ) as key:
local_client_path = winreg.QueryValueEx(key, "Path")[0]
local_client_exe = os.path.join(local_client_path, PARADOX_LAUNCHER_EXE)
self._local_client_path = local_client_path
self._local_client_exe = local_client_exe
except OSError:
self._local_client_exe = self._local_client_path = self._local_games_path = None

async def get_running_game(self, games, proc_iter_interval=0.05):
if not games:
return
for process_info in process_iter():
try:
await asyncio.sleep(proc_iter_interval)
for game in games:
if process_info.binary_path.lower() == games[game].lower():
log.info(f"Found a running game! {game}")
return RunningGame(name=game, process=process_info)
except:
continue
Loading

0 comments on commit 99ac6c3

Please sign in to comment.