mc5-api-client 1.0.17__py3-none-any.whl → 1.0.19__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.
- mc5_api_client/__init__.py +195 -7
- mc5_api_client/client.py +92 -16
- mc5_api_client/easy_mc5.py +682 -0
- mc5_api_client/pc_storage_client.py +229 -0
- mc5_api_client/pc_storage_quick.py +234 -0
- mc5_api_client/platform.py +3 -4
- mc5_api_client/storage_admin.py +285 -0
- {mc5_api_client-1.0.17.dist-info → mc5_api_client-1.0.19.dist-info}/METADATA +165 -20
- {mc5_api_client-1.0.17.dist-info → mc5_api_client-1.0.19.dist-info}/RECORD +13 -9
- {mc5_api_client-1.0.17.dist-info → mc5_api_client-1.0.19.dist-info}/WHEEL +0 -0
- {mc5_api_client-1.0.17.dist-info → mc5_api_client-1.0.19.dist-info}/entry_points.txt +0 -0
- {mc5_api_client-1.0.17.dist-info → mc5_api_client-1.0.19.dist-info}/licenses/LICENSE +0 -0
- {mc5_api_client-1.0.17.dist-info → mc5_api_client-1.0.19.dist-info}/top_level.txt +0 -0
mc5_api_client/__init__.py
CHANGED
|
@@ -15,7 +15,7 @@ messaging, and more.
|
|
|
15
15
|
|
|
16
16
|
from typing import Optional, Dict, Any
|
|
17
17
|
|
|
18
|
-
__version__ = "1.0.
|
|
18
|
+
__version__ = "1.0.19"
|
|
19
19
|
__author__ = "Chizoba"
|
|
20
20
|
__email__ = "chizoba2026@hotmail.com"
|
|
21
21
|
__license__ = "MIT"
|
|
@@ -87,6 +87,9 @@ from .client import MC5Client
|
|
|
87
87
|
from .auth import TokenGenerator
|
|
88
88
|
from .exceptions import MC5APIError, AuthenticationError, RateLimitError
|
|
89
89
|
from .help import help, examples, quick_reference
|
|
90
|
+
|
|
91
|
+
# Add MC5ApiClient as an alias for backward compatibility
|
|
92
|
+
MC5ApiClient = MC5Client
|
|
90
93
|
from .admin_client import (
|
|
91
94
|
AdminMC5Client,
|
|
92
95
|
create_admin_client,
|
|
@@ -94,11 +97,185 @@ from .admin_client import (
|
|
|
94
97
|
quick_add_squad_rating,
|
|
95
98
|
quick_update_player_score
|
|
96
99
|
)
|
|
100
|
+
from .pc_storage_client import PCStorageClient
|
|
101
|
+
from .storage_admin import StorageAdminMixin
|
|
102
|
+
from .easy_mc5 import MC5Easy
|
|
97
103
|
|
|
98
104
|
# Encrypted token convenience functions
|
|
99
105
|
from .auth import TokenGenerator
|
|
100
106
|
|
|
101
|
-
|
|
107
|
+
# Convenience functions for global ID operations
|
|
108
|
+
def get_global_id(device_id: str = None, device_type: str = "w10", hdidfv: str = None) -> str:
|
|
109
|
+
"""
|
|
110
|
+
Get a global device ID from Gameloft's global ID service.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
device_id: Your device ID (optional, will generate one if not provided)
|
|
114
|
+
device_type: Device type (w10, android, ios, etc.)
|
|
115
|
+
hdidfv: Hardware ID fingerprint (optional)
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
Global device ID string
|
|
119
|
+
"""
|
|
120
|
+
import requests
|
|
121
|
+
|
|
122
|
+
# Build request parameters
|
|
123
|
+
params = {
|
|
124
|
+
"source": "Identifiers_6.0.0",
|
|
125
|
+
"client_id": "1875:55979:6.0.0a:windows:windows",
|
|
126
|
+
"device_type": device_type,
|
|
127
|
+
"global_device_id": device_id or "1364509832654538259",
|
|
128
|
+
"hdidfv": hdidfv or "76dfc72e-7850-4d9e-b79c-9861c7e3ea20"
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
headers = {
|
|
132
|
+
"Accept": "*/*",
|
|
133
|
+
"Accept-Encoding": "gzip;q=1.0, deflate;q=1.0, identity;q=0.5, *;q=0"
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try:
|
|
137
|
+
response = requests.get(
|
|
138
|
+
"https://gdid.datalake.gameloft.com/assign_global_id/",
|
|
139
|
+
params=params,
|
|
140
|
+
headers=headers,
|
|
141
|
+
timeout=30
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if response.status_code == 200:
|
|
145
|
+
return response.text.strip()
|
|
146
|
+
else:
|
|
147
|
+
return ""
|
|
148
|
+
except Exception:
|
|
149
|
+
return ""
|
|
150
|
+
|
|
151
|
+
def generate_device_id() -> str:
|
|
152
|
+
"""
|
|
153
|
+
Generate a unique device ID for the current device.
|
|
154
|
+
|
|
155
|
+
Returns:
|
|
156
|
+
Generated device ID string
|
|
157
|
+
"""
|
|
158
|
+
import uuid
|
|
159
|
+
|
|
160
|
+
# Generate a unique device ID
|
|
161
|
+
device_id = f"mc5_{uuid.uuid4().hex[:16]}"
|
|
162
|
+
return device_id
|
|
163
|
+
|
|
164
|
+
def create_federation_session(username: str = None, password: str = None, device_id: str = None) -> Dict[str, Any]:
|
|
165
|
+
"""
|
|
166
|
+
Create a federation session for MC5 game launch.
|
|
167
|
+
|
|
168
|
+
Args:
|
|
169
|
+
username: MC5 username (can use MC5_USERNAME env var)
|
|
170
|
+
password: MC5 password (can use MC5_PASSWORD env var)
|
|
171
|
+
device_id: Device ID for the session (optional)
|
|
172
|
+
|
|
173
|
+
Returns:
|
|
174
|
+
Dictionary with session information
|
|
175
|
+
"""
|
|
176
|
+
import os
|
|
177
|
+
import requests
|
|
178
|
+
|
|
179
|
+
# Use environment variables if not provided
|
|
180
|
+
username = username or os.getenv('MC5_USERNAME')
|
|
181
|
+
password = password or os.getenv('MC5_PASSWORD')
|
|
182
|
+
|
|
183
|
+
if not username or not password:
|
|
184
|
+
return {"error": "Username and password are required"}
|
|
185
|
+
|
|
186
|
+
try:
|
|
187
|
+
# Build request parameters
|
|
188
|
+
params = {
|
|
189
|
+
"password": password,
|
|
190
|
+
"device_id": device_id or "1364509832654538259"
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
headers = {
|
|
194
|
+
"Accept": "*/*",
|
|
195
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
# Build URL with encoded credential
|
|
199
|
+
encoded_credential = username.replace(":", "%3A")
|
|
200
|
+
url = f"https://federation-eur.gameloft.com/sessions/1875%3A55979%3A6.0.0a%3Awindows%3Awindows/{encoded_credential}"
|
|
201
|
+
|
|
202
|
+
# Make request
|
|
203
|
+
response = requests.post(
|
|
204
|
+
url,
|
|
205
|
+
data=params,
|
|
206
|
+
headers=headers,
|
|
207
|
+
timeout=30
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
if response.status_code == 200:
|
|
211
|
+
return response.json()
|
|
212
|
+
else:
|
|
213
|
+
return {"error": f"HTTP {response.status_code}", "response": response.text}
|
|
214
|
+
|
|
215
|
+
except Exception as e:
|
|
216
|
+
return {"error": str(e)}
|
|
217
|
+
|
|
218
|
+
def locate_service(service: str) -> str:
|
|
219
|
+
"""
|
|
220
|
+
Locate a service endpoint for MC5.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
service: Service name (leaderboard, matchmaker, auth, social, alert, message, lobby, gs, sp)
|
|
224
|
+
|
|
225
|
+
Returns:
|
|
226
|
+
Service endpoint URL or empty string if not found
|
|
227
|
+
"""
|
|
228
|
+
import requests
|
|
229
|
+
|
|
230
|
+
try:
|
|
231
|
+
# Build request parameters
|
|
232
|
+
params = {
|
|
233
|
+
"service": service,
|
|
234
|
+
"client_id": "1875:55979:6.0.0a:windows:windows"
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
headers = {
|
|
238
|
+
"Accept": "*/*"
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# Make request
|
|
242
|
+
response = requests.get(
|
|
243
|
+
"https://vgold-eur.gameloft.com/1875:55979:6.0.0a:windows:windows/locate",
|
|
244
|
+
params=params,
|
|
245
|
+
headers=headers,
|
|
246
|
+
timeout=30
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
if response.status_code == 200:
|
|
250
|
+
return response.text.strip()
|
|
251
|
+
else:
|
|
252
|
+
return ""
|
|
253
|
+
|
|
254
|
+
except Exception:
|
|
255
|
+
return ""
|
|
256
|
+
|
|
257
|
+
def get_all_services() -> Dict[str, str]:
|
|
258
|
+
"""
|
|
259
|
+
Get all available service endpoints.
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Dictionary mapping service names to endpoints
|
|
263
|
+
"""
|
|
264
|
+
services = [
|
|
265
|
+
"leaderboard", "matchmaker", "auth", "social",
|
|
266
|
+
"alert", "message", "lobby", "gs", "sp"
|
|
267
|
+
]
|
|
268
|
+
|
|
269
|
+
service_endpoints = {}
|
|
270
|
+
|
|
271
|
+
for service in services:
|
|
272
|
+
endpoint = locate_service(service)
|
|
273
|
+
if endpoint:
|
|
274
|
+
service_endpoints[service] = endpoint
|
|
275
|
+
|
|
276
|
+
return service_endpoints
|
|
277
|
+
|
|
278
|
+
def generate_encrypted_token(
|
|
102
279
|
username: str,
|
|
103
280
|
password: str,
|
|
104
281
|
device_id: Optional[str] = None,
|
|
@@ -106,7 +283,7 @@ def quick_generate_encrypted_token(
|
|
|
106
283
|
nonce: str = "*"
|
|
107
284
|
) -> Dict[str, Any]:
|
|
108
285
|
"""
|
|
109
|
-
|
|
286
|
+
Generate and encrypt an access token.
|
|
110
287
|
|
|
111
288
|
Args:
|
|
112
289
|
username: MC5 username
|
|
@@ -125,12 +302,12 @@ def quick_generate_encrypted_token(
|
|
|
125
302
|
finally:
|
|
126
303
|
token_gen.close()
|
|
127
304
|
|
|
128
|
-
def
|
|
305
|
+
def encrypt_token(
|
|
129
306
|
access_token: str,
|
|
130
307
|
nonce: str = "*"
|
|
131
308
|
) -> str:
|
|
132
309
|
"""
|
|
133
|
-
|
|
310
|
+
Encrypt an existing access token.
|
|
134
311
|
|
|
135
312
|
Args:
|
|
136
313
|
access_token: Raw access token string
|
|
@@ -148,6 +325,7 @@ def quick_encrypt_token(
|
|
|
148
325
|
|
|
149
326
|
__all__ = [
|
|
150
327
|
"MC5Client",
|
|
328
|
+
"MC5ApiClient",
|
|
151
329
|
"SimpleMC5Client",
|
|
152
330
|
"AdminMC5Client",
|
|
153
331
|
"batch_search_players",
|
|
@@ -216,6 +394,16 @@ __all__ = [
|
|
|
216
394
|
"help",
|
|
217
395
|
"examples",
|
|
218
396
|
"quick_reference",
|
|
219
|
-
"
|
|
220
|
-
"
|
|
397
|
+
"generate_encrypted_token",
|
|
398
|
+
"encrypt_token",
|
|
399
|
+
"MC5Easy",
|
|
400
|
+
"quick_connect",
|
|
401
|
+
"check_my_daily_tasks",
|
|
402
|
+
"get_my_mc5_profile",
|
|
403
|
+
"find_mc5_player",
|
|
404
|
+
"get_global_id",
|
|
405
|
+
"generate_device_id",
|
|
406
|
+
"create_federation_session",
|
|
407
|
+
"locate_service",
|
|
408
|
+
"get_all_services"
|
|
221
409
|
]
|
mc5_api_client/client.py
CHANGED
|
@@ -27,6 +27,7 @@ from .alerts import AlertsMixin
|
|
|
27
27
|
from .account import AccountMixin
|
|
28
28
|
from .transfer import TransferMixin
|
|
29
29
|
from .platform import Platform, get_platform_config, detect_platform_from_client_id
|
|
30
|
+
from .storage_admin import StorageAdminMixin
|
|
30
31
|
from .exceptions import (
|
|
31
32
|
MC5APIError,
|
|
32
33
|
AuthenticationError,
|
|
@@ -37,7 +38,7 @@ from .exceptions import (
|
|
|
37
38
|
)
|
|
38
39
|
|
|
39
40
|
|
|
40
|
-
class MC5Client(SquadBattleMixin, FederationMixin, AlertsMixin, AccountMixin, TransferMixin):
|
|
41
|
+
class MC5Client(SquadBattleMixin, FederationMixin, AlertsMixin, AccountMixin, TransferMixin, StorageAdminMixin):
|
|
41
42
|
"""
|
|
42
43
|
Comprehensive MC5 API client with support for all major endpoints.
|
|
43
44
|
"""
|
|
@@ -1572,31 +1573,84 @@ class MC5Client(SquadBattleMixin, FederationMixin, AlertsMixin, AccountMixin, Tr
|
|
|
1572
1573
|
|
|
1573
1574
|
# Utility Methods
|
|
1574
1575
|
|
|
1575
|
-
def
|
|
1576
|
+
def close(self):
|
|
1577
|
+
"""Close the HTTP session and cleanup resources."""
|
|
1578
|
+
if self.session:
|
|
1579
|
+
self.session.close()
|
|
1580
|
+
if self.token_generator:
|
|
1581
|
+
self.token_generator.close()
|
|
1582
|
+
|
|
1583
|
+
# Server Status Methods (for compatibility)
|
|
1584
|
+
|
|
1585
|
+
def get_server_status(self) -> Dict[str, Any]:
|
|
1576
1586
|
"""
|
|
1577
|
-
Get
|
|
1587
|
+
Get server status information.
|
|
1578
1588
|
|
|
1579
1589
|
Returns:
|
|
1580
|
-
|
|
1590
|
+
Dictionary with server status
|
|
1581
1591
|
"""
|
|
1582
|
-
|
|
1583
|
-
|
|
1592
|
+
try:
|
|
1593
|
+
# Try to get events as a basic connectivity test
|
|
1594
|
+
events = self.get_events()
|
|
1595
|
+
return {
|
|
1596
|
+
"status": "online",
|
|
1597
|
+
"message": "MC5 servers are online",
|
|
1598
|
+
"events_count": len(events),
|
|
1599
|
+
"timestamp": time.time()
|
|
1600
|
+
}
|
|
1601
|
+
except Exception as e:
|
|
1602
|
+
return {
|
|
1603
|
+
"status": "offline",
|
|
1604
|
+
"message": f"Server status check failed: {str(e)}",
|
|
1605
|
+
"timestamp": time.time()
|
|
1606
|
+
}
|
|
1584
1607
|
|
|
1585
|
-
def
|
|
1608
|
+
def get_online_players(self) -> Dict[str, Any]:
|
|
1586
1609
|
"""
|
|
1587
|
-
|
|
1610
|
+
Get online players information (estimated).
|
|
1588
1611
|
|
|
1589
1612
|
Returns:
|
|
1590
|
-
|
|
1613
|
+
Dictionary with online player count
|
|
1591
1614
|
"""
|
|
1592
|
-
|
|
1615
|
+
try:
|
|
1616
|
+
# Get events as a proxy for activity
|
|
1617
|
+
events = self.get_events()
|
|
1618
|
+
# This is an estimate - actual player count isn't directly available
|
|
1619
|
+
return {
|
|
1620
|
+
"online_players": "Unknown",
|
|
1621
|
+
"active_events": len(events),
|
|
1622
|
+
"message": "Player count not directly available via API",
|
|
1623
|
+
"timestamp": time.time()
|
|
1624
|
+
}
|
|
1625
|
+
except Exception as e:
|
|
1626
|
+
return {
|
|
1627
|
+
"online_players": 0,
|
|
1628
|
+
"error": str(e),
|
|
1629
|
+
"timestamp": time.time()
|
|
1630
|
+
}
|
|
1593
1631
|
|
|
1594
|
-
def
|
|
1595
|
-
"""
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1632
|
+
def get_server_info(self) -> Dict[str, Any]:
|
|
1633
|
+
"""
|
|
1634
|
+
Get general server information.
|
|
1635
|
+
|
|
1636
|
+
Returns:
|
|
1637
|
+
Dictionary with server information
|
|
1638
|
+
"""
|
|
1639
|
+
try:
|
|
1640
|
+
return {
|
|
1641
|
+
"server": "Modern Combat 5",
|
|
1642
|
+
"region": "Europe",
|
|
1643
|
+
"api_version": "1.0.18",
|
|
1644
|
+
"endpoints": list(self.BASE_URLS.keys()),
|
|
1645
|
+
"client_id": self.client_id,
|
|
1646
|
+
"authenticated": bool(self.access_token),
|
|
1647
|
+
"timestamp": time.time()
|
|
1648
|
+
}
|
|
1649
|
+
except Exception as e:
|
|
1650
|
+
return {
|
|
1651
|
+
"error": str(e),
|
|
1652
|
+
"timestamp": time.time()
|
|
1653
|
+
}
|
|
1600
1654
|
|
|
1601
1655
|
# Events API
|
|
1602
1656
|
|
|
@@ -2271,6 +2325,28 @@ class MC5Client(SquadBattleMixin, FederationMixin, AlertsMixin, AccountMixin, Tr
|
|
|
2271
2325
|
response = self._make_request("GET", url, params=params)
|
|
2272
2326
|
return response if isinstance(response, list) else []
|
|
2273
2327
|
|
|
2328
|
+
def get_squad_members(self, squad_id: str, offset: int = 0, limit: int = 100) -> Dict[str, Any]:
|
|
2329
|
+
"""
|
|
2330
|
+
Get squad members list.
|
|
2331
|
+
|
|
2332
|
+
Args:
|
|
2333
|
+
squad_id: Squad ID
|
|
2334
|
+
offset: Offset for pagination
|
|
2335
|
+
limit: Number of members to retrieve
|
|
2336
|
+
|
|
2337
|
+
Returns:
|
|
2338
|
+
Squad members list
|
|
2339
|
+
"""
|
|
2340
|
+
url = f"{self.BASE_URLS['osiris']}/groups/{squad_id}/members"
|
|
2341
|
+
params = {
|
|
2342
|
+
"access_token": self._token_data['access_token'],
|
|
2343
|
+
"group_id": squad_id,
|
|
2344
|
+
"offset": str(offset),
|
|
2345
|
+
"limit": str(limit)
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
return self._make_request("GET", url, params=params)
|
|
2349
|
+
|
|
2274
2350
|
def get_clan_info(self, clan_id: str) -> Dict[str, Any]:
|
|
2275
2351
|
"""
|
|
2276
2352
|
Get detailed information about a clan.
|