python-xbox 0.1.0rc0__py3-none-any.whl
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.0rc0.dist-info/METADATA +213 -0
- python_xbox-0.1.0rc0.dist-info/RECORD +63 -0
- python_xbox-0.1.0rc0.dist-info/WHEEL +4 -0
- python_xbox-0.1.0rc0.dist-info/entry_points.txt +6 -0
- python_xbox-0.1.0rc0.dist-info/licenses/LICENSE +19 -0
- webapi/__init__.py +4 -0
- webapi/api/__init__.py +0 -0
- webapi/api/client.py +166 -0
- webapi/api/language.py +70 -0
- webapi/api/provider/__init__.py +0 -0
- webapi/api/provider/account/__init__.py +71 -0
- webapi/api/provider/account/models.py +11 -0
- webapi/api/provider/achievements/__init__.py +164 -0
- webapi/api/provider/achievements/models.py +133 -0
- webapi/api/provider/baseprovider.py +16 -0
- webapi/api/provider/catalog/__init__.py +88 -0
- webapi/api/provider/catalog/const.py +15 -0
- webapi/api/provider/catalog/models.py +428 -0
- webapi/api/provider/cqs/__init__.py +85 -0
- webapi/api/provider/cqs/models.py +59 -0
- webapi/api/provider/gameclips/__init__.py +167 -0
- webapi/api/provider/gameclips/models.py +59 -0
- webapi/api/provider/lists/__init__.py +71 -0
- webapi/api/provider/lists/models.py +35 -0
- webapi/api/provider/mediahub/__init__.py +64 -0
- webapi/api/provider/mediahub/models.py +84 -0
- webapi/api/provider/message/__init__.py +135 -0
- webapi/api/provider/message/models.py +96 -0
- webapi/api/provider/people/__init__.py +193 -0
- webapi/api/provider/people/models.py +252 -0
- webapi/api/provider/presence/__init__.py +112 -0
- webapi/api/provider/presence/models.py +54 -0
- webapi/api/provider/profile/__init__.py +142 -0
- webapi/api/provider/profile/models.py +48 -0
- webapi/api/provider/ratelimitedprovider.py +77 -0
- webapi/api/provider/screenshots/__init__.py +167 -0
- webapi/api/provider/screenshots/models.py +56 -0
- webapi/api/provider/smartglass/__init__.py +399 -0
- webapi/api/provider/smartglass/models.py +187 -0
- webapi/api/provider/titlehub/__init__.py +141 -0
- webapi/api/provider/titlehub/models.py +106 -0
- webapi/api/provider/usersearch/__init__.py +29 -0
- webapi/api/provider/usersearch/models.py +19 -0
- webapi/api/provider/userstats/__init__.py +166 -0
- webapi/api/provider/userstats/models.py +46 -0
- webapi/authentication/__init__.py +0 -0
- webapi/authentication/manager.py +162 -0
- webapi/authentication/models.py +163 -0
- webapi/authentication/xal.py +348 -0
- webapi/common/__init__.py +0 -0
- webapi/common/exceptions.py +57 -0
- webapi/common/filetimes.py +97 -0
- webapi/common/models.py +34 -0
- webapi/common/ratelimits/__init__.py +269 -0
- webapi/common/ratelimits/models.py +23 -0
- webapi/common/request_signer.py +189 -0
- webapi/common/signed_session.py +54 -0
- webapi/scripts/__init__.py +15 -0
- webapi/scripts/authenticate.py +159 -0
- webapi/scripts/change_gamertag.py +111 -0
- webapi/scripts/friends.py +80 -0
- webapi/scripts/search.py +43 -0
- webapi/scripts/xal.py +113 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Gameclips - Get gameclip info
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from xbox.webapi.api.provider.baseprovider import BaseProvider
|
|
6
|
+
from xbox.webapi.api.provider.gameclips.models import GameclipsResponse
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class GameclipProvider(BaseProvider):
|
|
10
|
+
GAMECLIPS_METADATA_URL = "https://gameclipsmetadata.xboxlive.com"
|
|
11
|
+
HEADERS_GAMECLIPS_METADATA = {"x-xbl-contract-version": "1"}
|
|
12
|
+
|
|
13
|
+
async def get_recent_community_clips_by_title_id(
|
|
14
|
+
self, title_id: str, **kwargs
|
|
15
|
+
) -> GameclipsResponse:
|
|
16
|
+
"""
|
|
17
|
+
Get recent community clips by Title Id
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
title_id: Title Id to get clips for
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
:class:`GameclipsResponse`: Game clip Response
|
|
24
|
+
"""
|
|
25
|
+
url = self.GAMECLIPS_METADATA_URL + f"/public/titles/{title_id}/clips"
|
|
26
|
+
params = {"qualifier": "created"}
|
|
27
|
+
resp = await self.client.session.get(
|
|
28
|
+
url, params=params, headers=self.HEADERS_GAMECLIPS_METADATA, **kwargs
|
|
29
|
+
)
|
|
30
|
+
resp.raise_for_status()
|
|
31
|
+
return GameclipsResponse(**resp.json())
|
|
32
|
+
|
|
33
|
+
async def get_recent_own_clips(
|
|
34
|
+
self, title_id: str = None, skip_items: int = 0, max_items: int = 25, **kwargs
|
|
35
|
+
) -> GameclipsResponse:
|
|
36
|
+
"""
|
|
37
|
+
Get own recent clips, optionally filter for title Id
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
title_id: Title ID to filter
|
|
41
|
+
skip_items: Item count to skip
|
|
42
|
+
max_items: Maximum item count to load
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
:class:`GameclipsResponse`: Game clip Response
|
|
46
|
+
"""
|
|
47
|
+
url = self.GAMECLIPS_METADATA_URL + "/users/me"
|
|
48
|
+
if title_id:
|
|
49
|
+
url += f"/titles/{title_id}"
|
|
50
|
+
url += "/clips"
|
|
51
|
+
|
|
52
|
+
params = {"skipItems": skip_items, "maxItems": max_items}
|
|
53
|
+
resp = await self.client.session.get(
|
|
54
|
+
url, params=params, headers=self.HEADERS_GAMECLIPS_METADATA, **kwargs
|
|
55
|
+
)
|
|
56
|
+
resp.raise_for_status()
|
|
57
|
+
return GameclipsResponse(**resp.json())
|
|
58
|
+
|
|
59
|
+
async def get_recent_clips_by_xuid(
|
|
60
|
+
self,
|
|
61
|
+
xuid: str,
|
|
62
|
+
title_id: str = None,
|
|
63
|
+
skip_items: int = 0,
|
|
64
|
+
max_items: int = 25,
|
|
65
|
+
**kwargs,
|
|
66
|
+
) -> GameclipsResponse:
|
|
67
|
+
"""
|
|
68
|
+
Get clips by XUID, optionally filter for title Id
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
xuid: XUID of user to get clips from
|
|
72
|
+
title_id: Optional title id filter
|
|
73
|
+
skip_items: Item count to skip
|
|
74
|
+
max_items: Maximum item count to load
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
:class:`GameclipsResponse`: Game clip Response
|
|
78
|
+
"""
|
|
79
|
+
url = self.GAMECLIPS_METADATA_URL + f"/users/xuid({xuid})"
|
|
80
|
+
if title_id:
|
|
81
|
+
url += f"/titles/{title_id}"
|
|
82
|
+
url += "/clips"
|
|
83
|
+
|
|
84
|
+
params = {"skipItems": skip_items, "maxItems": max_items}
|
|
85
|
+
resp = await self.client.session.get(
|
|
86
|
+
url, params=params, headers=self.HEADERS_GAMECLIPS_METADATA, **kwargs
|
|
87
|
+
)
|
|
88
|
+
resp.raise_for_status()
|
|
89
|
+
return GameclipsResponse(**resp.json())
|
|
90
|
+
|
|
91
|
+
async def get_saved_community_clips_by_title_id(
|
|
92
|
+
self, title_id: str, **kwargs
|
|
93
|
+
) -> GameclipsResponse:
|
|
94
|
+
"""
|
|
95
|
+
Get saved community clips by Title Id
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
title_id: Title Id to get screenshots for
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
:class:`GameclipsResponse`: Game clip Response
|
|
102
|
+
"""
|
|
103
|
+
url = self.GAMECLIPS_METADATA_URL + f"/public/titles/{title_id}/clips/saved"
|
|
104
|
+
params = {"qualifier": "created"}
|
|
105
|
+
resp = await self.client.session.get(
|
|
106
|
+
url, params=params, headers=self.HEADERS_GAMECLIPS_METADATA, **kwargs
|
|
107
|
+
)
|
|
108
|
+
resp.raise_for_status()
|
|
109
|
+
return GameclipsResponse(**resp.json())
|
|
110
|
+
|
|
111
|
+
async def get_saved_own_clips(
|
|
112
|
+
self, title_id: str = None, skip_items: int = 0, max_items: int = 25, **kwargs
|
|
113
|
+
) -> GameclipsResponse:
|
|
114
|
+
"""
|
|
115
|
+
Get own saved clips, optionally filter for title Id an
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
title_id: Optional Title ID to filter
|
|
119
|
+
skip_items: Item count to skip
|
|
120
|
+
max_items: Maximum item count to load
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
:class:`GameclipsResponse`: Game clip Response
|
|
124
|
+
"""
|
|
125
|
+
url = self.GAMECLIPS_METADATA_URL + "/users/me"
|
|
126
|
+
if title_id:
|
|
127
|
+
url += f"/titles/{title_id}"
|
|
128
|
+
url += "/clips/saved"
|
|
129
|
+
|
|
130
|
+
params = {"skipItems": skip_items, "maxItems": max_items}
|
|
131
|
+
resp = await self.client.session.get(
|
|
132
|
+
url, params=params, headers=self.HEADERS_GAMECLIPS_METADATA, **kwargs
|
|
133
|
+
)
|
|
134
|
+
resp.raise_for_status()
|
|
135
|
+
return GameclipsResponse(**resp.json())
|
|
136
|
+
|
|
137
|
+
async def get_saved_clips_by_xuid(
|
|
138
|
+
self,
|
|
139
|
+
xuid: str,
|
|
140
|
+
title_id: str = None,
|
|
141
|
+
skip_items: int = 0,
|
|
142
|
+
max_items: int = 25,
|
|
143
|
+
**kwargs,
|
|
144
|
+
) -> GameclipsResponse:
|
|
145
|
+
"""
|
|
146
|
+
Get saved clips by XUID, optionally filter for title Id
|
|
147
|
+
|
|
148
|
+
Args:
|
|
149
|
+
xuid: XUID of user to get screenshots from
|
|
150
|
+
title_id: Optional title id filter
|
|
151
|
+
skip_items: Item count to skip
|
|
152
|
+
max_items: Maximum item count to load
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
:class:`GameclipsResponse`: Game clip Response
|
|
156
|
+
"""
|
|
157
|
+
url = self.GAMECLIPS_METADATA_URL + f"/users/xuid({xuid})"
|
|
158
|
+
if title_id:
|
|
159
|
+
url += f"/titles/{title_id}"
|
|
160
|
+
url += "/clips/saved"
|
|
161
|
+
|
|
162
|
+
params = {"skipItems": skip_items, "maxItems": max_items}
|
|
163
|
+
resp = await self.client.session.get(
|
|
164
|
+
url, params=params, headers=self.HEADERS_GAMECLIPS_METADATA, **kwargs
|
|
165
|
+
)
|
|
166
|
+
resp.raise_for_status()
|
|
167
|
+
return GameclipsResponse(**resp.json())
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import List, Optional
|
|
3
|
+
|
|
4
|
+
from xbox.webapi.common.models import CamelCaseModel
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Thumbnail(CamelCaseModel):
|
|
8
|
+
uri: str
|
|
9
|
+
file_size: int
|
|
10
|
+
thumbnail_type: int
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class GameClipUri(CamelCaseModel):
|
|
14
|
+
uri: str
|
|
15
|
+
file_size: int
|
|
16
|
+
uri_type: int
|
|
17
|
+
expiration: str
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class GameClip(CamelCaseModel):
|
|
21
|
+
game_clip_id: str
|
|
22
|
+
state: int
|
|
23
|
+
date_published: datetime
|
|
24
|
+
date_recorded: datetime
|
|
25
|
+
last_modified: datetime
|
|
26
|
+
user_caption: str
|
|
27
|
+
type: int
|
|
28
|
+
duration_in_seconds: int
|
|
29
|
+
scid: str
|
|
30
|
+
title_id: int
|
|
31
|
+
rating: float
|
|
32
|
+
rating_count: int
|
|
33
|
+
views: int
|
|
34
|
+
title_data: str
|
|
35
|
+
system_properties: str
|
|
36
|
+
saved_by_user: bool
|
|
37
|
+
achievement_id: str
|
|
38
|
+
greatest_moment_id: str
|
|
39
|
+
thumbnails: List[Thumbnail]
|
|
40
|
+
game_clip_uris: List[GameClipUri]
|
|
41
|
+
xuid: str
|
|
42
|
+
clip_name: str
|
|
43
|
+
title_name: str
|
|
44
|
+
game_clip_locale: str
|
|
45
|
+
clip_content_attributes: int
|
|
46
|
+
device_type: str
|
|
47
|
+
comment_count: int
|
|
48
|
+
like_count: int
|
|
49
|
+
share_count: int
|
|
50
|
+
partial_views: int
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class PagingInfo(CamelCaseModel):
|
|
54
|
+
continuation_token: Optional[str] = None
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class GameclipsResponse(CamelCaseModel):
|
|
58
|
+
game_clips: List[GameClip]
|
|
59
|
+
paging_info: PagingInfo
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""
|
|
2
|
+
EPLists - Mainly used for XBL Pins
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from xbox.webapi.api.provider.baseprovider import BaseProvider
|
|
6
|
+
from xbox.webapi.api.provider.lists.models import ListMetadata, ListsResponse
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class ListsProvider(BaseProvider):
|
|
10
|
+
LISTS_URL = "https://eplists.xboxlive.com"
|
|
11
|
+
HEADERS_LISTS = {"Content-Type": "application/json", "x-xbl-contract-version": "2"}
|
|
12
|
+
|
|
13
|
+
SEPERATOR = "."
|
|
14
|
+
|
|
15
|
+
async def remove_items(
|
|
16
|
+
self, xuid: str, post_body: dict, listname: str = "XBLPins", **kwargs
|
|
17
|
+
) -> ListMetadata:
|
|
18
|
+
"""
|
|
19
|
+
Remove items from specific list, defaults to "XBLPins"
|
|
20
|
+
|
|
21
|
+
Args:
|
|
22
|
+
xuid (str/int): Xbox User Id
|
|
23
|
+
listname (str): Name of list to edit
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
:class:`ListMetadata`: List Metadata Response
|
|
27
|
+
"""
|
|
28
|
+
url = self.LISTS_URL + f"/users/xuid({xuid})/lists/PINS/{listname}"
|
|
29
|
+
resp = await self.client.session.delete(
|
|
30
|
+
url, json=post_body, headers=self.HEADERS_LISTS, **kwargs
|
|
31
|
+
)
|
|
32
|
+
resp.raise_for_status()
|
|
33
|
+
return ListMetadata(**resp.json())
|
|
34
|
+
|
|
35
|
+
async def get_items(
|
|
36
|
+
self, xuid: str, listname: str = "XBLPins", **kwargs
|
|
37
|
+
) -> ListsResponse:
|
|
38
|
+
"""
|
|
39
|
+
Get items from specific list, defaults to "XBLPins"
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
xuid (str/int): Xbox User Id
|
|
43
|
+
listname (str): Name of list to edit
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
:class:`ListsResponse`: List Response
|
|
47
|
+
"""
|
|
48
|
+
url = self.LISTS_URL + f"/users/xuid({xuid})/lists/PINS/{listname}"
|
|
49
|
+
resp = await self.client.session.get(url, headers=self.HEADERS_LISTS, **kwargs)
|
|
50
|
+
resp.raise_for_status()
|
|
51
|
+
return ListsResponse(**resp.json())
|
|
52
|
+
|
|
53
|
+
async def insert_items(
|
|
54
|
+
self, xuid: str, post_body: dict, listname: str = "XBLPins", **kwargs
|
|
55
|
+
) -> ListMetadata:
|
|
56
|
+
"""
|
|
57
|
+
Insert items to specific list, defaults to "XBLPins"
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
xuid (str/int): Xbox User Id
|
|
61
|
+
listname (str): Name of list to edit
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
:class:`ListMetadata`: List Metadata Response
|
|
65
|
+
"""
|
|
66
|
+
url = self.LISTS_URL + f"/users/xuid({xuid})/lists/PINS/{listname}"
|
|
67
|
+
resp = await self.client.session.post(
|
|
68
|
+
url, json=post_body, headers=self.HEADERS_LISTS, **kwargs
|
|
69
|
+
)
|
|
70
|
+
resp.raise_for_status()
|
|
71
|
+
return ListMetadata(**resp.json())
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from xbox.webapi.common.models import PascalCaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Item(PascalCaseModel):
|
|
7
|
+
item_id: str
|
|
8
|
+
content_type: str
|
|
9
|
+
title: Optional[str] = None
|
|
10
|
+
device_type: str
|
|
11
|
+
provider: Optional[str] = None
|
|
12
|
+
provider_id: Optional[str] = None
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ListItem(PascalCaseModel):
|
|
16
|
+
date_added: str
|
|
17
|
+
date_modified: str
|
|
18
|
+
index: int
|
|
19
|
+
k_value: int
|
|
20
|
+
item: Item
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class ListMetadata(PascalCaseModel):
|
|
24
|
+
list_title: str
|
|
25
|
+
list_version: int
|
|
26
|
+
list_count: int
|
|
27
|
+
allow_duplicates: bool
|
|
28
|
+
max_list_size: int
|
|
29
|
+
access_setting: str
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class ListsResponse(PascalCaseModel):
|
|
33
|
+
impression_id: str
|
|
34
|
+
list_items: List[ListItem]
|
|
35
|
+
list_metadata: ListMetadata
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Mediahub - Fetch screenshots and gameclips
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from xbox.webapi.api.provider.baseprovider import BaseProvider
|
|
6
|
+
from xbox.webapi.api.provider.mediahub.models import (
|
|
7
|
+
MediahubGameclips,
|
|
8
|
+
MediahubScreenshots,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class MediahubProvider(BaseProvider):
|
|
13
|
+
MEDIAHUB_URL = "https://mediahub.xboxlive.com"
|
|
14
|
+
HEADERS = {"x-xbl-contract-version": "3"}
|
|
15
|
+
|
|
16
|
+
async def fetch_own_clips(
|
|
17
|
+
self, skip: int = 0, count: int = 500, **kwargs
|
|
18
|
+
) -> MediahubGameclips:
|
|
19
|
+
"""
|
|
20
|
+
Fetch own clips
|
|
21
|
+
|
|
22
|
+
Args:
|
|
23
|
+
skip: Number of items to skip
|
|
24
|
+
count: Max entries to fetch
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
:class:`MediahubGameclips`: Gameclips
|
|
28
|
+
"""
|
|
29
|
+
url = f"{self.MEDIAHUB_URL}/gameclips/search"
|
|
30
|
+
post_data = {
|
|
31
|
+
"max": count,
|
|
32
|
+
"query": f"OwnerXuid eq {self.client.xuid}",
|
|
33
|
+
"skip": skip,
|
|
34
|
+
}
|
|
35
|
+
resp = await self.client.session.post(
|
|
36
|
+
url, json=post_data, headers=self.HEADERS, **kwargs
|
|
37
|
+
)
|
|
38
|
+
resp.raise_for_status()
|
|
39
|
+
return MediahubGameclips(**resp.json())
|
|
40
|
+
|
|
41
|
+
async def fetch_own_screenshots(
|
|
42
|
+
self, skip: int = 0, count: int = 500, **kwargs
|
|
43
|
+
) -> MediahubScreenshots:
|
|
44
|
+
"""
|
|
45
|
+
Fetch own screenshots
|
|
46
|
+
|
|
47
|
+
Args:
|
|
48
|
+
skip: Number of items to skip
|
|
49
|
+
count: Max entries to fetch
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
:class:`MediahubScreenshots`: Screenshots
|
|
53
|
+
"""
|
|
54
|
+
url = f"{self.MEDIAHUB_URL}/screenshots/search"
|
|
55
|
+
post_data = {
|
|
56
|
+
"max": count,
|
|
57
|
+
"query": f"OwnerXuid eq {self.client.xuid}",
|
|
58
|
+
"skip": skip,
|
|
59
|
+
}
|
|
60
|
+
resp = await self.client.session.post(
|
|
61
|
+
url, json=post_data, headers=self.HEADERS, **kwargs
|
|
62
|
+
)
|
|
63
|
+
resp.raise_for_status()
|
|
64
|
+
return MediahubScreenshots(**resp.json())
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
from typing import List, Optional
|
|
2
|
+
|
|
3
|
+
from xbox.webapi.common.models import CamelCaseModel
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class ContentSegment(CamelCaseModel):
|
|
7
|
+
segment_id: int
|
|
8
|
+
creation_type: str
|
|
9
|
+
creator_channel_id: Optional[str] = None
|
|
10
|
+
creator_xuid: int
|
|
11
|
+
record_date: str
|
|
12
|
+
duration_in_seconds: int
|
|
13
|
+
offset: int
|
|
14
|
+
secondary_title_id: Optional[int] = None
|
|
15
|
+
title_id: int
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class ContentLocator(CamelCaseModel):
|
|
19
|
+
expiration: Optional[str] = None
|
|
20
|
+
file_size: Optional[int] = None
|
|
21
|
+
locator_type: str
|
|
22
|
+
uri: str
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class GameclipContent(CamelCaseModel):
|
|
26
|
+
content_id: str
|
|
27
|
+
content_locators: List[ContentLocator]
|
|
28
|
+
content_segments: List[ContentSegment]
|
|
29
|
+
creation_type: str
|
|
30
|
+
duration_in_seconds: int
|
|
31
|
+
local_id: str
|
|
32
|
+
owner_xuid: int
|
|
33
|
+
sandbox_id: str
|
|
34
|
+
shared_to: List[int]
|
|
35
|
+
title_id: int
|
|
36
|
+
title_name: str
|
|
37
|
+
upload_date: str
|
|
38
|
+
upload_language: str
|
|
39
|
+
upload_region: str
|
|
40
|
+
upload_title_id: int
|
|
41
|
+
upload_device_type: str
|
|
42
|
+
comment_count: int
|
|
43
|
+
like_count: int
|
|
44
|
+
share_count: int
|
|
45
|
+
view_count: int
|
|
46
|
+
content_state: str
|
|
47
|
+
enforcement_state: str
|
|
48
|
+
sessions: List[str]
|
|
49
|
+
tournaments: List[str]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class MediahubGameclips(CamelCaseModel):
|
|
53
|
+
values: List[GameclipContent]
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class ScreenshotContent(CamelCaseModel):
|
|
57
|
+
content_id: str
|
|
58
|
+
capture_date: str
|
|
59
|
+
content_locators: List[ContentLocator]
|
|
60
|
+
local_id: str
|
|
61
|
+
owner_xuid: int
|
|
62
|
+
resolution_height: int
|
|
63
|
+
resolution_width: int
|
|
64
|
+
date_uploaded: str
|
|
65
|
+
sandbox_id: str
|
|
66
|
+
shared_to: List[int]
|
|
67
|
+
title_id: int
|
|
68
|
+
title_name: str
|
|
69
|
+
upload_language: str
|
|
70
|
+
upload_region: str
|
|
71
|
+
upload_title_id: int
|
|
72
|
+
upload_device_type: str
|
|
73
|
+
comment_count: int
|
|
74
|
+
like_count: int
|
|
75
|
+
share_count: int
|
|
76
|
+
view_count: int
|
|
77
|
+
content_state: str
|
|
78
|
+
enforcement_state: str
|
|
79
|
+
sessions: List[str]
|
|
80
|
+
tournaments: List[str]
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class MediahubScreenshots(CamelCaseModel):
|
|
84
|
+
values: List[ScreenshotContent]
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Message - Read and send messages
|
|
3
|
+
|
|
4
|
+
TODO: Support group messaging
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from xbox.webapi.api.provider.baseprovider import BaseProvider
|
|
8
|
+
from xbox.webapi.api.provider.message.models import (
|
|
9
|
+
ConversationResponse,
|
|
10
|
+
InboxResponse,
|
|
11
|
+
SendMessageResponse,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class MessageProvider(BaseProvider):
|
|
16
|
+
MSG_URL = "https://xblmessaging.xboxlive.com"
|
|
17
|
+
HEADERS_MESSAGE = {"x-xbl-contract-version": "1"}
|
|
18
|
+
HEADERS_HORIZON = {"x-xbl-contract-version": "2"}
|
|
19
|
+
|
|
20
|
+
async def get_inbox(self, max_items: int = 100, **kwargs) -> InboxResponse:
|
|
21
|
+
"""
|
|
22
|
+
Get messages
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
:class:`InboxResponse`: Inbox Response
|
|
26
|
+
"""
|
|
27
|
+
url = f"{self.MSG_URL}/network/Xbox/users/me/inbox"
|
|
28
|
+
params = {"maxItems": max_items}
|
|
29
|
+
resp = await self.client.session.get(
|
|
30
|
+
url, params=params, headers=self.HEADERS_MESSAGE, **kwargs
|
|
31
|
+
)
|
|
32
|
+
resp.raise_for_status()
|
|
33
|
+
return InboxResponse(**resp.json())
|
|
34
|
+
|
|
35
|
+
async def get_conversation(
|
|
36
|
+
self, xuid: str, max_items: int = 100, **kwargs
|
|
37
|
+
) -> ConversationResponse:
|
|
38
|
+
"""
|
|
39
|
+
Get detailed conversation info
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
xuid: Xuid of user having a conversation with
|
|
43
|
+
|
|
44
|
+
Returns:
|
|
45
|
+
:class:`ConversationResponse`: Conversation Response
|
|
46
|
+
"""
|
|
47
|
+
url = f"{self.MSG_URL}/network/Xbox/users/me/conversations/users/xuid({xuid})"
|
|
48
|
+
params = {"maxItems": max_items}
|
|
49
|
+
resp = await self.client.session.get(
|
|
50
|
+
url, params=params, headers=self.HEADERS_MESSAGE, **kwargs
|
|
51
|
+
)
|
|
52
|
+
resp.raise_for_status()
|
|
53
|
+
return ConversationResponse(**resp.json())
|
|
54
|
+
|
|
55
|
+
async def delete_conversation(
|
|
56
|
+
self, conversation_id: str, horizon: str, **kwargs
|
|
57
|
+
) -> bool:
|
|
58
|
+
"""
|
|
59
|
+
Delete message
|
|
60
|
+
|
|
61
|
+
**NOTE**: Returns HTTP Status Code **200** on success
|
|
62
|
+
|
|
63
|
+
Args:
|
|
64
|
+
conversation_id: Message Id
|
|
65
|
+
horizon: Delete horizon from get conversation response
|
|
66
|
+
|
|
67
|
+
Returns: True on success, False otherwise
|
|
68
|
+
"""
|
|
69
|
+
url = f"{self.MSG_URL}/network/Xbox/users/me/conversations/horizon"
|
|
70
|
+
post_data = {
|
|
71
|
+
"conversations": [
|
|
72
|
+
{
|
|
73
|
+
"conversationId": conversation_id,
|
|
74
|
+
"conversationType": "OneToOne",
|
|
75
|
+
"horizonType": "Delete",
|
|
76
|
+
"horizon": horizon,
|
|
77
|
+
}
|
|
78
|
+
]
|
|
79
|
+
}
|
|
80
|
+
resp = await self.client.session.put(
|
|
81
|
+
url, json=post_data, headers=self.HEADERS_HORIZON, **kwargs
|
|
82
|
+
)
|
|
83
|
+
return resp.status_code == 200
|
|
84
|
+
|
|
85
|
+
async def delete_message(
|
|
86
|
+
self, conversation_id: str, message_id: str, **kwargs
|
|
87
|
+
) -> bool:
|
|
88
|
+
"""
|
|
89
|
+
Delete message
|
|
90
|
+
|
|
91
|
+
**NOTE**: Returns HTTP Status Code **200** on success
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
conversation_id: Conversation Id
|
|
95
|
+
message_id: Message Id
|
|
96
|
+
|
|
97
|
+
Returns: True on success, False otherwise
|
|
98
|
+
"""
|
|
99
|
+
url = f"{self.MSG_URL}/network/Xbox/users/me/conversations/{conversation_id}/messages/{message_id}"
|
|
100
|
+
resp = await self.client.session.delete(
|
|
101
|
+
url, headers=self.HEADERS_MESSAGE, **kwargs
|
|
102
|
+
)
|
|
103
|
+
return resp.status_code == 200
|
|
104
|
+
|
|
105
|
+
async def send_message(
|
|
106
|
+
self, xuid: str, message_text: str, **kwargs
|
|
107
|
+
) -> SendMessageResponse:
|
|
108
|
+
"""
|
|
109
|
+
Send message to an xuid
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
xuid: Xuid
|
|
113
|
+
message_text: Message text
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
:class:`SendMessageResponse`: Send Message Response
|
|
117
|
+
"""
|
|
118
|
+
if len(message_text) > 256:
|
|
119
|
+
raise ValueError("Message text exceeds max length of 256 chars")
|
|
120
|
+
|
|
121
|
+
url = f"{self.MSG_URL}/network/Xbox/users/me/conversations/users/xuid({xuid})"
|
|
122
|
+
post_data = {
|
|
123
|
+
"parts": [
|
|
124
|
+
{
|
|
125
|
+
"contentType": "text",
|
|
126
|
+
"version": 0,
|
|
127
|
+
"text": message_text,
|
|
128
|
+
}
|
|
129
|
+
]
|
|
130
|
+
}
|
|
131
|
+
resp = await self.client.session.post(
|
|
132
|
+
url, json=post_data, headers=self.HEADERS_MESSAGE, **kwargs
|
|
133
|
+
)
|
|
134
|
+
resp.raise_for_status()
|
|
135
|
+
return SendMessageResponse(**resp.json())
|