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