python-xbox 0.1.0rc3__tar.gz → 0.1.0rc4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/LICENSE +2 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/PKG-INFO +5 -3
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/pyproject.toml +7 -4
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/__init__.py +1 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/client.py +2 -2
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/language.py +7 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/account/__init__.py +4 -2
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/achievements/__init__.py +6 -6
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/baseprovider.py +7 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/catalog/models.py +1 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/presence/__init__.py +1 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/ratelimitedprovider.py +6 -2
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/smartglass/__init__.py +5 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/titlehub/__init__.py +5 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/authentication/manager.py +1 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/authentication/models.py +1 -1
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/authentication/xal.py +2 -2
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/common/exceptions.py +5 -3
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/common/models.py +3 -3
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/common/ratelimits/__init__.py +5 -5
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/common/request_signer.py +8 -6
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/common/signed_session.py +10 -4
- python_xbox-0.1.0rc4/src/pythonxbox/py.typed +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/scripts/authenticate.py +4 -4
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/scripts/change_gamertag.py +2 -2
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/scripts/friends.py +2 -2
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/scripts/search.py +2 -2
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/scripts/xal.py +3 -3
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/.gitignore +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/README.md +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/account/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/achievements/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/catalog/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/catalog/const.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/cqs/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/cqs/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/gameclips/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/gameclips/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/lists/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/lists/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/mediahub/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/mediahub/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/message/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/message/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/people/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/people/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/presence/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/profile/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/profile/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/screenshots/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/screenshots/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/smartglass/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/titlehub/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/usersearch/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/usersearch/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/userstats/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/userstats/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/authentication/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/common/__init__.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/common/filetimes.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/common/ratelimits/models.py +0 -0
- {python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/scripts/__init__.py +0 -0
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
Copyright (c)
|
|
1
|
+
Copyright (c) 2025 Manfred Dennerlein Rodelo
|
|
2
|
+
Copyright (c) 2020 - 2025 OpenXbox
|
|
2
3
|
|
|
3
4
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
5
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-xbox
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.0rc4
|
|
4
4
|
Summary: A library to authenticate with Xbox Network and use their API
|
|
5
|
-
Project-URL:
|
|
5
|
+
Project-URL: Source, https://github.com/tr4nt0r/python-xbox
|
|
6
6
|
Author: OpenXbox
|
|
7
|
+
Author-email: Manfred Dennerlein Rodelo <manfred@dennerlein.name>
|
|
7
8
|
License-Expression: MIT
|
|
8
9
|
License-File: LICENSE
|
|
9
10
|
Keywords: xbox one live api
|
|
10
|
-
Classifier: Development Status ::
|
|
11
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
11
12
|
Classifier: Intended Audience :: Developers
|
|
12
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
13
15
|
Classifier: Programming Language :: Python :: 3
|
|
14
16
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
name = "python-xbox"
|
|
3
3
|
description = "A library to authenticate with Xbox Network and use their API"
|
|
4
4
|
authors = [
|
|
5
|
+
{name = "Manfred Dennerlein Rodelo", email = "manfred@dennerlein.name"},
|
|
5
6
|
{name = "OpenXbox"},
|
|
6
7
|
]
|
|
7
8
|
dependencies = [
|
|
@@ -15,7 +16,7 @@ readme = "README.md"
|
|
|
15
16
|
license = "MIT"
|
|
16
17
|
keywords = ["xbox one live api"]
|
|
17
18
|
classifiers = [
|
|
18
|
-
"Development Status ::
|
|
19
|
+
"Development Status :: 5 - Production/Stable",
|
|
19
20
|
"Intended Audience :: Developers",
|
|
20
21
|
"License :: OSI Approved :: MIT License",
|
|
21
22
|
"Programming Language :: Python :: 3",
|
|
@@ -23,12 +24,13 @@ classifiers = [
|
|
|
23
24
|
"Programming Language :: Python :: 3.12",
|
|
24
25
|
"Programming Language :: Python :: 3.13",
|
|
25
26
|
"Programming Language :: Python :: 3.14",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
26
28
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
29
|
]
|
|
28
30
|
dynamic = ["version"]
|
|
29
31
|
|
|
30
32
|
[project.urls]
|
|
31
|
-
|
|
33
|
+
Source = "https://github.com/tr4nt0r/python-xbox"
|
|
32
34
|
|
|
33
35
|
[project.optional-dependencies]
|
|
34
36
|
cli = [
|
|
@@ -47,8 +49,9 @@ line-length = 88
|
|
|
47
49
|
target-version = "py311"
|
|
48
50
|
|
|
49
51
|
[tool.ruff.lint]
|
|
50
|
-
select = ["E4", "E7", "E9", "F", "UP", "S"]
|
|
51
|
-
|
|
52
|
+
select = ["E4", "E7", "E9", "F", "UP", "S", "ANN"]
|
|
53
|
+
# select = ["ALL"]
|
|
54
|
+
ignore = ["ANN003", "ANN401"]
|
|
52
55
|
|
|
53
56
|
|
|
54
57
|
[tool.ruff.lint.per-file-ignores]
|
|
@@ -36,7 +36,7 @@ log = logging.getLogger("xbox.api")
|
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
class Session:
|
|
39
|
-
def __init__(self, auth_mgr: AuthenticationManager):
|
|
39
|
+
def __init__(self, auth_mgr: AuthenticationManager) -> None:
|
|
40
40
|
self._auth_mgr = auth_mgr
|
|
41
41
|
self._cv = CorrelationVector()
|
|
42
42
|
|
|
@@ -125,7 +125,7 @@ class XboxLiveClient:
|
|
|
125
125
|
self,
|
|
126
126
|
auth_mgr: AuthenticationManager,
|
|
127
127
|
language: XboxLiveLanguage = DefaultXboxLiveLanguages.United_States,
|
|
128
|
-
):
|
|
128
|
+
) -> None:
|
|
129
129
|
self._auth_mgr = auth_mgr
|
|
130
130
|
self.session = Session(auth_mgr)
|
|
131
131
|
self._language = language
|
|
@@ -4,7 +4,13 @@ Language definitions
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class XboxLiveLanguage:
|
|
7
|
-
def __init__(
|
|
7
|
+
def __init__(
|
|
8
|
+
self,
|
|
9
|
+
name: str,
|
|
10
|
+
short_id: str,
|
|
11
|
+
identifier: str,
|
|
12
|
+
locale: str,
|
|
13
|
+
) -> None:
|
|
8
14
|
"""
|
|
9
15
|
Initialize a new instance of :class:`XboxLiveLanguage`
|
|
10
16
|
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/account/__init__.py
RENAMED
|
@@ -12,7 +12,9 @@ class AccountProvider(BaseProvider):
|
|
|
12
12
|
HEADERS_USER_MGT = {"x-xbl-contract-version": "1"}
|
|
13
13
|
HEADERS_ACCOUNT = {"x-xbl-contract-version": "2"}
|
|
14
14
|
|
|
15
|
-
async def claim_gamertag(
|
|
15
|
+
async def claim_gamertag(
|
|
16
|
+
self, xuid: str, gamertag: str, **kwargs
|
|
17
|
+
) -> ClaimGamertagResult:
|
|
16
18
|
"""
|
|
17
19
|
Claim gamertag
|
|
18
20
|
|
|
@@ -40,7 +42,7 @@ class AccountProvider(BaseProvider):
|
|
|
40
42
|
resp.raise_for_status()
|
|
41
43
|
|
|
42
44
|
async def change_gamertag(
|
|
43
|
-
self, xuid, gamertag, preview=False, **kwargs
|
|
45
|
+
self, xuid: str, gamertag: str, preview: bool = False, **kwargs
|
|
44
46
|
) -> ChangeGamertagResult:
|
|
45
47
|
"""
|
|
46
48
|
Change your gamertag.
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/achievements/__init__.py
RENAMED
|
@@ -21,7 +21,7 @@ class AchievementsProvider(RateLimitedProvider):
|
|
|
21
21
|
RATE_LIMITS = {"burst": 100, "sustain": 300}
|
|
22
22
|
|
|
23
23
|
async def get_achievements_detail_item(
|
|
24
|
-
self, xuid, service_config_id, achievement_id, **kwargs
|
|
24
|
+
self, xuid: str, service_config_id: str, achievement_id: str, **kwargs
|
|
25
25
|
) -> AchievementResponse:
|
|
26
26
|
"""
|
|
27
27
|
Get achievement detail for specific item
|
|
@@ -45,7 +45,7 @@ class AchievementsProvider(RateLimitedProvider):
|
|
|
45
45
|
return AchievementResponse(**resp.json())
|
|
46
46
|
|
|
47
47
|
async def get_achievements_xbox360_all(
|
|
48
|
-
self, xuid, title_id, **kwargs
|
|
48
|
+
self, xuid: str, title_id: str, **kwargs
|
|
49
49
|
) -> Achievement360Response:
|
|
50
50
|
"""
|
|
51
51
|
Get all achievements for specific X360 title Id
|
|
@@ -70,7 +70,7 @@ class AchievementsProvider(RateLimitedProvider):
|
|
|
70
70
|
return Achievement360Response(**resp.json())
|
|
71
71
|
|
|
72
72
|
async def get_achievements_xbox360_earned(
|
|
73
|
-
self, xuid, title_id, **kwargs
|
|
73
|
+
self, xuid: str, title_id: str, **kwargs
|
|
74
74
|
) -> Achievement360Response:
|
|
75
75
|
"""
|
|
76
76
|
Get earned achievements for specific X360 title id
|
|
@@ -95,7 +95,7 @@ class AchievementsProvider(RateLimitedProvider):
|
|
|
95
95
|
return Achievement360Response(**resp.json())
|
|
96
96
|
|
|
97
97
|
async def get_achievements_xbox360_recent_progress_and_info(
|
|
98
|
-
self, xuid, **kwargs
|
|
98
|
+
self, xuid: str, **kwargs
|
|
99
99
|
) -> Achievement360ProgressResponse:
|
|
100
100
|
"""
|
|
101
101
|
Get recent achievement progress and information
|
|
@@ -117,7 +117,7 @@ class AchievementsProvider(RateLimitedProvider):
|
|
|
117
117
|
return Achievement360ProgressResponse(**resp.json())
|
|
118
118
|
|
|
119
119
|
async def get_achievements_xboxone_gameprogress(
|
|
120
|
-
self, xuid, title_id, **kwargs
|
|
120
|
+
self, xuid: str, title_id: str, **kwargs
|
|
121
121
|
) -> AchievementResponse:
|
|
122
122
|
"""
|
|
123
123
|
Get gameprogress for Xbox One title
|
|
@@ -142,7 +142,7 @@ class AchievementsProvider(RateLimitedProvider):
|
|
|
142
142
|
return AchievementResponse(**resp.json())
|
|
143
143
|
|
|
144
144
|
async def get_achievements_xboxone_recent_progress_and_info(
|
|
145
|
-
self, xuid, **kwargs
|
|
145
|
+
self, xuid: str, **kwargs
|
|
146
146
|
) -> RecentProgressResponse:
|
|
147
147
|
"""
|
|
148
148
|
Get recent achievement progress and information
|
|
@@ -4,9 +4,15 @@ BaseProvider
|
|
|
4
4
|
Subclassed by every *real* provider
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from pythonxbox.api.client import XboxLiveClient
|
|
12
|
+
|
|
7
13
|
|
|
8
14
|
class BaseProvider:
|
|
9
|
-
def __init__(self, client):
|
|
15
|
+
def __init__(self, client: "XboxLiveClient") -> None:
|
|
10
16
|
"""
|
|
11
17
|
Initialize an the BaseProvider
|
|
12
18
|
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/ratelimitedprovider.py
RENAMED
|
@@ -4,17 +4,21 @@ RateLimitedProvider
|
|
|
4
4
|
Subclassed by providers with rate limit support
|
|
5
5
|
"""
|
|
6
6
|
|
|
7
|
+
from typing import TYPE_CHECKING
|
|
7
8
|
from pythonxbox.api.provider.baseprovider import BaseProvider
|
|
8
9
|
from pythonxbox.common.exceptions import XboxException
|
|
9
10
|
from pythonxbox.common.ratelimits import CombinedRateLimit
|
|
10
11
|
from pythonxbox.common.ratelimits.models import LimitType, ParsedRateLimit, TimePeriod
|
|
11
12
|
|
|
13
|
+
if TYPE_CHECKING:
|
|
14
|
+
from pythonxbox.api.client import XboxLiveClient
|
|
15
|
+
|
|
12
16
|
|
|
13
17
|
class RateLimitedProvider(BaseProvider):
|
|
14
18
|
# dict -> Dict (typing.dict) https://stackoverflow.com/a/63460173
|
|
15
19
|
RATE_LIMITS: dict[str, int | dict[str, int]]
|
|
16
20
|
|
|
17
|
-
def __init__(self, client):
|
|
21
|
+
def __init__(self, client: "XboxLiveClient") -> None:
|
|
18
22
|
"""
|
|
19
23
|
Initialize Baseclass
|
|
20
24
|
|
|
@@ -39,7 +43,7 @@ class RateLimitedProvider(BaseProvider):
|
|
|
39
43
|
"RateLimitedProvider as parent class but RATE_LIMITS not set!"
|
|
40
44
|
)
|
|
41
45
|
|
|
42
|
-
def __handle_rate_limit_setup(self):
|
|
46
|
+
def __handle_rate_limit_setup(self) -> None:
|
|
43
47
|
# Retrieve burst and sustain from the dict
|
|
44
48
|
burst_key = self.RATE_LIMITS["burst"]
|
|
45
49
|
sustain_key = self.RATE_LIMITS["sustain"]
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/smartglass/__init__.py
RENAMED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
SmartGlass - Control Registered Devices
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
5
6
|
from uuid import uuid4
|
|
6
7
|
|
|
7
8
|
from httpx import Response
|
|
@@ -19,6 +20,9 @@ from pythonxbox.api.provider.smartglass.models import (
|
|
|
19
20
|
VolumeDirection,
|
|
20
21
|
)
|
|
21
22
|
|
|
23
|
+
if TYPE_CHECKING:
|
|
24
|
+
from pythonxbox.api.client import XboxLiveClient
|
|
25
|
+
|
|
22
26
|
|
|
23
27
|
class SmartglassProvider(BaseProvider):
|
|
24
28
|
SG_URL = "https://xccs.xboxlive.com"
|
|
@@ -27,7 +31,7 @@ class SmartglassProvider(BaseProvider):
|
|
|
27
31
|
"skillplatform": "RemoteManagement",
|
|
28
32
|
}
|
|
29
33
|
|
|
30
|
-
def __init__(self, client):
|
|
34
|
+
def __init__(self, client: "XboxLiveClient") -> None:
|
|
31
35
|
"""
|
|
32
36
|
Initialize Baseclass, create smartglass session id
|
|
33
37
|
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/titlehub/__init__.py
RENAMED
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
Titlehub - Get Title history and info
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
5
6
|
from pythonxbox.api.provider.baseprovider import BaseProvider
|
|
6
7
|
from pythonxbox.api.provider.titlehub.models import TitleFields, TitleHubResponse
|
|
7
8
|
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from pythonxbox.api.client import XboxLiveClient
|
|
11
|
+
|
|
8
12
|
|
|
9
13
|
class TitlehubProvider(BaseProvider):
|
|
10
14
|
TITLEHUB_URL = "https://titlehub.xboxlive.com"
|
|
11
15
|
SEPARATOR = ","
|
|
12
16
|
|
|
13
|
-
def __init__(self, client):
|
|
17
|
+
def __init__(self, client: "XboxLiveClient") -> None:
|
|
14
18
|
"""
|
|
15
19
|
Initialize Baseclass, set 'Accept-Language' header from client instance
|
|
16
20
|
|
|
@@ -29,7 +29,7 @@ class AuthenticationManager:
|
|
|
29
29
|
client_secret: str,
|
|
30
30
|
redirect_uri: str,
|
|
31
31
|
scopes: list[str] | None = None,
|
|
32
|
-
):
|
|
32
|
+
) -> None:
|
|
33
33
|
if not isinstance(client_session, (SignedSession, httpx.AsyncClient)):
|
|
34
34
|
raise DeprecationWarning(
|
|
35
35
|
"""Xbox WebAPI changed to use SignedSession (wrapped httpx.AsyncClient).
|
|
@@ -81,7 +81,7 @@ class XALManager:
|
|
|
81
81
|
device_id: uuid.UUID,
|
|
82
82
|
app_params: XalAppParameters,
|
|
83
83
|
client_params: XalClientParameters,
|
|
84
|
-
):
|
|
84
|
+
) -> None:
|
|
85
85
|
self.session = session
|
|
86
86
|
self.device_id = device_id
|
|
87
87
|
self.app_params = app_params
|
|
@@ -89,7 +89,7 @@ class XALManager:
|
|
|
89
89
|
self.cv = CorrelationVector()
|
|
90
90
|
|
|
91
91
|
@staticmethod
|
|
92
|
-
def _get_random_bytes(length) -> bytes:
|
|
92
|
+
def _get_random_bytes(length: int) -> bytes:
|
|
93
93
|
return os.urandom(length)
|
|
94
94
|
|
|
95
95
|
@staticmethod
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Special Exception subclasses
|
|
3
3
|
"""
|
|
4
4
|
|
|
5
|
+
from typing import Any
|
|
5
6
|
from pythonxbox.common.ratelimits import RateLimit
|
|
7
|
+
from httpx import Response
|
|
6
8
|
|
|
7
9
|
|
|
8
10
|
class XboxException(Exception):
|
|
@@ -18,7 +20,7 @@ class AuthenticationException(XboxException):
|
|
|
18
20
|
|
|
19
21
|
|
|
20
22
|
class TwoFactorAuthRequired(XboxException):
|
|
21
|
-
def __init__(self, message, server_data):
|
|
23
|
+
def __init__(self, message: str, server_data: dict[str, Any]) -> None:
|
|
22
24
|
"""
|
|
23
25
|
Raised when 2FA is required
|
|
24
26
|
|
|
@@ -31,7 +33,7 @@ class TwoFactorAuthRequired(XboxException):
|
|
|
31
33
|
|
|
32
34
|
|
|
33
35
|
class InvalidRequest(XboxException):
|
|
34
|
-
def __init__(self, message, response):
|
|
36
|
+
def __init__(self, message: str, response: Response) -> None:
|
|
35
37
|
"""
|
|
36
38
|
Raised when something is wrong with the request
|
|
37
39
|
|
|
@@ -51,7 +53,7 @@ class NotFoundException(XboxException):
|
|
|
51
53
|
|
|
52
54
|
|
|
53
55
|
class RateLimitExceededException(XboxException):
|
|
54
|
-
def __init__(self, message, rate_limit: RateLimit):
|
|
56
|
+
def __init__(self, message: str, rate_limit: RateLimit) -> None:
|
|
55
57
|
self.message = message
|
|
56
58
|
self.rate_limit = rate_limit
|
|
57
59
|
self.try_again_in = rate_limit.get_reset_after()
|
|
@@ -3,16 +3,16 @@
|
|
|
3
3
|
from pydantic import ConfigDict, BaseModel
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
def to_pascal(string):
|
|
6
|
+
def to_pascal(string: str) -> str:
|
|
7
7
|
return "".join(word.capitalize() for word in string.split("_"))
|
|
8
8
|
|
|
9
9
|
|
|
10
|
-
def to_camel(string):
|
|
10
|
+
def to_camel(string: str) -> str:
|
|
11
11
|
words = string.split("_")
|
|
12
12
|
return words[0] + "".join(word.capitalize() for word in words[1:])
|
|
13
13
|
|
|
14
14
|
|
|
15
|
-
def to_lower(string):
|
|
15
|
+
def to_lower(string: str) -> str:
|
|
16
16
|
return string.replace("_", "")
|
|
17
17
|
|
|
18
18
|
|
|
@@ -59,7 +59,7 @@ class SingleRateLimit(RateLimit):
|
|
|
59
59
|
This class is mainly used by the CombinedRateLimit class.
|
|
60
60
|
"""
|
|
61
61
|
|
|
62
|
-
def __init__(self, time_period: TimePeriod, type: LimitType, limit: int):
|
|
62
|
+
def __init__(self, time_period: TimePeriod, type: LimitType, limit: int) -> None:
|
|
63
63
|
self.__time_period = time_period
|
|
64
64
|
self.__type = type
|
|
65
65
|
self.__limit = limit
|
|
@@ -122,14 +122,14 @@ class SingleRateLimit(RateLimit):
|
|
|
122
122
|
return IncrementResult(counter=self.__counter, exceeded=self.__exceeded)
|
|
123
123
|
|
|
124
124
|
# Should be called after every inc of the counter
|
|
125
|
-
def __check_if_exceeded(self):
|
|
125
|
+
def __check_if_exceeded(self) -> None:
|
|
126
126
|
if not self.__exceeded:
|
|
127
127
|
if self.__counter >= self.__limit:
|
|
128
128
|
self.__exceeded = True
|
|
129
129
|
# reset-after is now dependent on the time since the first request of this cycle.
|
|
130
130
|
# self.__set_reset_after()
|
|
131
131
|
|
|
132
|
-
def __reset_counter_if_required(self):
|
|
132
|
+
def __reset_counter_if_required(self) -> None:
|
|
133
133
|
# Check to make sure reset_after is not None
|
|
134
134
|
# - This is the case if this function is called before the counter
|
|
135
135
|
# is incremented after a reset / new instantiation
|
|
@@ -139,7 +139,7 @@ class SingleRateLimit(RateLimit):
|
|
|
139
139
|
self.__counter = 0
|
|
140
140
|
self.__reset_after = None
|
|
141
141
|
|
|
142
|
-
def __set_reset_after(self):
|
|
142
|
+
def __set_reset_after(self) -> None:
|
|
143
143
|
self.__reset_after = datetime.now() + timedelta(
|
|
144
144
|
seconds=self.get_time_period().value
|
|
145
145
|
)
|
|
@@ -151,7 +151,7 @@ class CombinedRateLimit(RateLimit):
|
|
|
151
151
|
|
|
152
152
|
"""
|
|
153
153
|
|
|
154
|
-
def __init__(self, *parsed_limits: ParsedRateLimit, type: LimitType):
|
|
154
|
+
def __init__(self, *parsed_limits: ParsedRateLimit, type: LimitType) -> None:
|
|
155
155
|
# *parsed_limits is a tuple
|
|
156
156
|
|
|
157
157
|
# Create a SingleRateLimit instance for each limit
|
|
@@ -20,10 +20,12 @@ DEFAULT_SIGNING_POLICY = SignaturePolicy(
|
|
|
20
20
|
|
|
21
21
|
|
|
22
22
|
class RequestSigner:
|
|
23
|
-
def __init__(
|
|
24
|
-
self
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
signing_key: SigningKey | None = None,
|
|
26
|
+
signing_policy: SignaturePolicy | None = None,
|
|
27
|
+
) -> None:
|
|
28
|
+
self.signing_key = signing_key or SigningKey.generate(curve=NIST256p)
|
|
27
29
|
self.signing_policy = signing_policy or DEFAULT_SIGNING_POLICY
|
|
28
30
|
|
|
29
31
|
pk_point = self.signing_key.verifying_key.pubkey.point
|
|
@@ -44,7 +46,7 @@ class RequestSigner:
|
|
|
44
46
|
return SigningKey.from_pem(signing_key)
|
|
45
47
|
|
|
46
48
|
@classmethod
|
|
47
|
-
def from_pem(cls, pem_string: str):
|
|
49
|
+
def from_pem(cls, pem_string: str) -> SigningKey:
|
|
48
50
|
request_signer = RequestSigner.import_signing_key(pem_string)
|
|
49
51
|
return cls(request_signer)
|
|
50
52
|
|
|
@@ -184,5 +186,5 @@ class RequestSigner:
|
|
|
184
186
|
return encoded
|
|
185
187
|
|
|
186
188
|
@staticmethod
|
|
187
|
-
def __encode_ec_coord(coord) -> str:
|
|
189
|
+
def __encode_ec_coord(coord: int) -> str:
|
|
188
190
|
return RequestSigner.__base64_escaped(coord.to_bytes(32, "big"))
|
|
@@ -3,20 +3,26 @@ Signed Session
|
|
|
3
3
|
A wrapper around httpx' AsyncClient which transparently calculates the "Signature" header.
|
|
4
4
|
"""
|
|
5
5
|
|
|
6
|
+
from ssl import SSLContext
|
|
7
|
+
|
|
6
8
|
import httpx
|
|
9
|
+
from httpx import Response
|
|
7
10
|
|
|
8
|
-
from ssl import SSLContext
|
|
9
11
|
from pythonxbox.common.request_signer import RequestSigner
|
|
10
12
|
|
|
11
13
|
|
|
12
14
|
class SignedSession(httpx.AsyncClient):
|
|
13
|
-
def __init__(
|
|
15
|
+
def __init__(
|
|
16
|
+
self,
|
|
17
|
+
request_signer: RequestSigner | None = None,
|
|
18
|
+
ssl_context: SSLContext | None = None,
|
|
19
|
+
) -> None:
|
|
14
20
|
super().__init__(verify=ssl_context if ssl_context is not None else True)
|
|
15
21
|
|
|
16
22
|
self.request_signer = request_signer or RequestSigner()
|
|
17
23
|
|
|
18
24
|
@classmethod
|
|
19
|
-
def from_pem_signing_key(cls, pem_string: str):
|
|
25
|
+
def from_pem_signing_key(cls, pem_string: str) -> "SignedSession":
|
|
20
26
|
request_signer = RequestSigner.from_pem(pem_string)
|
|
21
27
|
return cls(request_signer)
|
|
22
28
|
|
|
@@ -45,7 +51,7 @@ class SignedSession(httpx.AsyncClient):
|
|
|
45
51
|
prepared = self._prepare_signed_request(request)
|
|
46
52
|
return await self.send(prepared)
|
|
47
53
|
|
|
48
|
-
async def send_signed(self, method: str, url: str, **kwargs):
|
|
54
|
+
async def send_signed(self, method: str, url: str, **kwargs) -> Response:
|
|
49
55
|
"""
|
|
50
56
|
Shorthand for creating request + prepare signed + send
|
|
51
57
|
"""
|
|
File without changes
|
|
@@ -25,7 +25,7 @@ class AuthCallbackRequestHandler(http.server.BaseHTTPRequestHandler):
|
|
|
25
25
|
Handles the auth callback that's received when Windows Live auth flow completed
|
|
26
26
|
"""
|
|
27
27
|
|
|
28
|
-
def do_GET(self):
|
|
28
|
+
def do_GET(self) -> None:
|
|
29
29
|
try:
|
|
30
30
|
url_path = self.requestline.split(" ")[1]
|
|
31
31
|
query_params = parse_qs(urlparse(url_path).query)
|
|
@@ -73,7 +73,7 @@ class AuthCallbackRequestHandler(http.server.BaseHTTPRequestHandler):
|
|
|
73
73
|
|
|
74
74
|
async def do_auth(
|
|
75
75
|
client_id: str, client_secret: str, redirect_uri: str, token_filepath: str
|
|
76
|
-
):
|
|
76
|
+
) -> None:
|
|
77
77
|
async with SignedSession() as session:
|
|
78
78
|
auth_mgr = AuthenticationManager(
|
|
79
79
|
session, client_id, client_secret, redirect_uri
|
|
@@ -99,7 +99,7 @@ async def do_auth(
|
|
|
99
99
|
f.write(auth_mgr.oauth.model_dump_json())
|
|
100
100
|
|
|
101
101
|
|
|
102
|
-
async def async_main():
|
|
102
|
+
async def async_main() -> None:
|
|
103
103
|
parser = argparse.ArgumentParser(description="Authenticate with XBL")
|
|
104
104
|
parser.add_argument(
|
|
105
105
|
"--tokens",
|
|
@@ -151,7 +151,7 @@ async def async_main():
|
|
|
151
151
|
)
|
|
152
152
|
|
|
153
153
|
|
|
154
|
-
def main():
|
|
154
|
+
def main() -> None:
|
|
155
155
|
asyncio.run(async_main())
|
|
156
156
|
|
|
157
157
|
|
|
@@ -20,7 +20,7 @@ from pythonxbox.common.signed_session import SignedSession
|
|
|
20
20
|
from pythonxbox.scripts import CLIENT_ID, CLIENT_SECRET, TOKENS_FILE
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
async def async_main():
|
|
23
|
+
async def async_main() -> None:
|
|
24
24
|
parser = argparse.ArgumentParser(description="Change your gamertag")
|
|
25
25
|
parser.add_argument(
|
|
26
26
|
"--tokens",
|
|
@@ -103,7 +103,7 @@ async def async_main():
|
|
|
103
103
|
print("Gamertag successfully changed to %s" % args.gamertag)
|
|
104
104
|
|
|
105
105
|
|
|
106
|
-
def main():
|
|
106
|
+
def main() -> None:
|
|
107
107
|
asyncio.run(async_main())
|
|
108
108
|
|
|
109
109
|
|
|
@@ -17,7 +17,7 @@ from pythonxbox.common.signed_session import SignedSession
|
|
|
17
17
|
from pythonxbox.scripts import CLIENT_ID, CLIENT_SECRET, TOKENS_FILE
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
async def async_main():
|
|
20
|
+
async def async_main() -> None:
|
|
21
21
|
parser = argparse.ArgumentParser(description="Change your gamertag")
|
|
22
22
|
parser.add_argument(
|
|
23
23
|
"--tokens",
|
|
@@ -72,7 +72,7 @@ async def async_main():
|
|
|
72
72
|
pprint(resp.dict())
|
|
73
73
|
|
|
74
74
|
|
|
75
|
-
def main():
|
|
75
|
+
def main() -> None:
|
|
76
76
|
asyncio.run(async_main())
|
|
77
77
|
|
|
78
78
|
|
|
@@ -14,7 +14,7 @@ from pythonxbox.authentication.manager import AuthenticationManager
|
|
|
14
14
|
from pythonxbox.common.signed_session import SignedSession
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
async def async_main():
|
|
17
|
+
async def async_main() -> None:
|
|
18
18
|
parser = argparse.ArgumentParser(description="Search for Content on XBL")
|
|
19
19
|
parser.add_argument("search_query", help="Name to search for")
|
|
20
20
|
|
|
@@ -35,7 +35,7 @@ async def async_main():
|
|
|
35
35
|
pprint(resp.dict())
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
def main():
|
|
38
|
+
def main() -> None:
|
|
39
39
|
asyncio.run(async_main())
|
|
40
40
|
|
|
41
41
|
|
|
@@ -50,7 +50,7 @@ def user_prompt_authentication(auth_url: str) -> str:
|
|
|
50
50
|
return redirect_url
|
|
51
51
|
|
|
52
52
|
|
|
53
|
-
async def do_auth(device_id: uuid.UUID, token_filepath: str):
|
|
53
|
+
async def do_auth(device_id: uuid.UUID, token_filepath: str) -> None:
|
|
54
54
|
async with SignedSession() as session:
|
|
55
55
|
app_params = APP_PARAMS_GAMEPASS_BETA
|
|
56
56
|
client_params = CLIENT_PARAMS_ANDROID
|
|
@@ -85,7 +85,7 @@ async def do_auth(device_id: uuid.UUID, token_filepath: str):
|
|
|
85
85
|
json.dump(store, f, default=pydantic_encoder)
|
|
86
86
|
|
|
87
87
|
|
|
88
|
-
async def async_main():
|
|
88
|
+
async def async_main() -> None:
|
|
89
89
|
parser = argparse.ArgumentParser(description="Authenticate with XBL via XAL")
|
|
90
90
|
parser.add_argument(
|
|
91
91
|
"--tokens",
|
|
@@ -105,7 +105,7 @@ async def async_main():
|
|
|
105
105
|
await do_auth(args.device_id, args.tokens)
|
|
106
106
|
|
|
107
107
|
|
|
108
|
-
def main():
|
|
108
|
+
def main() -> None:
|
|
109
109
|
asyncio.run(async_main())
|
|
110
110
|
|
|
111
111
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/achievements/models.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/catalog/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/gameclips/__init__.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/gameclips/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/mediahub/__init__.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/mediahub/models.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/message/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/people/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/presence/models.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/profile/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/screenshots/__init__.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/screenshots/models.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/smartglass/models.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/titlehub/models.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/usersearch/__init__.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/usersearch/models.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/userstats/__init__.py
RENAMED
|
File without changes
|
{python_xbox-0.1.0rc3 → python_xbox-0.1.0rc4}/src/pythonxbox/api/provider/userstats/models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|