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
|
@@ -0,0 +1,682 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# ────────────[ CHIZOBA ]────────────────────────────
|
|
3
|
+
# | Email : chizoba2026@hotmail.com
|
|
4
|
+
# | File : easy_mc5.py
|
|
5
|
+
# | License : MIT License © 2026 Chizoba
|
|
6
|
+
# | Brief | Easy-to-use MC5 functions for everyday tasks
|
|
7
|
+
# ────────────────★─────────────────────────────────
|
|
8
|
+
|
|
9
|
+
"""
|
|
10
|
+
Easy MC5 - Simplified Interface for Everyday Tasks
|
|
11
|
+
==================================================
|
|
12
|
+
|
|
13
|
+
This module provides simple, one-line functions for the most common MC5 tasks.
|
|
14
|
+
No complex setup, no confusing API calls - just simple functions that work.
|
|
15
|
+
|
|
16
|
+
Quick Start:
|
|
17
|
+
from mc5_api_client.easy_mc5 import MC5Easy
|
|
18
|
+
|
|
19
|
+
# Initialize with your credentials
|
|
20
|
+
mc5 = MC5Easy("your_credential", "your_password")
|
|
21
|
+
|
|
22
|
+
# Check your daily tasks
|
|
23
|
+
tasks = mc5.check_daily_tasks()
|
|
24
|
+
|
|
25
|
+
# Get your profile
|
|
26
|
+
profile = mc5.get_my_profile()
|
|
27
|
+
|
|
28
|
+
# Search for a player
|
|
29
|
+
player = mc5.find_player("f55f")
|
|
30
|
+
|
|
31
|
+
# Send a message
|
|
32
|
+
mc5.send_message_to_friend("f55f", "Hello!")
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
import os
|
|
36
|
+
from typing import Optional, Dict, Any, List
|
|
37
|
+
from .client import MC5Client, Platform
|
|
38
|
+
from .exceptions import MC5APIError
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class MC5Easy:
|
|
42
|
+
"""
|
|
43
|
+
Easy-to-use MC5 client for everyday tasks.
|
|
44
|
+
|
|
45
|
+
Simplifies common MC5 operations into simple, one-line functions.
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, username: str = None, password: str = None, platform: Platform = Platform.PC):
|
|
49
|
+
"""
|
|
50
|
+
Initialize MC5Easy client.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
username: MC5 credential (can be set via environment variable MC5_USERNAME)
|
|
54
|
+
password: MC5 password (can be set via environment variable MC5_PASSWORD)
|
|
55
|
+
platform: Platform to use (PC or Android)
|
|
56
|
+
"""
|
|
57
|
+
self.username = username or os.getenv('MC5_USERNAME')
|
|
58
|
+
self.password = password or os.getenv('MC5_PASSWORD')
|
|
59
|
+
self.platform = platform
|
|
60
|
+
self.client = None
|
|
61
|
+
self._connected = False
|
|
62
|
+
|
|
63
|
+
if not self.username or not self.password:
|
|
64
|
+
raise ValueError("Username and password are required. Set them directly or use MC5_USERNAME and MC5_PASSWORD environment variables.")
|
|
65
|
+
|
|
66
|
+
def connect(self) -> bool:
|
|
67
|
+
"""Connect to MC5 servers."""
|
|
68
|
+
try:
|
|
69
|
+
self.client = MC5Client(
|
|
70
|
+
username=self.username,
|
|
71
|
+
password=self.password,
|
|
72
|
+
platform=self.platform
|
|
73
|
+
)
|
|
74
|
+
self.client.authenticate()
|
|
75
|
+
self._connected = True
|
|
76
|
+
return True
|
|
77
|
+
except Exception as e:
|
|
78
|
+
print(f"❌ Connection failed: {e}")
|
|
79
|
+
self._connected = False
|
|
80
|
+
return False
|
|
81
|
+
|
|
82
|
+
def is_connected(self) -> bool:
|
|
83
|
+
"""Check if connected to MC5."""
|
|
84
|
+
return self._connected and self.client is not None
|
|
85
|
+
|
|
86
|
+
def _ensure_connected(self):
|
|
87
|
+
"""Ensure we're connected to MC5."""
|
|
88
|
+
if not self.is_connected():
|
|
89
|
+
if not self.connect():
|
|
90
|
+
raise ConnectionError("Not connected to MC5. Call connect() first.")
|
|
91
|
+
|
|
92
|
+
# === PROFILE FUNCTIONS ===
|
|
93
|
+
|
|
94
|
+
def get_my_profile(self) -> Dict[str, Any]:
|
|
95
|
+
"""Get your profile information."""
|
|
96
|
+
self._ensure_connected()
|
|
97
|
+
try:
|
|
98
|
+
profile = self.client.get_profile()
|
|
99
|
+
return profile
|
|
100
|
+
except Exception as e:
|
|
101
|
+
print(f"❌ Error getting profile: {e}")
|
|
102
|
+
return {}
|
|
103
|
+
|
|
104
|
+
def get_my_stats(self) -> Dict[str, Any]:
|
|
105
|
+
"""Get your stats in a simple format."""
|
|
106
|
+
profile = self.get_my_profile()
|
|
107
|
+
if not profile:
|
|
108
|
+
return {}
|
|
109
|
+
|
|
110
|
+
return {
|
|
111
|
+
'name': profile.get('name', 'Unknown'),
|
|
112
|
+
'level': profile.get('level') or profile.get('_level') or profile.get('player_level') or 'Unknown',
|
|
113
|
+
'xp': profile.get('xp') or profile.get('_xp') or profile.get('experience') or 0,
|
|
114
|
+
'score': profile.get('score') or profile.get('_score') or profile.get('player_score') or 0,
|
|
115
|
+
'clan_id': profile.get('groups', [None])[0]
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
# === PLAYER SEARCH FUNCTIONS ===
|
|
119
|
+
|
|
120
|
+
def find_player(self, dogtag: str) -> Dict[str, Any]:
|
|
121
|
+
"""Find a player by dogtag."""
|
|
122
|
+
self._ensure_connected()
|
|
123
|
+
try:
|
|
124
|
+
player = self.client.search_player(dogtag)
|
|
125
|
+
if player and 'kills' in player:
|
|
126
|
+
return {
|
|
127
|
+
'dogtag': dogtag,
|
|
128
|
+
'found': True,
|
|
129
|
+
'kills': player.get('kills', 0),
|
|
130
|
+
'level': player.get('level') or player.get('_level') or player.get('player_level') or 'Unknown',
|
|
131
|
+
'score': player.get('score') or player.get('_score') or player.get('player_score') or 0,
|
|
132
|
+
'wins': player.get('wins', 0),
|
|
133
|
+
'losses': player.get('losses', 0),
|
|
134
|
+
'draws': player.get('draws', 0)
|
|
135
|
+
}
|
|
136
|
+
else:
|
|
137
|
+
return {'dogtag': dogtag, 'found': False}
|
|
138
|
+
except Exception as e:
|
|
139
|
+
print(f"❌ Error finding player {dogtag}: {e}")
|
|
140
|
+
return {'dogtag': dogtag, 'found': False, 'error': str(e)}
|
|
141
|
+
|
|
142
|
+
def compare_players(self, dogtag1: str, dogtag2: str) -> Dict[str, Any]:
|
|
143
|
+
"""Compare two players."""
|
|
144
|
+
player1 = self.find_player(dogtag1)
|
|
145
|
+
player2 = self.find_player(dogtag2)
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
'player1': player1,
|
|
149
|
+
'player2': player2,
|
|
150
|
+
'comparison': {
|
|
151
|
+
'player1_wins_kills': player1.get('kills', 0) > player2.get('kills', 0),
|
|
152
|
+
'player1_wins_score': player1.get('score', 0) > player2.get('score', 0),
|
|
153
|
+
'player1_wins_level': player1.get('level', 0) > player2.get('level', 0)
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
# === CLAN/SQUAD FUNCTIONS ===
|
|
158
|
+
|
|
159
|
+
def get_my_clan_members(self) -> List[Dict[str, Any]]:
|
|
160
|
+
"""Get your clan members."""
|
|
161
|
+
self._ensure_connected()
|
|
162
|
+
try:
|
|
163
|
+
# First get profile to find clan ID
|
|
164
|
+
profile = self.get_my_profile()
|
|
165
|
+
clan_id = profile.get('groups', [None])[0]
|
|
166
|
+
|
|
167
|
+
if not clan_id:
|
|
168
|
+
return []
|
|
169
|
+
|
|
170
|
+
members = self.client.get_group_members(clan_id)
|
|
171
|
+
return members
|
|
172
|
+
except Exception as e:
|
|
173
|
+
print(f"❌ Error getting clan members: {e}")
|
|
174
|
+
return []
|
|
175
|
+
|
|
176
|
+
def get_clan_stats(self) -> Dict[str, Any]:
|
|
177
|
+
"""Get clan statistics."""
|
|
178
|
+
members = self.get_my_clan_members()
|
|
179
|
+
if not members:
|
|
180
|
+
return {}
|
|
181
|
+
|
|
182
|
+
online_count = len([m for m in members if m.get('online', False)])
|
|
183
|
+
total_score = sum(int(m.get('_score') or m.get('score') or 0) for m in members)
|
|
184
|
+
avg_score = total_score // len(members) if members else 0
|
|
185
|
+
|
|
186
|
+
# Sort by score
|
|
187
|
+
sorted_members = sorted(members, key=lambda x: int(x.get('_score') or x.get('score') or 0), reverse=True)
|
|
188
|
+
|
|
189
|
+
return {
|
|
190
|
+
'total_members': len(members),
|
|
191
|
+
'online_members': online_count,
|
|
192
|
+
'total_score': total_score,
|
|
193
|
+
'average_score': avg_score,
|
|
194
|
+
'top_member': sorted_members[0] if sorted_members else None,
|
|
195
|
+
'top_5_members': sorted_members[:5]
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
# === DAILY TASKS FUNCTIONS ===
|
|
199
|
+
|
|
200
|
+
def check_daily_tasks(self) -> List[Dict[str, Any]]:
|
|
201
|
+
"""Check your daily tasks."""
|
|
202
|
+
self._ensure_connected()
|
|
203
|
+
try:
|
|
204
|
+
events = self.client.get_events()
|
|
205
|
+
daily_tasks = [e for e in events if 'daily' in e.get('name', '').lower() or 'activities' in e.get('name', '').lower()]
|
|
206
|
+
return daily_tasks
|
|
207
|
+
except Exception as e:
|
|
208
|
+
print(f"❌ Error checking daily tasks: {e}")
|
|
209
|
+
return []
|
|
210
|
+
|
|
211
|
+
def get_daily_summary(self) -> Dict[str, Any]:
|
|
212
|
+
"""Get a summary of your daily status."""
|
|
213
|
+
tasks = self.check_daily_tasks()
|
|
214
|
+
profile = self.get_my_stats()
|
|
215
|
+
clan_stats = self.get_clan_stats()
|
|
216
|
+
|
|
217
|
+
return {
|
|
218
|
+
'daily_tasks': {
|
|
219
|
+
'count': len(tasks),
|
|
220
|
+
'tasks': tasks
|
|
221
|
+
},
|
|
222
|
+
'profile': profile,
|
|
223
|
+
'clan': clan_stats
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# === MESSAGING FUNCTIONS ===
|
|
227
|
+
|
|
228
|
+
def send_message_to_friend(self, dogtag: str, message: str) -> bool:
|
|
229
|
+
"""Send a message to a friend."""
|
|
230
|
+
self._ensure_connected()
|
|
231
|
+
try:
|
|
232
|
+
result = self.client.send_private_message(dogtag, message)
|
|
233
|
+
return result is not None
|
|
234
|
+
except Exception as e:
|
|
235
|
+
print(f"❌ Error sending message to {dogtag}: {e}")
|
|
236
|
+
return False
|
|
237
|
+
|
|
238
|
+
def broadcast_to_clan(self, message: str) -> bool:
|
|
239
|
+
"""Broadcast a message to your clan wall."""
|
|
240
|
+
self._ensure_connected()
|
|
241
|
+
try:
|
|
242
|
+
profile = self.get_my_profile()
|
|
243
|
+
clan_id = profile.get('groups', [None])[0]
|
|
244
|
+
|
|
245
|
+
if not clan_id:
|
|
246
|
+
print("❌ Not in a clan")
|
|
247
|
+
return False
|
|
248
|
+
|
|
249
|
+
result = self.client.post_group_wall(clan_id, message)
|
|
250
|
+
return result is not None
|
|
251
|
+
except Exception as e:
|
|
252
|
+
print(f"❌ Error broadcasting to clan: {e}")
|
|
253
|
+
return False
|
|
254
|
+
|
|
255
|
+
# === UTILITY FUNCTIONS ===
|
|
256
|
+
|
|
257
|
+
def quick_status(self) -> str:
|
|
258
|
+
"""Get a quick status overview."""
|
|
259
|
+
try:
|
|
260
|
+
profile = self.get_my_stats()
|
|
261
|
+
tasks = self.check_daily_tasks()
|
|
262
|
+
clan_stats = self.get_clan_stats()
|
|
263
|
+
|
|
264
|
+
status = f"🎮 MC5 Status:\n"
|
|
265
|
+
status += f"👤 Player: {profile.get('name', 'Unknown')} (Level {profile.get('level', 'Unknown')})\n"
|
|
266
|
+
status += f"⭐ XP: {profile.get('xp', 0):,}\n"
|
|
267
|
+
status += f"💰 Score: {profile.get('score', 0):,}\n"
|
|
268
|
+
status += f"📅 Daily Tasks: {len(tasks)} pending\n"
|
|
269
|
+
|
|
270
|
+
if clan_stats.get('total_members', 0) > 0:
|
|
271
|
+
status += f"🏰 Clan: {clan_stats['total_members']} members ({clan_stats['online_members']} online)\n"
|
|
272
|
+
|
|
273
|
+
return status
|
|
274
|
+
except Exception as e:
|
|
275
|
+
return f"❌ Error getting status: {e}"
|
|
276
|
+
|
|
277
|
+
def run_daily_routine(self) -> Dict[str, Any]:
|
|
278
|
+
"""Run a complete daily routine."""
|
|
279
|
+
print("🚀 Running daily MC5 routine...")
|
|
280
|
+
|
|
281
|
+
results = {
|
|
282
|
+
'profile': self.get_my_stats(),
|
|
283
|
+
'daily_tasks': self.check_daily_tasks(),
|
|
284
|
+
'clan_stats': self.get_clan_stats(),
|
|
285
|
+
'status': self.quick_status()
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
print("✅ Daily routine completed!")
|
|
289
|
+
return results
|
|
290
|
+
|
|
291
|
+
def close(self):
|
|
292
|
+
"""Close the connection."""
|
|
293
|
+
if self.client:
|
|
294
|
+
self.client.close()
|
|
295
|
+
self._connected = False
|
|
296
|
+
|
|
297
|
+
def __enter__(self):
|
|
298
|
+
"""Context manager entry."""
|
|
299
|
+
return self
|
|
300
|
+
|
|
301
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
302
|
+
"""Context manager exit."""
|
|
303
|
+
self.close()
|
|
304
|
+
|
|
305
|
+
# === DEVICE ID FUNCTIONS ===
|
|
306
|
+
|
|
307
|
+
def get_global_id(self, device_id: str = None, device_type: str = "w10", hdidfv: str = None) -> str:
|
|
308
|
+
"""
|
|
309
|
+
Get a global device ID from Gameloft's global ID service.
|
|
310
|
+
|
|
311
|
+
Args:
|
|
312
|
+
device_id: Your device ID (optional, will generate one if not provided)
|
|
313
|
+
device_type: Device type (w10, android, ios, etc.)
|
|
314
|
+
hdidfv: Hardware ID fingerprint (optional)
|
|
315
|
+
|
|
316
|
+
Returns:
|
|
317
|
+
Global device ID string
|
|
318
|
+
"""
|
|
319
|
+
self._ensure_connected()
|
|
320
|
+
|
|
321
|
+
try:
|
|
322
|
+
# Build request parameters
|
|
323
|
+
params = {
|
|
324
|
+
"source": "Identifiers_6.0.0",
|
|
325
|
+
"client_id": "1875:55979:6.0.0a:windows:windows",
|
|
326
|
+
"device_type": device_type,
|
|
327
|
+
"global_device_id": device_id or "1364509832654538259",
|
|
328
|
+
"hdidfv": hdidfv or "76dfc72e-7850-4d9e-b79c-9861c7e3ea20"
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
headers = {
|
|
332
|
+
"Accept": "*/*",
|
|
333
|
+
"Accept-Encoding": "gzip;q=1.0, deflate;q=1.0, identity;q=0.5, *;q=0"
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
# Make request
|
|
337
|
+
response = self.client.session.get(
|
|
338
|
+
"https://gdid.datalake.gameloft.com/assign_global_id/",
|
|
339
|
+
params=params,
|
|
340
|
+
headers=headers,
|
|
341
|
+
timeout=30
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
if response.status_code == 200:
|
|
345
|
+
return response.text.strip()
|
|
346
|
+
else:
|
|
347
|
+
print(f"❌ Global ID request failed: {response.status_code}")
|
|
348
|
+
return ""
|
|
349
|
+
|
|
350
|
+
except Exception as e:
|
|
351
|
+
print(f"❌ Error getting global ID: {e}")
|
|
352
|
+
return ""
|
|
353
|
+
|
|
354
|
+
def get_device_info(self) -> Dict[str, Any]:
|
|
355
|
+
"""
|
|
356
|
+
Get comprehensive device information including global ID.
|
|
357
|
+
|
|
358
|
+
Returns:
|
|
359
|
+
Dictionary with device information
|
|
360
|
+
"""
|
|
361
|
+
self._ensure_connected()
|
|
362
|
+
|
|
363
|
+
try:
|
|
364
|
+
# Get global ID
|
|
365
|
+
global_id = self.get_global_id()
|
|
366
|
+
|
|
367
|
+
# Get device info from profile
|
|
368
|
+
profile = self.get_my_stats()
|
|
369
|
+
|
|
370
|
+
return {
|
|
371
|
+
"global_id": global_id,
|
|
372
|
+
"device_type": "w10", # Default for PC
|
|
373
|
+
"client_id": "1875:55979:6.0.0a:windows:windows",
|
|
374
|
+
"profile_name": profile.get('name', 'Unknown'),
|
|
375
|
+
"profile_level": profile.get('level', 'Unknown'),
|
|
376
|
+
"profile_score": profile.get('score', 0),
|
|
377
|
+
"has_clan": bool(profile.get('clan_id')),
|
|
378
|
+
"timestamp": "Unknown"
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
except Exception as e:
|
|
382
|
+
print(f"❌ Error getting device info: {e}")
|
|
383
|
+
return {"error": str(e)}
|
|
384
|
+
|
|
385
|
+
def generate_device_id(self) -> str:
|
|
386
|
+
"""
|
|
387
|
+
Generate a unique device ID for the current device.
|
|
388
|
+
|
|
389
|
+
Returns:
|
|
390
|
+
Generated device ID string
|
|
391
|
+
"""
|
|
392
|
+
import uuid
|
|
393
|
+
import random
|
|
394
|
+
import string
|
|
395
|
+
|
|
396
|
+
# Generate a unique device ID
|
|
397
|
+
device_id = f"mc5_{uuid.uuid4().hex[:16]}"
|
|
398
|
+
return device_id
|
|
399
|
+
|
|
400
|
+
# === FEDERATION SESSION FUNCTIONS ===
|
|
401
|
+
|
|
402
|
+
def create_federation_session(self, device_id: str = None) -> Dict[str, Any]:
|
|
403
|
+
"""
|
|
404
|
+
Create a federation session for MC5 game launch.
|
|
405
|
+
|
|
406
|
+
Args:
|
|
407
|
+
device_id: Device ID for the session (optional)
|
|
408
|
+
|
|
409
|
+
Returns:
|
|
410
|
+
Dictionary with session information
|
|
411
|
+
"""
|
|
412
|
+
self._ensure_connected()
|
|
413
|
+
|
|
414
|
+
try:
|
|
415
|
+
# Build request parameters
|
|
416
|
+
params = {
|
|
417
|
+
"password": self.password,
|
|
418
|
+
"device_id": device_id or "1364509832654538259"
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
headers = {
|
|
422
|
+
"Accept": "*/*",
|
|
423
|
+
"Content-Type": "application/x-www-form-urlencoded"
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
# Build URL with encoded credential
|
|
427
|
+
encoded_credential = self.username.replace(":", "%3A")
|
|
428
|
+
url = f"https://federation-eur.gameloft.com/sessions/1875%3A55979%3A6.0.0a%3Awindows%3Awindows/{encoded_credential}"
|
|
429
|
+
|
|
430
|
+
# Make request
|
|
431
|
+
response = self.client.session.post(
|
|
432
|
+
url,
|
|
433
|
+
data=params,
|
|
434
|
+
headers=headers,
|
|
435
|
+
timeout=30
|
|
436
|
+
)
|
|
437
|
+
|
|
438
|
+
if response.status_code == 200:
|
|
439
|
+
return {
|
|
440
|
+
"status": "OK",
|
|
441
|
+
"response": response.text.strip(),
|
|
442
|
+
"service_type": service_type,
|
|
443
|
+
"timestamp": params["timestamp"],
|
|
444
|
+
"domain": params["domain"]
|
|
445
|
+
}
|
|
446
|
+
else:
|
|
447
|
+
return {
|
|
448
|
+
"status": "Failed",
|
|
449
|
+
"error": f"HTTP {response.status_code}",
|
|
450
|
+
"response": response.text,
|
|
451
|
+
"service_type": service_type
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
except Exception as e:
|
|
455
|
+
print(f"❌ Error checking connection status: {e}")
|
|
456
|
+
return {"error": str(e), "service_type": service_type}
|
|
457
|
+
|
|
458
|
+
def get_connection_status_all(self) -> Dict[str, Any]:
|
|
459
|
+
"""
|
|
460
|
+
Check connection status for all services.
|
|
461
|
+
|
|
462
|
+
Returns:
|
|
463
|
+
Dictionary with all service connection statuses
|
|
464
|
+
"""
|
|
465
|
+
services = ["auth", "matchmaking", "lobby", "gs", "sp"]
|
|
466
|
+
|
|
467
|
+
connection_statuses = {}
|
|
468
|
+
|
|
469
|
+
for service in services:
|
|
470
|
+
status = self.check_connection_status(service)
|
|
471
|
+
connection_statuses[service] = status
|
|
472
|
+
|
|
473
|
+
return connection_statuses
|
|
474
|
+
|
|
475
|
+
# === ROOM FINDER FUNCTIONS ===
|
|
476
|
+
|
|
477
|
+
def find_rooms(self, clan_id: str = None, clan_battle_room: bool = False, server_type: str = "mp_server") -> List[Dict[str, Any]]:
|
|
478
|
+
"""
|
|
479
|
+
Find available rooms using AnubisFinder.
|
|
480
|
+
|
|
481
|
+
Args:
|
|
482
|
+
clan_id: Clan ID to filter rooms (optional)
|
|
483
|
+
clan_battle_room: Filter for clan battle rooms (optional)
|
|
484
|
+
server_type: Server type (mp_server)
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
List of room dictionaries
|
|
488
|
+
"""
|
|
489
|
+
self._ensure_connected()
|
|
490
|
+
|
|
491
|
+
try:
|
|
492
|
+
# Build request parameters
|
|
493
|
+
params = {
|
|
494
|
+
"_can_spectate": "false",
|
|
495
|
+
"_customs_room": "true",
|
|
496
|
+
"_event_id": "x",
|
|
497
|
+
"_joinable": "true",
|
|
498
|
+
"_public": "true",
|
|
499
|
+
"full": "false",
|
|
500
|
+
"game_started": "true",
|
|
501
|
+
"server_type": server_type
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
# Add clan filters if provided
|
|
505
|
+
if clan_id:
|
|
506
|
+
params["_clan_" + clan_id] = "true"
|
|
507
|
+
|
|
508
|
+
if clan_battle_room:
|
|
509
|
+
params["_clan_battle_room"] = "true"
|
|
510
|
+
|
|
511
|
+
headers = {
|
|
512
|
+
"Accept": "*/*"
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
# Make request
|
|
516
|
+
response = self.client.session.get(
|
|
517
|
+
"https://eur-anubisfinder.gameloft.com/rooms/1875:55979:6.0.0a:windows:windows",
|
|
518
|
+
params=params,
|
|
519
|
+
headers=headers,
|
|
520
|
+
timeout=30
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
if response.status_code == 200:
|
|
524
|
+
try:
|
|
525
|
+
rooms = response.json()
|
|
526
|
+
return rooms if isinstance(rooms, list) else []
|
|
527
|
+
except:
|
|
528
|
+
return []
|
|
529
|
+
else:
|
|
530
|
+
print(f"❌ Room finder failed: {response.status_code}")
|
|
531
|
+
return []
|
|
532
|
+
|
|
533
|
+
except Exception as e:
|
|
534
|
+
print(f"❌ Error finding rooms: {e}")
|
|
535
|
+
return []
|
|
536
|
+
|
|
537
|
+
def get_available_rooms(self, clan_id: str = None) -> Dict[str, Any]:
|
|
538
|
+
"""
|
|
539
|
+
Get comprehensive room information.
|
|
540
|
+
|
|
541
|
+
Args:
|
|
542
|
+
clan_id: Clan ID to filter rooms (optional)
|
|
543
|
+
|
|
544
|
+
Returns:
|
|
545
|
+
Dictionary with room information
|
|
546
|
+
"""
|
|
547
|
+
rooms = self.find_rooms(clan_id=clan_id)
|
|
548
|
+
|
|
549
|
+
return {
|
|
550
|
+
"total_rooms": len(rooms),
|
|
551
|
+
"rooms": rooms,
|
|
552
|
+
"clan_id": clan_id,
|
|
553
|
+
"timestamp": "2026-02-03 22:10:19"
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
def _parse_access_token(self, access_token: str) -> Dict[str, Any]:
|
|
557
|
+
"""
|
|
558
|
+
Parse the access token into its components.
|
|
559
|
+
|
|
560
|
+
Args:
|
|
561
|
+
access_token: The access token string
|
|
562
|
+
|
|
563
|
+
Returns:
|
|
564
|
+
Dictionary with parsed token components
|
|
565
|
+
"""
|
|
566
|
+
try:
|
|
567
|
+
# Split the token by commas
|
|
568
|
+
parts = access_token.split(',')
|
|
569
|
+
|
|
570
|
+
if len(parts) >= 6:
|
|
571
|
+
return {
|
|
572
|
+
"token_id": parts[0],
|
|
573
|
+
"scopes": parts[1],
|
|
574
|
+
"client_id": parts[2],
|
|
575
|
+
"timestamp": parts[3],
|
|
576
|
+
"credential": parts[4],
|
|
577
|
+
"device_id": parts[5],
|
|
578
|
+
"signature": parts[6] if len(parts) > 6 else ""
|
|
579
|
+
}
|
|
580
|
+
else:
|
|
581
|
+
return {"raw_token": access_token, "parts": parts}
|
|
582
|
+
except Exception as e:
|
|
583
|
+
return {"error": str(e), "raw_token": access_token}
|
|
584
|
+
|
|
585
|
+
def launch_game_session(self, device_id: str = None) -> Dict[str, Any]:
|
|
586
|
+
"""
|
|
587
|
+
Create a federation session and prepare for game launch.
|
|
588
|
+
|
|
589
|
+
Args:
|
|
590
|
+
device_id: Device ID for the session (optional)
|
|
591
|
+
|
|
592
|
+
Returns:
|
|
593
|
+
Dictionary with launch information
|
|
594
|
+
"""
|
|
595
|
+
session_data = self.create_federation_session(device_id)
|
|
596
|
+
|
|
597
|
+
if session_data.get("success"):
|
|
598
|
+
return {
|
|
599
|
+
"success": True,
|
|
600
|
+
"message": "Game session ready for launch",
|
|
601
|
+
"room_id": session_data.get("room_id"),
|
|
602
|
+
"controller": {
|
|
603
|
+
"host": session_data.get("controller_host"),
|
|
604
|
+
"tcp_port": session_data.get("controller_tcp_port"),
|
|
605
|
+
"http_port": session_data.get("controller_http_port"),
|
|
606
|
+
"https_port": session_data.get("controller_https_port")
|
|
607
|
+
},
|
|
608
|
+
"access_token": session_data.get("access_token"),
|
|
609
|
+
"encrypted_access_token": session_data.get("encrypted_access_token"),
|
|
610
|
+
"parsed_token": session_data.get("parsed_access_token"),
|
|
611
|
+
"launch_command": self._build_launch_command(session_data)
|
|
612
|
+
}
|
|
613
|
+
else:
|
|
614
|
+
return session_data
|
|
615
|
+
|
|
616
|
+
def _build_launch_command(self, session_data: Dict[str, Any]) -> str:
|
|
617
|
+
"""
|
|
618
|
+
Build a launch command for the MC5 game.
|
|
619
|
+
|
|
620
|
+
Args:
|
|
621
|
+
session_data: Session data from federation
|
|
622
|
+
|
|
623
|
+
Returns:
|
|
624
|
+
Launch command string
|
|
625
|
+
"""
|
|
626
|
+
host = session_data.get("controller_host", "localhost")
|
|
627
|
+
room_id = session_data.get("room_id", "")
|
|
628
|
+
access_token = session_data.get("access_token", "")
|
|
629
|
+
|
|
630
|
+
# This is a simplified launch command
|
|
631
|
+
return f"mc5://launch?host={host}&room={room_id}&token={access_token}"
|
|
632
|
+
|
|
633
|
+
def get_session_info(self, device_id: str = None) -> Dict[str, Any]:
|
|
634
|
+
"""
|
|
635
|
+
Get comprehensive session information.
|
|
636
|
+
|
|
637
|
+
Args:
|
|
638
|
+
device_id: Device ID for the session (optional)
|
|
639
|
+
|
|
640
|
+
Returns:
|
|
641
|
+
Dictionary with session information
|
|
642
|
+
"""
|
|
643
|
+
session_data = self.create_federation_session(device_id)
|
|
644
|
+
|
|
645
|
+
if session_data.get("success"):
|
|
646
|
+
return {
|
|
647
|
+
"session": session_data,
|
|
648
|
+
"device_info": self.get_device_info(),
|
|
649
|
+
"global_id": self.get_global_id(device_id),
|
|
650
|
+
"timestamp": session_data.get("parsed_access_token", {}).get("timestamp", "Unknown")
|
|
651
|
+
}
|
|
652
|
+
else:
|
|
653
|
+
return session_data
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
# === CONVENIENCE FUNCTIONS ===
|
|
657
|
+
|
|
658
|
+
def quick_connect(username: str = None, password: str = None) -> MC5Easy:
|
|
659
|
+
"""Quick connect to MC5 with credentials."""
|
|
660
|
+
mc5 = MC5Easy(username, password)
|
|
661
|
+
mc5.connect()
|
|
662
|
+
return mc5
|
|
663
|
+
|
|
664
|
+
def check_my_daily_tasks(username: str = None, password: str = None) -> List[Dict[str, Any]]:
|
|
665
|
+
"""Quick check of daily tasks."""
|
|
666
|
+
with quick_connect(username, password) as mc5:
|
|
667
|
+
return mc5.check_daily_tasks()
|
|
668
|
+
|
|
669
|
+
def get_my_mc5_profile(username: str = None, password: str = None) -> Dict[str, Any]:
|
|
670
|
+
"""Quick get of profile."""
|
|
671
|
+
with quick_connect(username, password) as mc5:
|
|
672
|
+
return mc5.get_my_stats()
|
|
673
|
+
|
|
674
|
+
def find_mc5_player(dogtag: str, username: str = None, password: str = None) -> Dict[str, Any]:
|
|
675
|
+
"""Quick find player."""
|
|
676
|
+
with quick_connect(username, password) as mc5:
|
|
677
|
+
return mc5.find_player(dogtag)
|
|
678
|
+
|
|
679
|
+
def send_mc5_message(dogtag: str, message: str, username: str = None, password: str = None) -> bool:
|
|
680
|
+
"""Quick send message."""
|
|
681
|
+
with quick_connect(username, password) as mc5:
|
|
682
|
+
return mc5.send_message_to_friend(dogtag, message)
|