mc5-api-client 1.0.16__py3-none-any.whl → 1.0.17__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 +106 -1
- mc5_api_client/account.py +352 -0
- mc5_api_client/account_quick.py +246 -0
- mc5_api_client/alerts.py +336 -0
- mc5_api_client/alerts_quick.py +210 -0
- mc5_api_client/client.py +875 -34
- mc5_api_client/debug.py +259 -0
- mc5_api_client/federation.py +257 -0
- mc5_api_client/federation_quick.py +198 -0
- mc5_api_client/platform.py +109 -0
- mc5_api_client/simple_client.py +563 -19
- mc5_api_client/squad_battle.py +439 -0
- mc5_api_client/squad_battle_quick.py +223 -0
- mc5_api_client/telemetry.py +344 -0
- mc5_api_client/transfer.py +348 -0
- mc5_api_client/transfer_quick.py +280 -0
- {mc5_api_client-1.0.16.dist-info → mc5_api_client-1.0.17.dist-info}/METADATA +581 -7
- mc5_api_client-1.0.17.dist-info/RECORD +28 -0
- mc5_api_client-1.0.16.dist-info/RECORD +0 -15
- {mc5_api_client-1.0.16.dist-info → mc5_api_client-1.0.17.dist-info}/WHEEL +0 -0
- {mc5_api_client-1.0.16.dist-info → mc5_api_client-1.0.17.dist-info}/entry_points.txt +0 -0
- {mc5_api_client-1.0.16.dist-info → mc5_api_client-1.0.17.dist-info}/licenses/LICENSE +0 -0
- {mc5_api_client-1.0.16.dist-info → mc5_api_client-1.0.17.dist-info}/top_level.txt +0 -0
mc5_api_client/client.py
CHANGED
|
@@ -13,12 +13,20 @@ import time
|
|
|
13
13
|
import json
|
|
14
14
|
from typing import Optional, Dict, Any, List
|
|
15
15
|
from datetime import datetime
|
|
16
|
+
from urllib.parse import quote
|
|
16
17
|
|
|
17
18
|
import requests
|
|
18
19
|
from requests.adapters import HTTPAdapter
|
|
19
20
|
from urllib3.util.retry import Retry
|
|
20
21
|
|
|
21
22
|
from .auth import TokenGenerator
|
|
23
|
+
from .telemetry import telemetry, report_error, report_usage, is_telemetry_enabled
|
|
24
|
+
from .squad_battle import SquadBattleMixin
|
|
25
|
+
from .federation import FederationMixin
|
|
26
|
+
from .alerts import AlertsMixin
|
|
27
|
+
from .account import AccountMixin
|
|
28
|
+
from .transfer import TransferMixin
|
|
29
|
+
from .platform import Platform, get_platform_config, detect_platform_from_client_id
|
|
22
30
|
from .exceptions import (
|
|
23
31
|
MC5APIError,
|
|
24
32
|
AuthenticationError,
|
|
@@ -29,7 +37,7 @@ from .exceptions import (
|
|
|
29
37
|
)
|
|
30
38
|
|
|
31
39
|
|
|
32
|
-
class MC5Client:
|
|
40
|
+
class MC5Client(SquadBattleMixin, FederationMixin, AlertsMixin, AccountMixin, TransferMixin):
|
|
33
41
|
"""
|
|
34
42
|
Comprehensive MC5 API client with support for all major endpoints.
|
|
35
43
|
"""
|
|
@@ -42,15 +50,18 @@ class MC5Client:
|
|
|
42
50
|
"olympus": "https://eur-olympus.gameloft.com:443",
|
|
43
51
|
"iris": "https://eur-iris.gameloft.com:443",
|
|
44
52
|
"hermes": "https://eur-hermes.gameloft.com",
|
|
53
|
+
"anubisfinder": "https://eur-anubisfinder.gameloft.com:443",
|
|
45
54
|
"pandora": "https://vgold-eur.gameloft.com",
|
|
46
|
-
"game_portal": "https://app-468561b3-9ecd-4d21-8241-30ed288f4d8b.gold0009.gameloft.com"
|
|
55
|
+
"game_portal": "https://app-468561b3-9ecd-4d21-8241-30ed288f4d8b.gold0009.gameloft.com",
|
|
56
|
+
"arion": "https://eur-arion.gameloft.com"
|
|
47
57
|
}
|
|
48
58
|
|
|
49
59
|
def __init__(
|
|
50
60
|
self,
|
|
51
61
|
username: Optional[str] = None,
|
|
52
62
|
password: Optional[str] = None,
|
|
53
|
-
|
|
63
|
+
platform: Platform = Platform.PC,
|
|
64
|
+
client_id: Optional[str] = None,
|
|
54
65
|
auto_refresh: bool = True,
|
|
55
66
|
timeout: int = 30,
|
|
56
67
|
max_retries: int = 3
|
|
@@ -61,12 +72,25 @@ class MC5Client:
|
|
|
61
72
|
Args:
|
|
62
73
|
username: MC5 username
|
|
63
74
|
password: MC5 password
|
|
64
|
-
|
|
75
|
+
platform: Platform to use (PC or Android)
|
|
76
|
+
client_id: Game client identifier (auto-generated if not provided)
|
|
65
77
|
auto_refresh: Automatically refresh expired tokens
|
|
66
78
|
timeout: Request timeout in seconds
|
|
67
79
|
max_retries: Maximum number of retry attempts
|
|
68
80
|
"""
|
|
69
|
-
|
|
81
|
+
# Set platform configuration
|
|
82
|
+
self.platform = platform
|
|
83
|
+
self.platform_config = get_platform_config(platform)
|
|
84
|
+
|
|
85
|
+
# Use platform-specific client_id if not provided
|
|
86
|
+
if client_id is None:
|
|
87
|
+
self.client_id = self.platform_config.get_client_id()
|
|
88
|
+
else:
|
|
89
|
+
self.client_id = client_id
|
|
90
|
+
# Detect platform from custom client_id
|
|
91
|
+
self.platform = detect_platform_from_client_id(client_id)
|
|
92
|
+
self.platform_config = get_platform_config(self.platform)
|
|
93
|
+
|
|
70
94
|
self.auto_refresh = auto_refresh
|
|
71
95
|
self.timeout = timeout
|
|
72
96
|
self.max_retries = max_retries
|
|
@@ -92,7 +116,7 @@ class MC5Client:
|
|
|
92
116
|
|
|
93
117
|
# Default headers
|
|
94
118
|
self.session.headers.update({
|
|
95
|
-
"User-Agent":
|
|
119
|
+
"User-Agent": self.platform_config.get_user_agent(),
|
|
96
120
|
"Accept": "*/*",
|
|
97
121
|
"Content-Type": "application/x-www-form-urlencoded",
|
|
98
122
|
"Connection": "close"
|
|
@@ -122,6 +146,10 @@ class MC5Client:
|
|
|
122
146
|
self._username = username
|
|
123
147
|
self._password = password
|
|
124
148
|
|
|
149
|
+
# Generate platform-specific device ID if not provided
|
|
150
|
+
if device_id is None:
|
|
151
|
+
device_id = self.platform_config.generate_device_id()
|
|
152
|
+
|
|
125
153
|
self._token_data = self.token_generator.generate_token(
|
|
126
154
|
username=username,
|
|
127
155
|
password=password,
|
|
@@ -173,7 +201,7 @@ class MC5Client:
|
|
|
173
201
|
require_token: bool = True
|
|
174
202
|
) -> Dict[str, Any]:
|
|
175
203
|
"""
|
|
176
|
-
Make an HTTP request with proper error handling.
|
|
204
|
+
Make an HTTP request with proper error handling and telemetry.
|
|
177
205
|
|
|
178
206
|
Args:
|
|
179
207
|
method: HTTP method (GET, POST, PUT, DELETE)
|
|
@@ -191,6 +219,13 @@ class MC5Client:
|
|
|
191
219
|
MC5APIError: For API-related errors
|
|
192
220
|
NetworkError: For network-related errors
|
|
193
221
|
"""
|
|
222
|
+
import time
|
|
223
|
+
start_time = time.time()
|
|
224
|
+
function_name = f"{method.upper()} {url.split('/')[-1]}"
|
|
225
|
+
|
|
226
|
+
# if is_telemetry_enabled():
|
|
227
|
+
# print(f"🔍 Debug: Making {method} request to {url}")
|
|
228
|
+
|
|
194
229
|
if require_token:
|
|
195
230
|
self._ensure_valid_token()
|
|
196
231
|
# Add access token to params
|
|
@@ -214,29 +249,69 @@ class MC5Client:
|
|
|
214
249
|
timeout=self.timeout
|
|
215
250
|
)
|
|
216
251
|
|
|
252
|
+
duration = time.time() - start_time
|
|
253
|
+
|
|
217
254
|
# Handle different response types
|
|
218
255
|
if response.status_code == 200:
|
|
219
256
|
try:
|
|
220
|
-
|
|
257
|
+
result = response.json()
|
|
258
|
+
# if is_telemetry_enabled():
|
|
259
|
+
# print(f"✅ Debug: {method} request successful ({duration:.2f}s)")
|
|
260
|
+
report_usage(function_name, True, duration, {
|
|
261
|
+
"status_code": response.status_code,
|
|
262
|
+
"response_size": len(str(result))
|
|
263
|
+
})
|
|
264
|
+
return result
|
|
221
265
|
except json.JSONDecodeError:
|
|
222
|
-
|
|
266
|
+
result = {"response": response.text}
|
|
267
|
+
# if is_telemetry_enabled():
|
|
268
|
+
# print(f"✅ Debug: {method} request successful (text response, {duration:.2f}s)")
|
|
269
|
+
report_usage(function_name, True, duration, {
|
|
270
|
+
"status_code": response.status_code,
|
|
271
|
+
"response_type": "text"
|
|
272
|
+
})
|
|
273
|
+
return result
|
|
223
274
|
|
|
224
275
|
elif response.status_code == 401:
|
|
225
|
-
|
|
276
|
+
error = AuthenticationError("Unauthorized - invalid or expired token")
|
|
277
|
+
# if is_telemetry_enabled():
|
|
278
|
+
# print(f"❌ Debug: Authentication failed ({duration:.2f}s)")
|
|
279
|
+
report_error(error, f"{method} {url}", {
|
|
280
|
+
"status_code": response.status_code,
|
|
281
|
+
"duration": duration
|
|
282
|
+
})
|
|
283
|
+
raise error
|
|
226
284
|
|
|
227
285
|
elif response.status_code == 403:
|
|
228
286
|
try:
|
|
229
287
|
error_data = response.json()
|
|
230
|
-
|
|
288
|
+
error = InsufficientScopeError(error_data.get("error", "Insufficient permissions"))
|
|
231
289
|
except:
|
|
232
|
-
|
|
290
|
+
error = InsufficientScopeError("Insufficient permissions")
|
|
291
|
+
|
|
292
|
+
if is_telemetry_enabled():
|
|
293
|
+
print(f"❌ Debug: Permission denied ({duration:.2f}s)")
|
|
294
|
+
report_error(error, f"{method} {url}", {
|
|
295
|
+
"status_code": response.status_code,
|
|
296
|
+
"duration": duration
|
|
297
|
+
})
|
|
298
|
+
raise error
|
|
233
299
|
|
|
234
300
|
elif response.status_code == 429:
|
|
235
301
|
retry_after = response.headers.get("Retry-After")
|
|
236
|
-
|
|
302
|
+
error = RateLimitError(
|
|
237
303
|
"Rate limit exceeded",
|
|
238
304
|
retry_after=int(retry_after) if retry_after else None
|
|
239
305
|
)
|
|
306
|
+
|
|
307
|
+
if is_telemetry_enabled():
|
|
308
|
+
print(f"⏱️ Debug: Rate limit hit ({duration:.2f}s)")
|
|
309
|
+
report_error(error, f"{method} {url}", {
|
|
310
|
+
"status_code": response.status_code,
|
|
311
|
+
"retry_after": retry_after,
|
|
312
|
+
"duration": duration
|
|
313
|
+
})
|
|
314
|
+
raise error
|
|
240
315
|
|
|
241
316
|
else:
|
|
242
317
|
try:
|
|
@@ -245,18 +320,51 @@ class MC5Client:
|
|
|
245
320
|
except:
|
|
246
321
|
error_msg = response.text or "Unknown error"
|
|
247
322
|
|
|
248
|
-
|
|
323
|
+
error = MC5APIError(
|
|
249
324
|
f"Request failed with status {response.status_code}: {error_msg}",
|
|
250
325
|
status_code=response.status_code,
|
|
251
326
|
response_data=error_data if 'error_data' in locals() else {}
|
|
252
327
|
)
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
328
|
+
|
|
329
|
+
if is_telemetry_enabled():
|
|
330
|
+
print(f"❌ Debug: API error {response.status_code}: {error_msg} ({duration:.2f}s)")
|
|
331
|
+
report_error(error, f"{method} {url}", {
|
|
332
|
+
"status_code": response.status_code,
|
|
333
|
+
"error_message": error_msg,
|
|
334
|
+
"duration": duration
|
|
335
|
+
})
|
|
336
|
+
raise error
|
|
337
|
+
|
|
338
|
+
except requests.exceptions.Timeout as e:
|
|
339
|
+
error = NetworkError("Request timed out")
|
|
340
|
+
if is_telemetry_enabled():
|
|
341
|
+
print(f"⏰ Debug: Request timeout ({time.time() - start_time:.2f}s)")
|
|
342
|
+
report_error(error, f"{method} {url}", {
|
|
343
|
+
"error_type": "timeout",
|
|
344
|
+
"duration": time.time() - start_time
|
|
345
|
+
})
|
|
346
|
+
raise error
|
|
347
|
+
|
|
348
|
+
except requests.exceptions.ConnectionError as e:
|
|
349
|
+
error = NetworkError("Connection failed")
|
|
350
|
+
if is_telemetry_enabled():
|
|
351
|
+
print(f"🔌 Debug: Connection failed ({time.time() - start_time:.2f}s)")
|
|
352
|
+
report_error(error, f"{method} {url}", {
|
|
353
|
+
"error_type": "connection",
|
|
354
|
+
"duration": time.time() - start_time
|
|
355
|
+
})
|
|
356
|
+
raise error
|
|
357
|
+
|
|
258
358
|
except requests.exceptions.RequestException as e:
|
|
259
|
-
|
|
359
|
+
error = NetworkError(f"Network error: {str(e)}")
|
|
360
|
+
if is_telemetry_enabled():
|
|
361
|
+
print(f"🌐 Debug: Network error ({time.time() - start_time:.2f}s): {e}")
|
|
362
|
+
report_error(error, f"{method} {url}", {
|
|
363
|
+
"error_type": "network",
|
|
364
|
+
"error_message": str(e),
|
|
365
|
+
"duration": time.time() - start_time
|
|
366
|
+
})
|
|
367
|
+
raise error
|
|
260
368
|
|
|
261
369
|
# Profile Management
|
|
262
370
|
|
|
@@ -1007,6 +1115,126 @@ class MC5Client:
|
|
|
1007
1115
|
return self.delete_multiple_inbox_messages(message_ids)
|
|
1008
1116
|
return {"status": "success", "message": "No messages to delete"}
|
|
1009
1117
|
|
|
1118
|
+
def remove_friend(self, target_user_id: str, message: str = "removed",
|
|
1119
|
+
killsig_color: str = None, killsig_name: str = None) -> Dict[str, Any]:
|
|
1120
|
+
"""
|
|
1121
|
+
Send a friend removal notification message.
|
|
1122
|
+
|
|
1123
|
+
Args:
|
|
1124
|
+
target_user_id: Target player's user ID
|
|
1125
|
+
message: Message content (default: "removed")
|
|
1126
|
+
killsig_color: Kill signature color (optional)
|
|
1127
|
+
killsig_name: Kill signature name (optional)
|
|
1128
|
+
|
|
1129
|
+
Returns:
|
|
1130
|
+
Message result
|
|
1131
|
+
"""
|
|
1132
|
+
url = f"{self.BASE_URLS['hermes']}/messages/inbox/{quote(target_user_id)}"
|
|
1133
|
+
headers = {
|
|
1134
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
data = {
|
|
1138
|
+
'access_token': self._token_data["access_token"],
|
|
1139
|
+
'from': self.get_profile().get('credential', ''),
|
|
1140
|
+
'body': message,
|
|
1141
|
+
'reply_to': target_user_id,
|
|
1142
|
+
'_type': 'friendRemoved',
|
|
1143
|
+
'type': 'friendRemoved'
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
# Add optional parameters
|
|
1147
|
+
if killsig_color:
|
|
1148
|
+
data['_killSignColor'] = killsig_color
|
|
1149
|
+
data['killSignColor'] = killsig_color
|
|
1150
|
+
if killsig_name:
|
|
1151
|
+
data['_killSignName'] = killsig_name
|
|
1152
|
+
data['killSignName'] = killsig_name
|
|
1153
|
+
|
|
1154
|
+
response = self._make_request("POST", url, data=data)
|
|
1155
|
+
return response
|
|
1156
|
+
|
|
1157
|
+
def delete_friend_connection(self, target_user_id: str) -> Dict[str, Any]:
|
|
1158
|
+
"""
|
|
1159
|
+
Delete a friend connection (actual removal from friends list).
|
|
1160
|
+
|
|
1161
|
+
Args:
|
|
1162
|
+
target_user_id: Target player's user ID
|
|
1163
|
+
|
|
1164
|
+
Returns:
|
|
1165
|
+
Deletion result
|
|
1166
|
+
"""
|
|
1167
|
+
url = f"{self.BASE_URLS['osiris']}/accounts/me/connections/friend/{quote(target_user_id)}/delete"
|
|
1168
|
+
headers = {
|
|
1169
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
data = {
|
|
1173
|
+
'access_token': self._token_data["access_token"],
|
|
1174
|
+
'target_credential': target_user_id
|
|
1175
|
+
}
|
|
1176
|
+
|
|
1177
|
+
response = self._make_request("POST", url, data=data)
|
|
1178
|
+
return response
|
|
1179
|
+
|
|
1180
|
+
def send_friend_request(self, target_user_id: str, alert_kairos: bool = True) -> Dict[str, Any]:
|
|
1181
|
+
"""
|
|
1182
|
+
Send a friend request to another player.
|
|
1183
|
+
|
|
1184
|
+
Args:
|
|
1185
|
+
target_user_id: Target player's user ID
|
|
1186
|
+
alert_kairos: Whether to send alert notification
|
|
1187
|
+
|
|
1188
|
+
Returns:
|
|
1189
|
+
Friend request result
|
|
1190
|
+
"""
|
|
1191
|
+
url = f"{self.BASE_URLS['osiris']}/accounts/me/connections/friend"
|
|
1192
|
+
headers = {
|
|
1193
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1196
|
+
data = {
|
|
1197
|
+
'access_token': self._token_data["access_token"],
|
|
1198
|
+
'target_credential': target_user_id,
|
|
1199
|
+
'requester_credential': self.get_profile().get('credential', ''),
|
|
1200
|
+
'alert_kairos': str(alert_kairos).lower()
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
response = self._make_request("POST", url, data=data)
|
|
1204
|
+
return response
|
|
1205
|
+
|
|
1206
|
+
def get_friend_connection_status(self, target_user_id: str) -> Dict[str, Any]:
|
|
1207
|
+
"""
|
|
1208
|
+
Get the connection status with a specific friend.
|
|
1209
|
+
|
|
1210
|
+
Args:
|
|
1211
|
+
target_user_id: Target player's user ID
|
|
1212
|
+
|
|
1213
|
+
Returns:
|
|
1214
|
+
Connection status information
|
|
1215
|
+
"""
|
|
1216
|
+
url = f"{self.BASE_URLS['osiris']}/accounts/me/connections/friend/{quote(target_user_id)}"
|
|
1217
|
+
params = {
|
|
1218
|
+
'access_token': self._token_data["access_token"],
|
|
1219
|
+
'target_credential': target_user_id
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
response = self._make_request("GET", url, params=params)
|
|
1223
|
+
return response
|
|
1224
|
+
|
|
1225
|
+
def accept_friend_request(self, request_id: str) -> Dict[str, Any]:
|
|
1226
|
+
"""
|
|
1227
|
+
Accept an incoming friend request.
|
|
1228
|
+
|
|
1229
|
+
Args:
|
|
1230
|
+
request_id: Friend request ID to accept
|
|
1231
|
+
|
|
1232
|
+
Returns:
|
|
1233
|
+
Accept result with connection details
|
|
1234
|
+
"""
|
|
1235
|
+
url = f"{self.BASE_URLS['osiris']}/accounts/me/requests/{request_id}/accept"
|
|
1236
|
+
return self._make_request("POST", url)
|
|
1237
|
+
|
|
1010
1238
|
def send_squad_wall_message(self, clan_id: str, message: str, msg_type: int = 0,
|
|
1011
1239
|
player_killsig: str = None, player_killsig_color: str = None,
|
|
1012
1240
|
language: str = "en", activity_type: str = "user_post") -> Dict[str, Any]:
|
|
@@ -1066,20 +1294,6 @@ class MC5Client:
|
|
|
1066
1294
|
response = self._make_request("GET", url)
|
|
1067
1295
|
return response.get("friends", [])
|
|
1068
1296
|
|
|
1069
|
-
def send_friend_request(self, credential: str) -> Dict[str, Any]:
|
|
1070
|
-
"""
|
|
1071
|
-
Send friend request.
|
|
1072
|
-
|
|
1073
|
-
Args:
|
|
1074
|
-
credential: Target player credential
|
|
1075
|
-
|
|
1076
|
-
Returns:
|
|
1077
|
-
Friend request result
|
|
1078
|
-
"""
|
|
1079
|
-
url = f"{self.BASE_URLS['osiris']}/accounts/me/friends"
|
|
1080
|
-
data = {"credential": credential}
|
|
1081
|
-
return self._make_request("POST", url, data=data)
|
|
1082
|
-
|
|
1083
1297
|
def check_friend_status(self, credential: str) -> Dict[str, Any]:
|
|
1084
1298
|
"""
|
|
1085
1299
|
Check friend connection status.
|
|
@@ -1139,6 +1353,136 @@ class MC5Client:
|
|
|
1139
1353
|
return event
|
|
1140
1354
|
raise MC5APIError(f"Event '{event_name}' not found")
|
|
1141
1355
|
|
|
1356
|
+
def sign_up_for_event(self, event_id: str) -> Dict[str, Any]:
|
|
1357
|
+
"""
|
|
1358
|
+
Sign up for an event.
|
|
1359
|
+
|
|
1360
|
+
Args:
|
|
1361
|
+
event_id: Event ID to sign up for
|
|
1362
|
+
|
|
1363
|
+
Returns:
|
|
1364
|
+
Signup result
|
|
1365
|
+
"""
|
|
1366
|
+
url = f"{self.BASE_URLS['osiris']}/events/{event_id}/participants/me"
|
|
1367
|
+
return self._make_request("POST", url)
|
|
1368
|
+
|
|
1369
|
+
def get_event_leaderboard(self, event_id: str, offset: int = 0, limit: int = 100) -> Dict[str, Any]:
|
|
1370
|
+
"""
|
|
1371
|
+
Get the leaderboard for a specific event.
|
|
1372
|
+
|
|
1373
|
+
Args:
|
|
1374
|
+
event_id: Event ID
|
|
1375
|
+
offset: Pagination offset (default: 0)
|
|
1376
|
+
limit: Number of entries to retrieve (default: 100)
|
|
1377
|
+
|
|
1378
|
+
Returns:
|
|
1379
|
+
Event leaderboard data
|
|
1380
|
+
"""
|
|
1381
|
+
url = f"{self.BASE_URLS['olympus']}/leaderboards/desc/Leaderboard_{event_id}"
|
|
1382
|
+
params = {
|
|
1383
|
+
"offset": str(offset),
|
|
1384
|
+
"limit": str(limit)
|
|
1385
|
+
}
|
|
1386
|
+
return self._make_request("GET", url, params=params)
|
|
1387
|
+
|
|
1388
|
+
def get_my_event_leaderboard_entry(self, event_id: str, limit: int = 5) -> Dict[str, Any]:
|
|
1389
|
+
"""
|
|
1390
|
+
Get your entry and surrounding players in the event leaderboard.
|
|
1391
|
+
|
|
1392
|
+
Args:
|
|
1393
|
+
event_id: Event ID
|
|
1394
|
+
limit: Number of surrounding entries to show (default: 5)
|
|
1395
|
+
|
|
1396
|
+
Returns:
|
|
1397
|
+
Your leaderboard entry with surrounding players
|
|
1398
|
+
"""
|
|
1399
|
+
url = f"{self.BASE_URLS['olympus']}/leaderboards/desc/Leaderboard_{event_id}/me"
|
|
1400
|
+
params = {
|
|
1401
|
+
"limit": str(limit)
|
|
1402
|
+
}
|
|
1403
|
+
return self._make_request("GET", url, params=params)
|
|
1404
|
+
|
|
1405
|
+
# Squad/Group Management
|
|
1406
|
+
|
|
1407
|
+
def get_squad_invitations(self) -> List[Dict[str, Any]]:
|
|
1408
|
+
"""
|
|
1409
|
+
Get all pending squad invitations.
|
|
1410
|
+
|
|
1411
|
+
Returns:
|
|
1412
|
+
List of squad invitations
|
|
1413
|
+
"""
|
|
1414
|
+
url = f"{self.BASE_URLS['osiris']}/accounts/me/requests"
|
|
1415
|
+
params = {
|
|
1416
|
+
"request_type": "group_invitation"
|
|
1417
|
+
}
|
|
1418
|
+
response = self._make_request("GET", url, params=params)
|
|
1419
|
+
return response if isinstance(response, list) else []
|
|
1420
|
+
|
|
1421
|
+
def accept_squad_invitation(self, group_id: str, credential: str,
|
|
1422
|
+
killsig_color: str = None, killsig_id: str = None,
|
|
1423
|
+
score: str = None, xp: str = None) -> Dict[str, Any]:
|
|
1424
|
+
"""
|
|
1425
|
+
Accept a squad invitation and join the squad.
|
|
1426
|
+
|
|
1427
|
+
Args:
|
|
1428
|
+
group_id: Squad/group ID
|
|
1429
|
+
credential: Your credential (user ID)
|
|
1430
|
+
killsig_color: Kill signature color (optional)
|
|
1431
|
+
killsig_id: Kill signature ID (optional)
|
|
1432
|
+
score: Your score (optional)
|
|
1433
|
+
xp: Your XP (optional)
|
|
1434
|
+
|
|
1435
|
+
Returns:
|
|
1436
|
+
Accept result
|
|
1437
|
+
"""
|
|
1438
|
+
url = f"{self.BASE_URLS['osiris']}/groups/{group_id}/members/{quote(credential)}"
|
|
1439
|
+
data = {
|
|
1440
|
+
"credential": credential,
|
|
1441
|
+
"operation": "update"
|
|
1442
|
+
}
|
|
1443
|
+
|
|
1444
|
+
# Add optional parameters
|
|
1445
|
+
if killsig_color is not None:
|
|
1446
|
+
data["_killsig_color"] = str(killsig_color)
|
|
1447
|
+
if killsig_id is not None:
|
|
1448
|
+
data["_killsig_id"] = killsig_id
|
|
1449
|
+
if score is not None:
|
|
1450
|
+
data["_score"] = str(score)
|
|
1451
|
+
if xp is not None:
|
|
1452
|
+
data["_xp"] = str(xp)
|
|
1453
|
+
|
|
1454
|
+
return self._make_request("POST", url, data=data)
|
|
1455
|
+
|
|
1456
|
+
def delete_squad_invitation_message(self, message_id: str) -> Dict[str, Any]:
|
|
1457
|
+
"""
|
|
1458
|
+
Delete a squad invitation message after accepting/declining.
|
|
1459
|
+
|
|
1460
|
+
Args:
|
|
1461
|
+
message_id: Message ID to delete
|
|
1462
|
+
|
|
1463
|
+
Returns:
|
|
1464
|
+
Delete result
|
|
1465
|
+
"""
|
|
1466
|
+
url = f"{self.BASE_URLS['hermes']}/messages/inbox/me"
|
|
1467
|
+
params = {
|
|
1468
|
+
"msgids": message_id
|
|
1469
|
+
}
|
|
1470
|
+
return self._make_request("DELETE", url, params=params)
|
|
1471
|
+
|
|
1472
|
+
def decline_squad_invitation(self, message_id: str) -> Dict[str, Any]:
|
|
1473
|
+
"""
|
|
1474
|
+
Decline a squad invitation by rejecting the request.
|
|
1475
|
+
|
|
1476
|
+
Args:
|
|
1477
|
+
message_id: Message ID to decline/reject
|
|
1478
|
+
|
|
1479
|
+
Returns:
|
|
1480
|
+
Decline result
|
|
1481
|
+
"""
|
|
1482
|
+
# Use the correct endpoint to actually reject the invitation
|
|
1483
|
+
url = f"{self.BASE_URLS['osiris']}/accounts/me/requests/{message_id}/reject"
|
|
1484
|
+
return self._make_request("POST", url)
|
|
1485
|
+
|
|
1142
1486
|
# Leaderboard
|
|
1143
1487
|
|
|
1144
1488
|
def get_leaderboard(self, leaderboard_type: str = "ro") -> Dict[str, Any]:
|
|
@@ -2267,6 +2611,503 @@ class MC5Client:
|
|
|
2267
2611
|
|
|
2268
2612
|
return parsed_stats
|
|
2269
2613
|
|
|
2614
|
+
def get_platform_info(self) -> Dict[str, Any]:
|
|
2615
|
+
"""
|
|
2616
|
+
Get current platform information.
|
|
2617
|
+
|
|
2618
|
+
Returns:
|
|
2619
|
+
Platform configuration details
|
|
2620
|
+
"""
|
|
2621
|
+
return {
|
|
2622
|
+
"platform": self.platform.value,
|
|
2623
|
+
"client_id": self.client_id,
|
|
2624
|
+
"device_info": self.platform_config.get_device_info(),
|
|
2625
|
+
"user_agent": self.platform_config.get_user_agent(),
|
|
2626
|
+
"platform_id": self.platform_config.get_platform_id()
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
def switch_platform(self, platform: Platform) -> None:
|
|
2630
|
+
"""
|
|
2631
|
+
Switch to a different platform.
|
|
2632
|
+
|
|
2633
|
+
Args:
|
|
2634
|
+
platform: New platform to use
|
|
2635
|
+
"""
|
|
2636
|
+
self.platform = platform
|
|
2637
|
+
self.platform_config = get_platform_config(platform)
|
|
2638
|
+
self.client_id = self.platform_config.get_client_id()
|
|
2639
|
+
|
|
2640
|
+
# Update session headers
|
|
2641
|
+
self.session.headers.update({
|
|
2642
|
+
"User-Agent": self.platform_config.get_user_agent()
|
|
2643
|
+
})
|
|
2644
|
+
|
|
2645
|
+
# Reinitialize token generator with new client_id
|
|
2646
|
+
self.token_generator = TokenGenerator(
|
|
2647
|
+
client_id=self.client_id,
|
|
2648
|
+
timeout=self.timeout,
|
|
2649
|
+
max_retries=self.max_retries
|
|
2650
|
+
)
|
|
2651
|
+
|
|
2652
|
+
# Clear existing token
|
|
2653
|
+
self._token_data = None
|
|
2654
|
+
|
|
2655
|
+
def generate_platform_credential(self) -> str:
|
|
2656
|
+
"""
|
|
2657
|
+
Generate platform-specific anonymous credential.
|
|
2658
|
+
|
|
2659
|
+
Returns:
|
|
2660
|
+
Platform-appropriate anonymous credential
|
|
2661
|
+
"""
|
|
2662
|
+
if self.platform == Platform.ANDROID:
|
|
2663
|
+
return self.platform_config.generate_android_credential()
|
|
2664
|
+
else:
|
|
2665
|
+
# PC credential format
|
|
2666
|
+
import random
|
|
2667
|
+
import string
|
|
2668
|
+
return f"win8_v2_{random.randint(100000000, 999999999)}_{''.join(random.choices(string.ascii_letters + string.digits, k=20))}"
|
|
2669
|
+
|
|
2670
|
+
def subscribe_to_chat_room(self, room_id: str, language: str = "en") -> Dict[str, Any]:
|
|
2671
|
+
"""
|
|
2672
|
+
Subscribe to a chat room (Android-specific).
|
|
2673
|
+
|
|
2674
|
+
Args:
|
|
2675
|
+
room_id: Chat room ID
|
|
2676
|
+
language: Language code
|
|
2677
|
+
|
|
2678
|
+
Returns:
|
|
2679
|
+
Chat subscription information
|
|
2680
|
+
"""
|
|
2681
|
+
self._ensure_valid_token()
|
|
2682
|
+
|
|
2683
|
+
url = f"{self.BASE_URLS['arion']}/chat/rooms/{room_id}/subscribe"
|
|
2684
|
+
data = {
|
|
2685
|
+
"language": language,
|
|
2686
|
+
"access_token": self._token_data['access_token']
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
return self._make_request("POST", url, data=data)
|
|
2690
|
+
|
|
2691
|
+
def send_squad_message(
|
|
2692
|
+
self,
|
|
2693
|
+
room_id: str,
|
|
2694
|
+
message: str,
|
|
2695
|
+
kill_sign: str = "default_killsig_03",
|
|
2696
|
+
kill_sign_color: str = "1212155"
|
|
2697
|
+
) -> Dict[str, Any]:
|
|
2698
|
+
"""
|
|
2699
|
+
Send message to squad chat room (Android-specific).
|
|
2700
|
+
|
|
2701
|
+
Args:
|
|
2702
|
+
room_id: Squad room ID
|
|
2703
|
+
message: Message content
|
|
2704
|
+
kill_sign: Kill signature
|
|
2705
|
+
kill_sign_color: Kill signature color
|
|
2706
|
+
|
|
2707
|
+
Returns:
|
|
2708
|
+
Message response
|
|
2709
|
+
"""
|
|
2710
|
+
self._ensure_valid_token()
|
|
2711
|
+
|
|
2712
|
+
# Get chat URL from subscription
|
|
2713
|
+
subscription = self.subscribe_to_chat_room(room_id)
|
|
2714
|
+
chat_url = subscription.get('cmd_url')
|
|
2715
|
+
|
|
2716
|
+
if not chat_url:
|
|
2717
|
+
raise MC5APIError("Could not get chat URL from subscription")
|
|
2718
|
+
|
|
2719
|
+
data = {
|
|
2720
|
+
"_killSignColor": kill_sign_color,
|
|
2721
|
+
"_fedId": f"fed_id:{self._token_data.get('fed_id', '')}",
|
|
2722
|
+
"_senderTimestamp": str(int(time.time())),
|
|
2723
|
+
"_senderName": self._username.split(":")[-1] if ":" in self._username else "Player",
|
|
2724
|
+
"_anonId": self._username,
|
|
2725
|
+
"_killSign": kill_sign,
|
|
2726
|
+
"_": "wU\\UWZ", # Android signature
|
|
2727
|
+
"msg": message,
|
|
2728
|
+
"user": '{"nickname":"' + (self._username.split(":")[-1] if ":" in self._username else "Player") + '"}',
|
|
2729
|
+
"access_token": self._token_data['access_token']
|
|
2730
|
+
}
|
|
2731
|
+
|
|
2732
|
+
return self._make_request("POST", chat_url, data=data)
|
|
2733
|
+
|
|
2734
|
+
def send_global_message(
|
|
2735
|
+
self,
|
|
2736
|
+
message: str,
|
|
2737
|
+
nickname: str = "Player",
|
|
2738
|
+
kill_sign: str = "default_killsig_03"
|
|
2739
|
+
) -> Dict[str, Any]:
|
|
2740
|
+
"""
|
|
2741
|
+
Send message to global chat (Android-specific).
|
|
2742
|
+
|
|
2743
|
+
Args:
|
|
2744
|
+
message: Message content
|
|
2745
|
+
nickname: Player nickname
|
|
2746
|
+
kill_sign: Kill signature
|
|
2747
|
+
|
|
2748
|
+
Returns:
|
|
2749
|
+
Message response
|
|
2750
|
+
"""
|
|
2751
|
+
self._ensure_valid_token()
|
|
2752
|
+
|
|
2753
|
+
# Global chat endpoint
|
|
2754
|
+
url = "https://eur-fedex-fsg006.gameloft.com:54435/v1/chat/channels/mc5_global.en"
|
|
2755
|
+
|
|
2756
|
+
data = {
|
|
2757
|
+
"_killSignColor": "1212155",
|
|
2758
|
+
"_fedId": f"fed_id:{self._token_data.get('fed_id', '')}",
|
|
2759
|
+
"_senderTimestamp": str(int(time.time())),
|
|
2760
|
+
"_senderName": nickname,
|
|
2761
|
+
"_anonId": self._username,
|
|
2762
|
+
"_killSign": kill_sign,
|
|
2763
|
+
"_": "wU\\UWZ",
|
|
2764
|
+
"msg": message,
|
|
2765
|
+
"user": '{"nickname":"' + nickname + '"}',
|
|
2766
|
+
"access_token": self._token_data['access_token']
|
|
2767
|
+
}
|
|
2768
|
+
|
|
2769
|
+
return self._make_request("POST", url, data=data)
|
|
2770
|
+
|
|
2771
|
+
def get_squad_wall(
|
|
2772
|
+
self,
|
|
2773
|
+
room_id: str,
|
|
2774
|
+
limit: int = 20,
|
|
2775
|
+
include_fields: List[str] = None
|
|
2776
|
+
) -> List[Dict[str, Any]]:
|
|
2777
|
+
"""
|
|
2778
|
+
Get squad wall posts (Android-specific).
|
|
2779
|
+
|
|
2780
|
+
Args:
|
|
2781
|
+
room_id: Squad room ID
|
|
2782
|
+
limit: Number of posts to retrieve
|
|
2783
|
+
include_fields: Fields to include in response
|
|
2784
|
+
|
|
2785
|
+
Returns:
|
|
2786
|
+
List of wall posts
|
|
2787
|
+
"""
|
|
2788
|
+
self._ensure_valid_token()
|
|
2789
|
+
|
|
2790
|
+
if include_fields is None:
|
|
2791
|
+
include_fields = ["actor", "creation", "id", "text"]
|
|
2792
|
+
|
|
2793
|
+
url = f"{self.BASE_URLS['osiris']}/groups/{room_id}/wall"
|
|
2794
|
+
params = {
|
|
2795
|
+
"access_token": self._token_data['access_token'],
|
|
2796
|
+
"sort_type": "chronological",
|
|
2797
|
+
"limit": str(limit),
|
|
2798
|
+
"include_fields": ",".join(include_fields)
|
|
2799
|
+
}
|
|
2800
|
+
|
|
2801
|
+
return self._make_request("GET", url, params=params)
|
|
2802
|
+
|
|
2803
|
+
def send_private_message(
|
|
2804
|
+
self,
|
|
2805
|
+
target_credential: str,
|
|
2806
|
+
message: str,
|
|
2807
|
+
from_name: str = "Player",
|
|
2808
|
+
kill_sign: str = "default_killsig_03",
|
|
2809
|
+
kill_sign_color: str = "1212155"
|
|
2810
|
+
) -> Dict[str, Any]:
|
|
2811
|
+
"""
|
|
2812
|
+
Send private message (Android-specific).
|
|
2813
|
+
|
|
2814
|
+
Args:
|
|
2815
|
+
target_credential: Target user's credential
|
|
2816
|
+
message: Message content
|
|
2817
|
+
from_name: Sender name
|
|
2818
|
+
kill_sign: Kill signature
|
|
2819
|
+
kill_sign_color: Kill signature color
|
|
2820
|
+
|
|
2821
|
+
Returns:
|
|
2822
|
+
Message response
|
|
2823
|
+
"""
|
|
2824
|
+
self._ensure_valid_token()
|
|
2825
|
+
|
|
2826
|
+
url = f"{self.BASE_URLS['hermes']}/messages/inbox/{target_credential}"
|
|
2827
|
+
data = {
|
|
2828
|
+
"access_token": self._token_data['access_token'],
|
|
2829
|
+
"alert_kairos": "true",
|
|
2830
|
+
"from": from_name,
|
|
2831
|
+
"body": message,
|
|
2832
|
+
"reply_to": self._username,
|
|
2833
|
+
"_killSignColor": kill_sign_color,
|
|
2834
|
+
"_killSignName": kill_sign,
|
|
2835
|
+
"_type": "inbox"
|
|
2836
|
+
}
|
|
2837
|
+
|
|
2838
|
+
return self._make_request("POST", url, data=data)
|
|
2839
|
+
|
|
2840
|
+
def check_friend_connection(self, target_credential: str) -> Dict[str, Any]:
|
|
2841
|
+
"""
|
|
2842
|
+
Check friend connection status (Android-specific).
|
|
2843
|
+
|
|
2844
|
+
Args:
|
|
2845
|
+
target_credential: Target user's credential
|
|
2846
|
+
|
|
2847
|
+
Returns:
|
|
2848
|
+
Friend connection status
|
|
2849
|
+
"""
|
|
2850
|
+
self._ensure_valid_token()
|
|
2851
|
+
|
|
2852
|
+
url = f"{self.BASE_URLS['osiris']}/accounts/me/connections/friend/{target_credential}"
|
|
2853
|
+
params = {
|
|
2854
|
+
"access_token": self._token_data['access_token'],
|
|
2855
|
+
"target_credential": target_credential
|
|
2856
|
+
}
|
|
2857
|
+
|
|
2858
|
+
return self._make_request("GET", url, params=params)
|
|
2859
|
+
|
|
2860
|
+
def remove_friend(
|
|
2861
|
+
self,
|
|
2862
|
+
target_credential: str
|
|
2863
|
+
) -> Dict[str, Any]:
|
|
2864
|
+
"""
|
|
2865
|
+
Remove friend connection (Android-specific).
|
|
2866
|
+
|
|
2867
|
+
Args:
|
|
2868
|
+
target_credential: Target user's credential to remove
|
|
2869
|
+
|
|
2870
|
+
Returns:
|
|
2871
|
+
Friend removal response
|
|
2872
|
+
"""
|
|
2873
|
+
self._ensure_valid_token()
|
|
2874
|
+
|
|
2875
|
+
url = f"{self.BASE_URLS['osiris']}/accounts/me/connections/friend/{target_credential}/delete"
|
|
2876
|
+
data = {
|
|
2877
|
+
"access_token": self._token_data['access_token'],
|
|
2878
|
+
"target_credential": target_credential
|
|
2879
|
+
}
|
|
2880
|
+
|
|
2881
|
+
return self._make_request("POST", url, data=data)
|
|
2882
|
+
|
|
2883
|
+
def send_friend_removed_message(
|
|
2884
|
+
self,
|
|
2885
|
+
target_credential: str,
|
|
2886
|
+
message: str = "removed",
|
|
2887
|
+
from_name: str = "Player",
|
|
2888
|
+
kill_sign: str = "default_killsig_03",
|
|
2889
|
+
kill_sign_color: str = "1212155"
|
|
2890
|
+
) -> Dict[str, Any]:
|
|
2891
|
+
"""
|
|
2892
|
+
Send friend removed notification message (Android-specific).
|
|
2893
|
+
|
|
2894
|
+
Args:
|
|
2895
|
+
target_credential: Target user's credential
|
|
2896
|
+
message: Message content (default: "removed")
|
|
2897
|
+
from_name: Sender name
|
|
2898
|
+
kill_sign: Kill signature
|
|
2899
|
+
kill_sign_color: Kill signature color
|
|
2900
|
+
|
|
2901
|
+
Returns:
|
|
2902
|
+
Message response
|
|
2903
|
+
"""
|
|
2904
|
+
self._ensure_valid_token()
|
|
2905
|
+
|
|
2906
|
+
url = f"{self.BASE_URLS['hermes']}/messages/inbox/{target_credential}"
|
|
2907
|
+
data = {
|
|
2908
|
+
"access_token": self._token_data['access_token'],
|
|
2909
|
+
"alert_kairos": "true",
|
|
2910
|
+
"from": from_name,
|
|
2911
|
+
"body": message,
|
|
2912
|
+
"reply_to": self._username,
|
|
2913
|
+
"_killSignColor": kill_sign_color,
|
|
2914
|
+
"_killSignName": kill_sign,
|
|
2915
|
+
"_type": "friendRemoved"
|
|
2916
|
+
}
|
|
2917
|
+
|
|
2918
|
+
return self._make_request("POST", url, data=data)
|
|
2919
|
+
|
|
2920
|
+
def update_squad_info(
|
|
2921
|
+
self,
|
|
2922
|
+
clan_id: str,
|
|
2923
|
+
rating: Optional[int] = None,
|
|
2924
|
+
score: Optional[int] = None,
|
|
2925
|
+
name: Optional[str] = None,
|
|
2926
|
+
description: Optional[str] = None,
|
|
2927
|
+
member_count: Optional[int] = None,
|
|
2928
|
+
member_limit: Optional[int] = None,
|
|
2929
|
+
membership: Optional[str] = None,
|
|
2930
|
+
logo: Optional[str] = None,
|
|
2931
|
+
logo_color_primary: Optional[int] = None,
|
|
2932
|
+
logo_color_secondary: Optional[int] = None,
|
|
2933
|
+
min_join_value: Optional[int] = None,
|
|
2934
|
+
currency: Optional[int] = None,
|
|
2935
|
+
active_clan_label: Optional[bool] = None
|
|
2936
|
+
) -> Dict[str, Any]:
|
|
2937
|
+
"""
|
|
2938
|
+
Update squad information (Android-specific).
|
|
2939
|
+
|
|
2940
|
+
Args:
|
|
2941
|
+
clan_id: Squad/Clan ID to update
|
|
2942
|
+
rating: Squad rating value
|
|
2943
|
+
score: Squad score (may not update in-game)
|
|
2944
|
+
name: Squad name
|
|
2945
|
+
description: Squad description
|
|
2946
|
+
member_count: Current member count
|
|
2947
|
+
member_limit: Maximum member limit
|
|
2948
|
+
membership: Membership type (e.g., "owner_approved")
|
|
2949
|
+
logo: Logo ID
|
|
2950
|
+
logo_color_primary: Primary logo color
|
|
2951
|
+
logo_color_secondary: Secondary logo color
|
|
2952
|
+
min_join_value: Minimum join value
|
|
2953
|
+
currency: Squad currency
|
|
2954
|
+
active_clan_label: Whether clan label is active
|
|
2955
|
+
|
|
2956
|
+
Returns:
|
|
2957
|
+
Update response
|
|
2958
|
+
"""
|
|
2959
|
+
self._ensure_valid_token()
|
|
2960
|
+
|
|
2961
|
+
url = f"{self.BASE_URLS['osiris']}/groups/{clan_id}"
|
|
2962
|
+
|
|
2963
|
+
# Build payload with provided parameters
|
|
2964
|
+
payload = {
|
|
2965
|
+
"access_token": self._token_data['access_token'],
|
|
2966
|
+
"timestamp": str(int(time.time())),
|
|
2967
|
+
"_anonId": self._username
|
|
2968
|
+
}
|
|
2969
|
+
|
|
2970
|
+
# Add optional parameters if provided
|
|
2971
|
+
if rating is not None:
|
|
2972
|
+
payload["_rating"] = str(rating)
|
|
2973
|
+
if score is not None:
|
|
2974
|
+
payload["score"] = str(score)
|
|
2975
|
+
if name is not None:
|
|
2976
|
+
payload["name"] = name
|
|
2977
|
+
if description is not None:
|
|
2978
|
+
payload["description"] = description
|
|
2979
|
+
if member_count is not None:
|
|
2980
|
+
payload["member_count"] = str(member_count)
|
|
2981
|
+
if member_limit is not None:
|
|
2982
|
+
payload["member_limit"] = str(member_limit)
|
|
2983
|
+
if membership is not None:
|
|
2984
|
+
payload["membership"] = membership
|
|
2985
|
+
if logo is not None:
|
|
2986
|
+
payload["_logo"] = logo
|
|
2987
|
+
if logo_color_primary is not None:
|
|
2988
|
+
payload["_logo_clr_prim"] = str(logo_color_primary)
|
|
2989
|
+
if logo_color_secondary is not None:
|
|
2990
|
+
payload["_logo_clr_sec"] = str(logo_color_secondary)
|
|
2991
|
+
if min_join_value is not None:
|
|
2992
|
+
payload["_min_join_value"] = str(min_join_value)
|
|
2993
|
+
if currency is not None:
|
|
2994
|
+
payload["currency"] = str(currency)
|
|
2995
|
+
if active_clan_label is not None:
|
|
2996
|
+
payload["active_clan_label"] = "true" if active_clan_label else "false"
|
|
2997
|
+
|
|
2998
|
+
return self._make_request("POST", url, data=payload)
|
|
2999
|
+
|
|
3000
|
+
def get_squad_info(
|
|
3001
|
+
self,
|
|
3002
|
+
clan_id: str
|
|
3003
|
+
) -> Dict[str, Any]:
|
|
3004
|
+
"""
|
|
3005
|
+
Get squad information (Android-specific).
|
|
3006
|
+
|
|
3007
|
+
Args:
|
|
3008
|
+
clan_id: Squad/Clan ID to retrieve
|
|
3009
|
+
|
|
3010
|
+
Returns:
|
|
3011
|
+
Squad information
|
|
3012
|
+
"""
|
|
3013
|
+
self._ensure_valid_token()
|
|
3014
|
+
|
|
3015
|
+
url = f"{self.BASE_URLS['osiris']}/groups/{clan_id}"
|
|
3016
|
+
params = {
|
|
3017
|
+
"access_token": self._token_data['access_token']
|
|
3018
|
+
}
|
|
3019
|
+
|
|
3020
|
+
return self._make_request("GET", url, params=params)
|
|
3021
|
+
|
|
3022
|
+
def post_to_squad_wall(
|
|
3023
|
+
self,
|
|
3024
|
+
room_id: str,
|
|
3025
|
+
message: str,
|
|
3026
|
+
kill_sign: str = "default_killsig_03",
|
|
3027
|
+
kill_sign_color: int = 1212155,
|
|
3028
|
+
language: str = "en"
|
|
3029
|
+
) -> Dict[str, Any]:
|
|
3030
|
+
"""
|
|
3031
|
+
Post to squad wall (Android-specific).
|
|
3032
|
+
|
|
3033
|
+
Args:
|
|
3034
|
+
room_id: Squad room ID
|
|
3035
|
+
message: Message content
|
|
3036
|
+
kill_sign: Kill signature
|
|
3037
|
+
kill_sign_color: Kill signature color
|
|
3038
|
+
language: Language code
|
|
3039
|
+
|
|
3040
|
+
Returns:
|
|
3041
|
+
Wall post response
|
|
3042
|
+
"""
|
|
3043
|
+
self._ensure_valid_token()
|
|
3044
|
+
|
|
3045
|
+
url = f"{self.BASE_URLS['osiris']}/groups/{room_id}/wall"
|
|
3046
|
+
|
|
3047
|
+
post_data = {
|
|
3048
|
+
"msg_body": message,
|
|
3049
|
+
"msg_type": 0,
|
|
3050
|
+
"playerKillSign": kill_sign,
|
|
3051
|
+
"playerKillSignColor": kill_sign_color
|
|
3052
|
+
}
|
|
3053
|
+
|
|
3054
|
+
data = {
|
|
3055
|
+
"access_token": self._token_data['access_token'],
|
|
3056
|
+
"text": json.dumps(post_data) + "\n",
|
|
3057
|
+
"language": language,
|
|
3058
|
+
"activity_type": "user_post",
|
|
3059
|
+
"alert_kairos": "false"
|
|
3060
|
+
}
|
|
3061
|
+
|
|
3062
|
+
return self._make_request("POST", url, data=data)
|
|
3063
|
+
|
|
3064
|
+
def get_game_alias(self) -> Dict[str, Any]:
|
|
3065
|
+
"""
|
|
3066
|
+
Get game alias (Android-specific).
|
|
3067
|
+
|
|
3068
|
+
Returns:
|
|
3069
|
+
Game alias information
|
|
3070
|
+
"""
|
|
3071
|
+
self._ensure_valid_token()
|
|
3072
|
+
|
|
3073
|
+
url = f"{self.BASE_URLS['janus']}/games/mygame/alias"
|
|
3074
|
+
data = {
|
|
3075
|
+
"access_token": self._token_data['access_token']
|
|
3076
|
+
}
|
|
3077
|
+
|
|
3078
|
+
return self._make_request("POST", url, data=data)
|
|
3079
|
+
|
|
3080
|
+
def get_batch_profiles(
|
|
3081
|
+
self,
|
|
3082
|
+
credentials: List[str],
|
|
3083
|
+
include_fields: List[str] = None
|
|
3084
|
+
) -> Dict[str, Any]:
|
|
3085
|
+
"""
|
|
3086
|
+
Get batch profiles for multiple credentials (Android-specific).
|
|
3087
|
+
|
|
3088
|
+
Args:
|
|
3089
|
+
credentials: List of player credentials (fed_id:... format)
|
|
3090
|
+
include_fields: Fields to include in response
|
|
3091
|
+
|
|
3092
|
+
Returns:
|
|
3093
|
+
Batch profile data
|
|
3094
|
+
"""
|
|
3095
|
+
if include_fields is None:
|
|
3096
|
+
include_fields = ["_game_save", "inventory"]
|
|
3097
|
+
|
|
3098
|
+
# Use game portal endpoint
|
|
3099
|
+
url = f"{self.BASE_URLS['game_portal']}/1924/190/public/OfficialScripts/mc5Portal.wsgi"
|
|
3100
|
+
|
|
3101
|
+
data = {
|
|
3102
|
+
"op_code": "get_batch_profiles",
|
|
3103
|
+
"client_id": self.client_id,
|
|
3104
|
+
"credentials": ",".join(credentials),
|
|
3105
|
+
"pandora": f"https://vgold-eur.gameloft.com/{self.client_id}",
|
|
3106
|
+
"include_fields": ",".join(include_fields)
|
|
3107
|
+
}
|
|
3108
|
+
|
|
3109
|
+
return self._make_request("POST", url, data=data)
|
|
3110
|
+
|
|
2270
3111
|
def __enter__(self):
|
|
2271
3112
|
"""Context manager entry."""
|
|
2272
3113
|
return self
|