Skip to content

Commit f746eb1

Browse files
version 0.31
1 parent 89d4428 commit f746eb1

File tree

7 files changed

+206
-7
lines changed

7 files changed

+206
-7
lines changed

src/backend.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -383,3 +383,65 @@ def parse_timestamp(product_info_xml) -> Timestamp:
383383
except (ET.ParseError, AttributeError, ValueError):
384384
logging.exception("Can not parse backend response: %s", await response.text())
385385
raise UnknownBackendResponse()
386+
387+
async def get_favorite_games(self, user_id):
388+
response = await self._http_client.get("{base_api}/atom/users/{user_id}/privacySettings/FAVORITEGAMES".format(
389+
base_api=self._get_api_host(),
390+
user_id=user_id
391+
))
392+
393+
'''
394+
<?xml version="1.0" encoding="UTF-8"?>
395+
<privacySettings>
396+
<privacySetting>
397+
<userId>1008620950926</userId>
398+
<category>FAVORITEGAMES</category>
399+
<payload>OFB-EAST:48217;OFB-EAST:109552409;DR:119971300</payload>
400+
</privacySetting>
401+
</privacySettings>
402+
'''
403+
404+
try:
405+
content = await response.text()
406+
payload_xml = ET.ElementTree(ET.fromstring(content)).find("privacySetting/payload")
407+
if payload_xml is None or payload_xml.text is None:
408+
# No games tagged, if on object evaluates to false
409+
return []
410+
411+
favorite_games = set(payload_xml.text.split(';'))
412+
413+
return favorite_games
414+
except (ET.ParseError, AttributeError, ValueError):
415+
logging.exception("Can not parse backend response: %s", await response.text())
416+
raise UnknownBackendResponse()
417+
418+
async def get_hidden_games(self, user_id):
419+
response = await self._http_client.get("{base_api}/atom/users/{user_id}/privacySettings/HIDDENGAMES".format(
420+
base_api=self._get_api_host(),
421+
user_id=user_id
422+
))
423+
424+
'''
425+
<?xml version="1.0" encoding="UTF-8"?>
426+
<privacySettings>
427+
<privacySetting>
428+
<userId>1008620950926</userId>
429+
<category>HIDDENGAMES</category>
430+
<payload>1.0|OFB-EAST:109552409;OFB-EAST:109552409</payload>
431+
</privacySetting>
432+
</privacySettings>
433+
'''
434+
435+
try:
436+
content = await response.text()
437+
payload_xml = ET.ElementTree(ET.fromstring(content)).find("privacySetting/payload")
438+
if payload_xml is None or payload_xml.text is None:
439+
# No games tagged, if on object evaluates to false
440+
return []
441+
payload_text = payload_xml.text.replace('1.0|', '')
442+
hidden_games = set(payload_text.split(';'))
443+
444+
return hidden_games
445+
except (ET.ParseError, AttributeError, ValueError):
446+
logging.exception("Can not parse backend response: %s", await response.text())
447+
raise UnknownBackendResponse()

src/plugin.py

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,13 @@
1515
AccessDenied, AuthenticationRequired, InvalidCredentials, UnknownBackendResponse, UnknownError
1616
)
1717
from galaxy.api.plugin import create_and_run_plugin, Plugin
18-
from galaxy.api.types import Achievement, Authentication, FriendInfo, Game, GameTime, LicenseInfo, NextStep
18+
from galaxy.api.types import Achievement, Authentication, FriendInfo, Game, GameTime, LicenseInfo, NextStep, GameLibrarySettings
1919

2020
from backend import AuthenticatedHttpClient, MasterTitleId, OfferId, OriginBackendClient, Timestamp
2121
from local_games import get_local_content_path, LocalGames
2222
from uri_scheme_handler import is_uri_handler_installed
2323
from version import __version__
24-
24+
import re
2525

2626
def is_windows():
2727
return platform.system().lower() == "windows"
@@ -38,6 +38,14 @@ def is_windows():
3838
"&redirect_uri=https://www.origin.com/views/login.html",
3939
"end_uri_regex": r"^https://www\.origin\.com/views/login\.html.*"
4040
}
41+
def regex_pattern(regex):
42+
return ".*" + re.escape(regex) + ".*"
43+
44+
JS = {regex_pattern(r"originX/login?execution"): [
45+
r'''
46+
document.getElementById("rememberMe").click();
47+
'''
48+
]}
4149

4250
MultiplayerId = NewType("MultiplayerId", str)
4351
AchievementsImportContext = namedtuple("AchievementsImportContext", ["owned_games", "achievements"])
@@ -97,7 +105,7 @@ async def authenticate(self, stored_credentials=None):
97105
stored_cookies = stored_credentials.get("cookies") if stored_credentials else None
98106

99107
if not stored_cookies:
100-
return NextStep("web_session", AUTH_PARAMS)
108+
return NextStep("web_session", AUTH_PARAMS,js=JS)
101109

102110
return await self._do_authenticate(stored_cookies)
103111

@@ -111,7 +119,6 @@ async def get_owned_games(self):
111119
self._check_authenticated()
112120

113121
owned_offers = await self._get_owned_offers()
114-
115122
games = []
116123
for offer in owned_offers:
117124
game = Game(
@@ -304,6 +311,26 @@ async def get_game_time(self, game_id: OfferId, last_played_games: Any) -> GameT
304311
logging.exception("Failed to import game times")
305312
raise UnknownBackendResponse(str(e))
306313

314+
async def prepare_game_library_settings_context(self, game_ids: List[str]) -> Any:
315+
self._check_authenticated()
316+
hidden_games = await self._backend_client.get_hidden_games(self._user_id)
317+
favorite_games = await self._backend_client.get_favorite_games(self._user_id)
318+
319+
library_context = {}
320+
for game_id in game_ids:
321+
library_context[game_id] = {'hidden': game_id in hidden_games, 'favorite': game_id in favorite_games}
322+
return library_context
323+
324+
async def get_game_library_settings(self, game_id: str, context: Any) -> GameLibrarySettings:
325+
if not context:
326+
# Unable to retrieve context
327+
return GameLibrarySettings(game_id, None, None)
328+
game_library_settings = context.get(game_id)
329+
if game_library_settings is None:
330+
# Able to retrieve context but game is not in its values -> It doesnt have any tags or hidden status set
331+
return GameLibrarySettings(game_id, [], False)
332+
return GameLibrarySettings(game_id, ['favorite'] if game_library_settings['favorite'] else [], game_library_settings['hidden'])
333+
307334
def game_times_import_complete(self):
308335
if self._persistent_cache_updated:
309336
self.push_cache()

src/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "0.30"
1+
__version__ = "0.31"

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,8 @@ def backend_client():
6969
mock.get_owned_games = AsyncMock()
7070
mock.get_friends = AsyncMock()
7171
mock.get_lastplayed_games = MagicMock()
72+
mock.get_hidden_games = AsyncMock()
73+
mock.get_favorite_games = AsyncMock()
7274

7375
return mock
7476

tests/integration_test.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ def test_integration():
5656
"ImportOwnedGames",
5757
"ImportAchievements",
5858
"ImportInstalledGames",
59+
"ImportGameLibrarySettings",
5960
"LaunchGame",
6061
"InstallGame",
6162
"ShutdownPlatformClient",

tests/test_authentication.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from galaxy.api.types import Authentication, NextStep
66

7-
from plugin import AUTH_PARAMS
7+
from plugin import AUTH_PARAMS, JS
88

99

1010
def test_no_stored_credentials(plugin, http_client, backend_client):
@@ -22,7 +22,7 @@ def test_no_stored_credentials(plugin, http_client, backend_client):
2222

2323
with patch.object(plugin, "store_credentials") as store_credentials:
2424
result = loop.run_until_complete(plugin.authenticate())
25-
assert result == NextStep("web_session", AUTH_PARAMS)
25+
assert result == NextStep("web_session", AUTH_PARAMS, js=JS)
2626

2727
credentials = {
2828
"cookies": cookies,
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
import pytest
2+
from galaxy.api.errors import AuthenticationRequired
3+
from galaxy.api.types import GameLibrarySettings
4+
from galaxy.unittest.mock import async_return_value
5+
6+
GAME_IDS = ['DR:119971300', 'OFB-EAST:48217', 'OFB-EAST:109552409', 'Origin.OFR.50.0002694']
7+
8+
BACKEND_HIDDEN_RESPONSE = '''
9+
<?xml version="1.0" encoding="UTF-8"?>
10+
<privacySettings>
11+
<privacySetting>
12+
<userId>1008620950926</userId>
13+
<category>HIDDENGAMES</category>
14+
<payload>Origin.OFR.50.0002694;OFB-EAST:109552409</payload>
15+
</privacySetting>
16+
</privacySettings>
17+
'''
18+
19+
BACKEND_FAVORITES_RESPONSE = '''
20+
<?xml version="1.0" encoding="UTF-8"?>
21+
<privacySettings>
22+
<privacySetting>
23+
<userId>1008620950926</userId>
24+
<category>FAVORITEGAMES</category>
25+
<payload>OFB-EAST:48217;OFB-EAST:109552409;DR:119971300</payload>
26+
</privacySetting>
27+
</privacySettings>
28+
'''
29+
30+
FAVORITE_GAMES = {'OFB-EAST:48217', 'OFB-EAST:109552409', 'DR:119971300'}
31+
HIDDEN_GAMES = {'Origin.OFR.50.0002694', 'OFB-EAST:109552409'}
32+
33+
GAME_LIBRARY_CONTEXT = {
34+
'OFB-EAST:48217': {
35+
'hidden': False,
36+
'favorite': True
37+
},
38+
'OFB-EAST:109552409': {
39+
'hidden': True,
40+
'favorite': True
41+
},
42+
'DR:119971300': {
43+
'hidden': False,
44+
'favorite': True
45+
},
46+
'Origin.OFR.50.0002694': {
47+
'hidden': True,
48+
'favorite': False
49+
}
50+
}
51+
52+
GAME_LIBRARY_SETTINGS = GameLibrarySettings('OFB-EAST:48217', ['favorite'], False)
53+
54+
55+
@pytest.mark.asyncio
56+
async def test_not_authenticated(plugin, http_client):
57+
http_client.is_authenticated.return_value = False
58+
with pytest.raises(AuthenticationRequired):
59+
await plugin.prepare_game_library_settings_context([])
60+
61+
62+
@pytest.mark.asyncio
63+
async def test_prepare_library_settings_context(
64+
authenticated_plugin,
65+
backend_client,
66+
user_id,
67+
68+
):
69+
backend_client.get_favorite_games.return_value = FAVORITE_GAMES
70+
backend_client.get_hidden_games.return_value = HIDDEN_GAMES
71+
72+
assert GAME_LIBRARY_CONTEXT == await authenticated_plugin.prepare_game_library_settings_context(GAME_IDS)
73+
74+
backend_client.get_favorite_games.assert_called_once_with(user_id)
75+
backend_client.get_hidden_games.assert_called_once_with(user_id)
76+
77+
78+
@pytest.mark.asyncio
79+
async def test_get_favorite_games(
80+
backend_client,
81+
user_id,
82+
http_client,
83+
):
84+
http_client.get.return_value = async_return_value(BACKEND_FAVORITES_RESPONSE)
85+
86+
assert FAVORITE_GAMES == await backend_client.get_favorite_games(user_id)
87+
88+
89+
@pytest.mark.asyncio
90+
async def test_get_hidden_games(
91+
backend_client,
92+
user_id,
93+
http_client,
94+
):
95+
http_client.get.return_value = async_return_value(BACKEND_HIDDEN_RESPONSE)
96+
97+
assert HIDDEN_GAMES == await backend_client.get_hidden_games(user_id)
98+
99+
100+
@pytest.mark.asyncio
101+
async def test_get_game_library_settings(
102+
authenticated_plugin,
103+
):
104+
assert GAME_LIBRARY_SETTINGS == await authenticated_plugin.get_game_library_settings('OFB-EAST:48217', GAME_LIBRARY_CONTEXT)
105+
106+
107+

0 commit comments

Comments
 (0)