mirror of
https://github.com/gogcom/galaxy-integrations-python-api.git
synced 2026-01-03 12:28:14 -05:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4d3c9b78c4 | ||
|
|
392e4c5f68 | ||
|
|
4d6d3b8eb2 | ||
|
|
d5610221a9 | ||
|
|
aa7b398d3b | ||
|
|
8d6ec500f9 |
2
setup.py
2
setup.py
@@ -2,7 +2,7 @@ from setuptools import setup, find_packages
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="galaxy.plugin.api",
|
name="galaxy.plugin.api",
|
||||||
version="0.48",
|
version="0.51",
|
||||||
description="GOG Galaxy Integrations Python API",
|
description="GOG Galaxy Integrations Python API",
|
||||||
author='Galaxy team',
|
author='Galaxy team',
|
||||||
author_email='galaxy@gog.com',
|
author_email='galaxy@gog.com',
|
||||||
|
|||||||
@@ -81,6 +81,16 @@ class Platform(Enum):
|
|||||||
NintendoDs = "nds"
|
NintendoDs = "nds"
|
||||||
Nintendo3Ds = "3ds"
|
Nintendo3Ds = "3ds"
|
||||||
PathOfExile = "pathofexile"
|
PathOfExile = "pathofexile"
|
||||||
|
Twitch = "twitch"
|
||||||
|
Minecraft = "minecraft"
|
||||||
|
GameSessions = "gamesessions"
|
||||||
|
Nuuvem = "nuuvem"
|
||||||
|
FXStore = "fxstore"
|
||||||
|
IndieGala = "indiegala"
|
||||||
|
Playfire = "playfire"
|
||||||
|
Oculus = "oculus"
|
||||||
|
Test = "test"
|
||||||
|
|
||||||
|
|
||||||
class Feature(Enum):
|
class Feature(Enum):
|
||||||
"""Possible features that can be implemented by an integration.
|
"""Possible features that can be implemented by an integration.
|
||||||
|
|||||||
@@ -18,6 +18,17 @@ class JsonRpcError(Exception):
|
|||||||
def __eq__(self, other):
|
def __eq__(self, other):
|
||||||
return self.code == other.code and self.message == other.message and self.data == other.data
|
return self.code == other.code and self.message == other.message and self.data == other.data
|
||||||
|
|
||||||
|
def json(self):
|
||||||
|
obj = {
|
||||||
|
"code": self.code,
|
||||||
|
"message": self.message
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.data is not None:
|
||||||
|
obj["error"]["data"] = self.data
|
||||||
|
|
||||||
|
return obj
|
||||||
|
|
||||||
class ParseError(JsonRpcError):
|
class ParseError(JsonRpcError):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
super().__init__(-32700, "Parse error")
|
super().__init__(-32700, "Parse error")
|
||||||
@@ -232,15 +243,9 @@ class Server():
|
|||||||
response = {
|
response = {
|
||||||
"jsonrpc": "2.0",
|
"jsonrpc": "2.0",
|
||||||
"id": request_id,
|
"id": request_id,
|
||||||
"error": {
|
"error": error.json()
|
||||||
"code": error.code,
|
|
||||||
"message": error.message
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if error.data is not None:
|
|
||||||
response["error"]["data"] = error.data
|
|
||||||
|
|
||||||
self._send(response)
|
self._send(response)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
|||||||
@@ -174,7 +174,6 @@ class Plugin:
|
|||||||
async def run(self):
|
async def run(self):
|
||||||
"""Plugin's main coroutine."""
|
"""Plugin's main coroutine."""
|
||||||
await self._server.run()
|
await self._server.run()
|
||||||
await self._external_task_manager.wait()
|
|
||||||
|
|
||||||
def close(self) -> None:
|
def close(self) -> None:
|
||||||
if not self._active:
|
if not self._active:
|
||||||
@@ -332,10 +331,7 @@ class Plugin:
|
|||||||
def _game_achievements_import_failure(self, game_id: str, error: ApplicationError) -> None:
|
def _game_achievements_import_failure(self, game_id: str, error: ApplicationError) -> None:
|
||||||
params = {
|
params = {
|
||||||
"game_id": game_id,
|
"game_id": game_id,
|
||||||
"error": {
|
"error": error.json()
|
||||||
"code": error.code,
|
|
||||||
"message": error.message
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self._notification_client.notify("game_achievements_import_failure", params)
|
self._notification_client.notify("game_achievements_import_failure", params)
|
||||||
|
|
||||||
@@ -399,10 +395,7 @@ class Plugin:
|
|||||||
def _game_time_import_failure(self, game_id: str, error: ApplicationError) -> None:
|
def _game_time_import_failure(self, game_id: str, error: ApplicationError) -> None:
|
||||||
params = {
|
params = {
|
||||||
"game_id": game_id,
|
"game_id": game_id,
|
||||||
"error": {
|
"error": error.json()
|
||||||
"code": error.code,
|
|
||||||
"message": error.message
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
self._notification_client.notify("game_time_import_failure", params)
|
self._notification_client.notify("game_time_import_failure", params)
|
||||||
|
|
||||||
|
|||||||
98
src/galaxy/registry_monitor.py
Normal file
98
src/galaxy/registry_monitor.py
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
import platform
|
||||||
|
if platform.system().lower() == "windows":
|
||||||
|
import logging
|
||||||
|
import ctypes
|
||||||
|
from ctypes.wintypes import LONG, HKEY, LPCWSTR, DWORD, BOOL, HANDLE, LPVOID
|
||||||
|
|
||||||
|
LPSECURITY_ATTRIBUTES = LPVOID
|
||||||
|
|
||||||
|
RegOpenKeyEx = ctypes.windll.advapi32.RegOpenKeyExW
|
||||||
|
RegOpenKeyEx.restype = LONG
|
||||||
|
RegOpenKeyEx.argtypes = [HKEY, LPCWSTR, DWORD, DWORD, ctypes.POINTER(HKEY)]
|
||||||
|
|
||||||
|
RegCloseKey = ctypes.windll.advapi32.RegCloseKey
|
||||||
|
RegCloseKey.restype = LONG
|
||||||
|
RegCloseKey.argtypes = [HKEY]
|
||||||
|
|
||||||
|
RegNotifyChangeKeyValue = ctypes.windll.advapi32.RegNotifyChangeKeyValue
|
||||||
|
RegNotifyChangeKeyValue.restype = LONG
|
||||||
|
RegNotifyChangeKeyValue.argtypes = [HKEY, BOOL, DWORD, HANDLE, BOOL]
|
||||||
|
|
||||||
|
CloseHandle = ctypes.windll.kernel32.CloseHandle
|
||||||
|
CloseHandle.restype = BOOL
|
||||||
|
CloseHandle.argtypes = [HANDLE]
|
||||||
|
|
||||||
|
CreateEvent = ctypes.windll.kernel32.CreateEventW
|
||||||
|
CreateEvent.restype = BOOL
|
||||||
|
CreateEvent.argtypes = [LPSECURITY_ATTRIBUTES, BOOL, BOOL, LPCWSTR]
|
||||||
|
|
||||||
|
WaitForSingleObject = ctypes.windll.kernel32.WaitForSingleObject
|
||||||
|
WaitForSingleObject.restype = DWORD
|
||||||
|
WaitForSingleObject.argtypes = [HANDLE, DWORD]
|
||||||
|
|
||||||
|
ERROR_SUCCESS = 0x00000000
|
||||||
|
|
||||||
|
KEY_READ = 0x00020019
|
||||||
|
KEY_QUERY_VALUE = 0x00000001
|
||||||
|
|
||||||
|
REG_NOTIFY_CHANGE_NAME = 0x00000001
|
||||||
|
REG_NOTIFY_CHANGE_LAST_SET = 0x00000004
|
||||||
|
|
||||||
|
WAIT_OBJECT_0 = 0x00000000
|
||||||
|
WAIT_TIMEOUT = 0x00000102
|
||||||
|
|
||||||
|
class RegistryMonitor:
|
||||||
|
|
||||||
|
def __init__(self, root, subkey):
|
||||||
|
self._root = root
|
||||||
|
self._subkey = subkey
|
||||||
|
self._event = CreateEvent(None, False, False, None)
|
||||||
|
|
||||||
|
self._key = None
|
||||||
|
self._open_key()
|
||||||
|
if self._key:
|
||||||
|
self._set_key_update_notification()
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
CloseHandle(self._event)
|
||||||
|
if self._key:
|
||||||
|
RegCloseKey(self._key)
|
||||||
|
self._key = None
|
||||||
|
|
||||||
|
def is_updated(self):
|
||||||
|
wait_result = WaitForSingleObject(self._event, 0)
|
||||||
|
|
||||||
|
# previously watched
|
||||||
|
if wait_result == WAIT_OBJECT_0:
|
||||||
|
self._set_key_update_notification()
|
||||||
|
return True
|
||||||
|
|
||||||
|
# no changes or no key before
|
||||||
|
if wait_result != WAIT_TIMEOUT:
|
||||||
|
# unexpected error
|
||||||
|
logging.warning("Unexpected WaitForSingleObject result %s", wait_result)
|
||||||
|
return False
|
||||||
|
|
||||||
|
if self._key is None:
|
||||||
|
self._open_key()
|
||||||
|
|
||||||
|
if self._key is None:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self._set_key_update_notification()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def _set_key_update_notification(self):
|
||||||
|
filter_ = REG_NOTIFY_CHANGE_NAME | REG_NOTIFY_CHANGE_LAST_SET
|
||||||
|
status = RegNotifyChangeKeyValue(self._key, True, filter_, self._event, True)
|
||||||
|
if status != ERROR_SUCCESS:
|
||||||
|
# key was deleted
|
||||||
|
RegCloseKey(self._key)
|
||||||
|
self._key = None
|
||||||
|
|
||||||
|
def _open_key(self):
|
||||||
|
access = KEY_QUERY_VALUE | KEY_READ
|
||||||
|
self._key = HKEY()
|
||||||
|
rc = RegOpenKeyEx(self._root, self._subkey, 0, access, ctypes.byref(self._key))
|
||||||
|
if rc != ERROR_SUCCESS:
|
||||||
|
self._key = None
|
||||||
@@ -135,7 +135,7 @@ async def test_prepare_get_unlocked_achievements_context_error(plugin, read, wri
|
|||||||
"game_ids": ["14"]
|
"game_ids": ["14"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
|
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
|
|
||||||
@@ -176,7 +176,7 @@ async def test_import_in_progress(plugin, read, write):
|
|||||||
read.side_effect = [
|
read.side_effect = [
|
||||||
async_return_value(create_message(requests[0])),
|
async_return_value(create_message(requests[0])),
|
||||||
async_return_value(create_message(requests[1])),
|
async_return_value(create_message(requests[1])),
|
||||||
async_return_value(b"")
|
async_return_value(b"", 10)
|
||||||
]
|
]
|
||||||
|
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ async def test_success(plugin, read, write):
|
|||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "init_authentication"
|
"method": "init_authentication"
|
||||||
}
|
}
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek"))
|
plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek"))
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
plugin.authenticate.assert_called_with()
|
plugin.authenticate.assert_called_with()
|
||||||
@@ -55,7 +55,7 @@ async def test_failure(plugin, read, write, error, code, message):
|
|||||||
"method": "init_authentication"
|
"method": "init_authentication"
|
||||||
}
|
}
|
||||||
|
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
plugin.authenticate.side_effect = error()
|
plugin.authenticate.side_effect = error()
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
plugin.authenticate.assert_called_with()
|
plugin.authenticate.assert_called_with()
|
||||||
@@ -84,7 +84,7 @@ async def test_stored_credentials(plugin, read, write):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek"))
|
plugin.authenticate.return_value = async_return_value(Authentication("132", "Zenek"))
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
plugin.authenticate.assert_called_with(stored_credentials={"token": "ABC"})
|
plugin.authenticate.assert_called_with(stored_credentials={"token": "ABC"})
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ async def test_get_friends_success(plugin, read, write):
|
|||||||
"method": "import_friends"
|
"method": "import_friends"
|
||||||
}
|
}
|
||||||
|
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
plugin.get_friends.return_value = async_return_value([
|
plugin.get_friends.return_value = async_return_value([
|
||||||
FriendInfo("3", "Jan"),
|
FriendInfo("3", "Jan"),
|
||||||
FriendInfo("5", "Ola")
|
FriendInfo("5", "Ola")
|
||||||
@@ -45,7 +45,7 @@ async def test_get_friends_failure(plugin, read, write):
|
|||||||
"method": "import_friends"
|
"method": "import_friends"
|
||||||
}
|
}
|
||||||
|
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
plugin.get_friends.side_effect = UnknownError()
|
plugin.get_friends.side_effect = UnknownError()
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
plugin.get_friends.assert_called_with()
|
plugin.get_friends.assert_called_with()
|
||||||
|
|||||||
@@ -135,7 +135,7 @@ async def test_prepare_get_game_time_context_error(plugin, read, write):
|
|||||||
"game_ids": ["6"]
|
"game_ids": ["6"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
|
|
||||||
assert get_messages(write) == [
|
assert get_messages(write) == [
|
||||||
@@ -174,7 +174,7 @@ async def test_import_in_progress(plugin, read, write):
|
|||||||
read.side_effect = [
|
read.side_effect = [
|
||||||
async_return_value(create_message(requests[0])),
|
async_return_value(create_message(requests[0])),
|
||||||
async_return_value(create_message(requests[1])),
|
async_return_value(create_message(requests[1])),
|
||||||
async_return_value(b"")
|
async_return_value(b"", 10)
|
||||||
]
|
]
|
||||||
|
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ async def test_success(plugin, read, write):
|
|||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_local_games"
|
"method": "import_local_games"
|
||||||
}
|
}
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
|
|
||||||
plugin.get_local_games.return_value = async_return_value([
|
plugin.get_local_games.return_value = async_return_value([
|
||||||
LocalGame("1", LocalGameState.Running),
|
LocalGame("1", LocalGameState.Running),
|
||||||
@@ -63,7 +63,7 @@ async def test_failure(plugin, read, write, error, code, message):
|
|||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_local_games"
|
"method": "import_local_games"
|
||||||
}
|
}
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
plugin.get_local_games.side_effect = error()
|
plugin.get_local_games.side_effect = error()
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
plugin.get_local_games.assert_called_with()
|
plugin.get_local_games.assert_called_with()
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ async def test_success(plugin, read, write):
|
|||||||
"id": "3",
|
"id": "3",
|
||||||
"method": "import_owned_games"
|
"method": "import_owned_games"
|
||||||
}
|
}
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
|
|
||||||
plugin.get_owned_games.return_value = async_return_value([
|
plugin.get_owned_games.return_value = async_return_value([
|
||||||
Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)),
|
Game("3", "Doom", None, LicenseInfo(LicenseType.SinglePurchase, None)),
|
||||||
@@ -80,7 +80,7 @@ async def test_failure(plugin, read, write):
|
|||||||
"method": "import_owned_games"
|
"method": "import_owned_games"
|
||||||
}
|
}
|
||||||
|
|
||||||
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"")]
|
read.side_effect = [async_return_value(create_message(request)), async_return_value(b"", 10)]
|
||||||
plugin.get_owned_games.side_effect = UnknownError()
|
plugin.get_owned_games.side_effect = UnknownError()
|
||||||
await plugin.run()
|
await plugin.run()
|
||||||
plugin.get_owned_games.assert_called_with()
|
plugin.get_owned_games.assert_called_with()
|
||||||
|
|||||||
Reference in New Issue
Block a user