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,257 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# ────────────[ CHIZOBA ]────────────────────────────
|
|
3
|
+
# | Email : chizoba2026@hotmail.com
|
|
4
|
+
# | File : federation.py
|
|
5
|
+
# | License | MIT License © 2026 Chizoba
|
|
6
|
+
# | Brief | Federation and session management APIs
|
|
7
|
+
# ────────────────★─────────────────────────────────
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from typing import Optional, Dict, Any, List
|
|
11
|
+
from .telemetry import report_error, report_usage
|
|
12
|
+
from .debug import debug_function, debug_print
|
|
13
|
+
|
|
14
|
+
class FederationMixin:
|
|
15
|
+
"""Mixin class for federation and session management functionality."""
|
|
16
|
+
|
|
17
|
+
# Federation endpoint
|
|
18
|
+
FEDERATION_BASE_URL = "https://federation-eur.gameloft.com"
|
|
19
|
+
|
|
20
|
+
@debug_function
|
|
21
|
+
def get_active_sessions(self, credentials: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
22
|
+
"""
|
|
23
|
+
Get active game sessions from federation server.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
credentials: Optional list of user credentials to filter by
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
Session information with owner details
|
|
30
|
+
"""
|
|
31
|
+
try:
|
|
32
|
+
url = f"{self.FEDERATION_BASE_URL}/sessions/{self.client_id}"
|
|
33
|
+
|
|
34
|
+
# Always provide credentials parameter based on API discovery
|
|
35
|
+
if credentials:
|
|
36
|
+
credentials_str = ",".join(credentials)
|
|
37
|
+
else:
|
|
38
|
+
# Use current user's credential if none provided
|
|
39
|
+
user_credential = self._token_data.get("user_credential")
|
|
40
|
+
if not user_credential:
|
|
41
|
+
# Extract from access_token if needed
|
|
42
|
+
token_parts = self._token_data["access_token"].split(",")
|
|
43
|
+
for part in token_parts:
|
|
44
|
+
if "anonymous:" in part:
|
|
45
|
+
user_credential = part.strip()
|
|
46
|
+
break
|
|
47
|
+
credentials_str = user_credential if user_credential else ""
|
|
48
|
+
|
|
49
|
+
params = {
|
|
50
|
+
"access_token": self._token_data["access_token"],
|
|
51
|
+
"credentials": credentials_str
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
debug_print(f"🔍 Getting active sessions for {self.client_id}", "info")
|
|
55
|
+
result = self._make_request("GET", url, params=params)
|
|
56
|
+
|
|
57
|
+
if result:
|
|
58
|
+
sessions = result.get("sessions", [])
|
|
59
|
+
debug_print(f"✅ Found {len(sessions)} active sessions", "success")
|
|
60
|
+
|
|
61
|
+
# Analyze sessions
|
|
62
|
+
session_info = []
|
|
63
|
+
for session in sessions:
|
|
64
|
+
owner = session.get("owner", {})
|
|
65
|
+
session_data = {
|
|
66
|
+
"session_id": session.get("created"),
|
|
67
|
+
"client_id": session.get("client_id"),
|
|
68
|
+
"created": session.get("created"),
|
|
69
|
+
"owner_info": {
|
|
70
|
+
"fed_id": owner.get("fed_id"),
|
|
71
|
+
"credential": owner.get("credential"),
|
|
72
|
+
"name": owner.get("name"),
|
|
73
|
+
"server_type": owner.get("server_type"),
|
|
74
|
+
"country": owner.get("country")
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
session_info.append(session_data)
|
|
78
|
+
|
|
79
|
+
report_usage("get_active_sessions", True, 0, {
|
|
80
|
+
"sessions_found": len(sessions),
|
|
81
|
+
"client_id": self.client_id
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
"sessions": session_info,
|
|
86
|
+
"total_count": len(sessions),
|
|
87
|
+
"client_id": self.client_id
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {"sessions": [], "total_count": 0}
|
|
91
|
+
|
|
92
|
+
except Exception as e:
|
|
93
|
+
debug_print(f"❌ Failed to get active sessions: {e}", "error")
|
|
94
|
+
report_error(e, "get_active_sessions", {
|
|
95
|
+
"client_id": self.client_id,
|
|
96
|
+
"credentials_count": len(credentials) if credentials else 0
|
|
97
|
+
})
|
|
98
|
+
raise
|
|
99
|
+
|
|
100
|
+
@debug_function
|
|
101
|
+
def get_my_sessions(self) -> Dict[str, Any]:
|
|
102
|
+
"""
|
|
103
|
+
Get sessions for the current user.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
User's active sessions
|
|
107
|
+
"""
|
|
108
|
+
try:
|
|
109
|
+
# Use current user's credential
|
|
110
|
+
user_credential = self._token_data.get("user_credential")
|
|
111
|
+
if not user_credential:
|
|
112
|
+
# Extract from access_token if needed
|
|
113
|
+
token_parts = self._token_data["access_token"].split(",")
|
|
114
|
+
for part in token_parts:
|
|
115
|
+
if "anonymous:" in part:
|
|
116
|
+
user_credential = part.strip()
|
|
117
|
+
break
|
|
118
|
+
|
|
119
|
+
if user_credential:
|
|
120
|
+
return self.get_active_sessions([user_credential])
|
|
121
|
+
else:
|
|
122
|
+
debug_print("⚠️ Could not determine user credential", "warning")
|
|
123
|
+
return {"sessions": [], "total_count": 0}
|
|
124
|
+
|
|
125
|
+
except Exception as e:
|
|
126
|
+
debug_print(f"❌ Failed to get user sessions: {e}", "error")
|
|
127
|
+
report_error(e, "get_my_sessions", {})
|
|
128
|
+
raise
|
|
129
|
+
|
|
130
|
+
@debug_function
|
|
131
|
+
def get_session_statistics(self) -> Dict[str, Any]:
|
|
132
|
+
"""
|
|
133
|
+
Get statistics about active sessions.
|
|
134
|
+
|
|
135
|
+
Returns:
|
|
136
|
+
Session statistics and analytics
|
|
137
|
+
"""
|
|
138
|
+
try:
|
|
139
|
+
sessions_data = self.get_active_sessions()
|
|
140
|
+
sessions = sessions_data.get("sessions", [])
|
|
141
|
+
|
|
142
|
+
if not sessions:
|
|
143
|
+
return {"error": "No sessions found"}
|
|
144
|
+
|
|
145
|
+
# Analyze session data
|
|
146
|
+
total_sessions = len(sessions)
|
|
147
|
+
|
|
148
|
+
# Server type distribution
|
|
149
|
+
server_types = {}
|
|
150
|
+
countries = {}
|
|
151
|
+
|
|
152
|
+
for session in sessions:
|
|
153
|
+
owner_info = session.get("owner_info", {})
|
|
154
|
+
|
|
155
|
+
# Count server types
|
|
156
|
+
server_type = owner_info.get("server_type", "unknown")
|
|
157
|
+
server_types[server_type] = server_types.get(server_type, 0) + 1
|
|
158
|
+
|
|
159
|
+
# Count countries
|
|
160
|
+
country = owner_info.get("country", "unknown")
|
|
161
|
+
countries[country] = countries.get(country, 0) + 1
|
|
162
|
+
|
|
163
|
+
# Recent sessions (last 5)
|
|
164
|
+
recent_sessions = sorted(sessions, key=lambda x: x.get("created", ""), reverse=True)[:5]
|
|
165
|
+
|
|
166
|
+
stats = {
|
|
167
|
+
"total_sessions": total_sessions,
|
|
168
|
+
"client_id": self.client_id,
|
|
169
|
+
"server_type_distribution": server_types,
|
|
170
|
+
"country_distribution": countries,
|
|
171
|
+
"recent_sessions": recent_sessions,
|
|
172
|
+
"analysis_timestamp": __import__('datetime').datetime.now().isoformat()
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
debug_print(f"✅ Session statistics analyzed: {total_sessions} sessions", "success")
|
|
176
|
+
return stats
|
|
177
|
+
|
|
178
|
+
except Exception as e:
|
|
179
|
+
debug_print(f"❌ Failed to analyze session statistics: {e}", "error")
|
|
180
|
+
report_error(e, "get_session_statistics", {})
|
|
181
|
+
raise
|
|
182
|
+
|
|
183
|
+
@debug_function
|
|
184
|
+
def check_session_status(self, fed_id: str) -> Dict[str, Any]:
|
|
185
|
+
"""
|
|
186
|
+
Check status of a specific session by federation ID.
|
|
187
|
+
|
|
188
|
+
Args:
|
|
189
|
+
fed_id: Federation ID to check
|
|
190
|
+
|
|
191
|
+
Returns:
|
|
192
|
+
Session status information
|
|
193
|
+
"""
|
|
194
|
+
try:
|
|
195
|
+
sessions_data = self.get_active_sessions()
|
|
196
|
+
sessions = sessions_data.get("sessions", [])
|
|
197
|
+
|
|
198
|
+
# Find session by fed_id
|
|
199
|
+
target_session = None
|
|
200
|
+
for session in sessions:
|
|
201
|
+
if session.get("owner_info", {}).get("fed_id") == fed_id:
|
|
202
|
+
target_session = session
|
|
203
|
+
break
|
|
204
|
+
|
|
205
|
+
if target_session:
|
|
206
|
+
debug_print(f"✅ Found session for fed_id: {fed_id}", "success")
|
|
207
|
+
return {
|
|
208
|
+
"found": True,
|
|
209
|
+
"session": target_session,
|
|
210
|
+
"status": "active"
|
|
211
|
+
}
|
|
212
|
+
else:
|
|
213
|
+
debug_print(f"⚠️ No session found for fed_id: {fed_id}", "warning")
|
|
214
|
+
return {
|
|
215
|
+
"found": False,
|
|
216
|
+
"status": "not_found"
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
debug_print(f"❌ Failed to check session status: {e}", "error")
|
|
221
|
+
report_error(e, "check_session_status", {"fed_id": fed_id})
|
|
222
|
+
raise
|
|
223
|
+
|
|
224
|
+
@debug_function
|
|
225
|
+
def get_server_type_sessions(self, server_type: str) -> Dict[str, Any]:
|
|
226
|
+
"""
|
|
227
|
+
Get sessions filtered by server type.
|
|
228
|
+
|
|
229
|
+
Args:
|
|
230
|
+
server_type: Server type to filter (ts, mp_server, etc.)
|
|
231
|
+
|
|
232
|
+
Returns:
|
|
233
|
+
Filtered sessions by server type
|
|
234
|
+
"""
|
|
235
|
+
try:
|
|
236
|
+
sessions_data = self.get_active_sessions()
|
|
237
|
+
sessions = sessions_data.get("sessions", [])
|
|
238
|
+
|
|
239
|
+
# Filter by server type
|
|
240
|
+
filtered_sessions = []
|
|
241
|
+
for session in sessions:
|
|
242
|
+
owner_info = session.get("owner_info", {})
|
|
243
|
+
if owner_info.get("server_type") == server_type:
|
|
244
|
+
filtered_sessions.append(session)
|
|
245
|
+
|
|
246
|
+
debug_print(f"✅ Found {len(filtered_sessions)} {server_type} sessions", "success")
|
|
247
|
+
|
|
248
|
+
return {
|
|
249
|
+
"server_type": server_type,
|
|
250
|
+
"sessions": filtered_sessions,
|
|
251
|
+
"total_count": len(filtered_sessions)
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
except Exception as e:
|
|
255
|
+
debug_print(f"❌ Failed to get {server_type} sessions: {e}", "error")
|
|
256
|
+
report_error(e, "get_server_type_sessions", {"server_type": server_type})
|
|
257
|
+
raise
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# ────────────[ CHIZOBA ]────────────────────────────
|
|
3
|
+
# | Email : chizoba2026@hotmail.com
|
|
4
|
+
# | File : federation_quick.py
|
|
5
|
+
# | License | MIT License © 2026 Chizoba
|
|
6
|
+
# | Brief | Quick functions for federation and session management
|
|
7
|
+
# ────────────────★─────────────────────────────────
|
|
8
|
+
|
|
9
|
+
from typing import Optional, Dict, Any, List
|
|
10
|
+
from .simple_client import SimpleMC5Client
|
|
11
|
+
from .telemetry import report_usage
|
|
12
|
+
from .debug import debug_function, debug_print
|
|
13
|
+
|
|
14
|
+
@debug_function
|
|
15
|
+
def quick_get_active_sessions(username: str, password: str, credentials: Optional[List[str]] = None) -> Optional[Dict[str, Any]]:
|
|
16
|
+
"""
|
|
17
|
+
Quick function to get active game sessions.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
username: MC5 username
|
|
21
|
+
password: MC5 password
|
|
22
|
+
credentials: Optional list of user credentials to filter by
|
|
23
|
+
|
|
24
|
+
Returns:
|
|
25
|
+
Session information or None if failed
|
|
26
|
+
"""
|
|
27
|
+
try:
|
|
28
|
+
with SimpleMC5Client(username, password) as client:
|
|
29
|
+
if client.connect():
|
|
30
|
+
sessions = client.client.get_active_sessions(credentials)
|
|
31
|
+
debug_print(f"✅ Retrieved {sessions.get('total_count', 0)} active sessions", "success")
|
|
32
|
+
return sessions
|
|
33
|
+
return None
|
|
34
|
+
except Exception as e:
|
|
35
|
+
debug_print(f"❌ Failed to get active sessions: {e}", "error")
|
|
36
|
+
return None
|
|
37
|
+
|
|
38
|
+
@debug_function
|
|
39
|
+
def quick_get_my_sessions(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
40
|
+
"""
|
|
41
|
+
Quick function to get current user's sessions.
|
|
42
|
+
|
|
43
|
+
Args:
|
|
44
|
+
username: MC5 username
|
|
45
|
+
password: MC5 password
|
|
46
|
+
|
|
47
|
+
Returns:
|
|
48
|
+
User's sessions or None if failed
|
|
49
|
+
"""
|
|
50
|
+
try:
|
|
51
|
+
with SimpleMC5Client(username, password) as client:
|
|
52
|
+
if client.connect():
|
|
53
|
+
sessions = client.client.get_my_sessions()
|
|
54
|
+
debug_print(f"✅ Retrieved {sessions.get('total_count', 0)} user sessions", "success")
|
|
55
|
+
return sessions
|
|
56
|
+
return None
|
|
57
|
+
except Exception as e:
|
|
58
|
+
debug_print(f"❌ Failed to get user sessions: {e}", "error")
|
|
59
|
+
return None
|
|
60
|
+
|
|
61
|
+
@debug_function
|
|
62
|
+
def quick_get_session_statistics(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
63
|
+
"""
|
|
64
|
+
Quick function to get session statistics.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
username: MC5 username
|
|
68
|
+
password: MC5 password
|
|
69
|
+
|
|
70
|
+
Returns:
|
|
71
|
+
Session statistics or None if failed
|
|
72
|
+
"""
|
|
73
|
+
try:
|
|
74
|
+
with SimpleMC5Client(username, password) as client:
|
|
75
|
+
if client.connect():
|
|
76
|
+
stats = client.client.get_session_statistics()
|
|
77
|
+
debug_print(f"✅ Session statistics analyzed: {stats.get('total_sessions', 0)} sessions", "success")
|
|
78
|
+
return stats
|
|
79
|
+
return None
|
|
80
|
+
except Exception as e:
|
|
81
|
+
debug_print(f"❌ Failed to get session statistics: {e}", "error")
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
@debug_function
|
|
85
|
+
def quick_check_session_status(username: str, password: str, fed_id: str) -> Optional[Dict[str, Any]]:
|
|
86
|
+
"""
|
|
87
|
+
Quick function to check status of a specific session.
|
|
88
|
+
|
|
89
|
+
Args:
|
|
90
|
+
username: MC5 username
|
|
91
|
+
password: MC5 password
|
|
92
|
+
fed_id: Federation ID to check
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Session status or None if failed
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
with SimpleMC5Client(username, password) as client:
|
|
99
|
+
if client.connect():
|
|
100
|
+
status = client.client.check_session_status(fed_id)
|
|
101
|
+
if status.get("found"):
|
|
102
|
+
debug_print(f"✅ Session found: {status.get('status')}", "success")
|
|
103
|
+
else:
|
|
104
|
+
debug_print(f"⚠️ Session not found", "warning")
|
|
105
|
+
return status
|
|
106
|
+
return None
|
|
107
|
+
except Exception as e:
|
|
108
|
+
debug_print(f"❌ Failed to check session status: {e}", "error")
|
|
109
|
+
return None
|
|
110
|
+
|
|
111
|
+
@debug_function
|
|
112
|
+
def quick_get_server_type_sessions(username: str, password: str, server_type: str) -> Optional[Dict[str, Any]]:
|
|
113
|
+
"""
|
|
114
|
+
Quick function to get sessions by server type.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
username: MC5 username
|
|
118
|
+
password: MC5 password
|
|
119
|
+
server_type: Server type (ts, mp_server, etc.)
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Filtered sessions or None if failed
|
|
123
|
+
"""
|
|
124
|
+
try:
|
|
125
|
+
with SimpleMC5Client(username, password) as client:
|
|
126
|
+
if client.connect():
|
|
127
|
+
sessions = client.client.get_server_type_sessions(server_type)
|
|
128
|
+
debug_print(f"✅ Found {sessions.get('total_count', 0)} {server_type} sessions", "success")
|
|
129
|
+
return sessions
|
|
130
|
+
return None
|
|
131
|
+
except Exception as e:
|
|
132
|
+
debug_print(f"❌ Failed to get {server_type} sessions: {e}", "error")
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
@debug_function
|
|
136
|
+
def quick_analyze_session_activity(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
137
|
+
"""
|
|
138
|
+
Quick function to analyze overall session activity.
|
|
139
|
+
|
|
140
|
+
Args:
|
|
141
|
+
username: MC5 username
|
|
142
|
+
password: MC5 password
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
Activity analysis or None if failed
|
|
146
|
+
"""
|
|
147
|
+
try:
|
|
148
|
+
with SimpleMC5Client(username, password) as client:
|
|
149
|
+
if client.connect():
|
|
150
|
+
# Get all sessions
|
|
151
|
+
all_sessions = client.client.get_active_sessions()
|
|
152
|
+
sessions = all_sessions.get("sessions", [])
|
|
153
|
+
|
|
154
|
+
if not sessions:
|
|
155
|
+
return {"error": "No sessions found"}
|
|
156
|
+
|
|
157
|
+
# Analyze activity patterns
|
|
158
|
+
total_sessions = len(sessions)
|
|
159
|
+
|
|
160
|
+
# Server type analysis
|
|
161
|
+
server_analysis = {}
|
|
162
|
+
country_analysis = {}
|
|
163
|
+
|
|
164
|
+
for session in sessions:
|
|
165
|
+
owner_info = session.get("owner_info", {})
|
|
166
|
+
|
|
167
|
+
# Server types
|
|
168
|
+
server_type = owner_info.get("server_type", "unknown")
|
|
169
|
+
server_analysis[server_type] = server_analysis.get(server_type, 0) + 1
|
|
170
|
+
|
|
171
|
+
# Countries
|
|
172
|
+
country = owner_info.get("country", "unknown")
|
|
173
|
+
country_analysis[country] = country_analysis.get(country, 0) + 1
|
|
174
|
+
|
|
175
|
+
# Find most active server type
|
|
176
|
+
most_active_server = max(server_analysis.items(), key=lambda x: x[1]) if server_analysis else ("unknown", 0)
|
|
177
|
+
|
|
178
|
+
# Find most active country
|
|
179
|
+
most_active_country = max(country_analysis.items(), key=lambda x: x[1]) if country_analysis else ("unknown", 0)
|
|
180
|
+
|
|
181
|
+
analysis = {
|
|
182
|
+
"total_sessions": total_sessions,
|
|
183
|
+
"server_type_distribution": server_analysis,
|
|
184
|
+
"country_distribution": country_analysis,
|
|
185
|
+
"most_active_server_type": most_active_server[0],
|
|
186
|
+
"most_active_server_count": most_active_server[1],
|
|
187
|
+
"most_active_country": most_active_country[0],
|
|
188
|
+
"most_active_country_count": most_active_country[1],
|
|
189
|
+
"analysis_timestamp": __import__('datetime').datetime.now().isoformat()
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
debug_print(f"✅ Activity analysis complete: {total_sessions} sessions", "success")
|
|
193
|
+
return analysis
|
|
194
|
+
|
|
195
|
+
return None
|
|
196
|
+
except Exception as e:
|
|
197
|
+
debug_print(f"❌ Failed to analyze session activity: {e}", "error")
|
|
198
|
+
return None
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
# ────────────[ CHIZOBA ]────────────────────────────
|
|
2
|
+
# | Email : chizoba2026@hotmail.com
|
|
3
|
+
# | File : pc_storage_client.py
|
|
4
|
+
# | License : MIT License © 2026 Chizoba
|
|
5
|
+
# | Brief | PC storage admin client for MC5 API
|
|
6
|
+
# ────────────────★─────────────────────────────────
|
|
7
|
+
|
|
8
|
+
"""
|
|
9
|
+
PC Storage Admin Client for MC5 API with storage, storage_restricted, and storage_admin scopes.
|
|
10
|
+
Provides advanced squad management capabilities for PC platform.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from typing import Optional, Dict, Any, List
|
|
14
|
+
from .client import MC5Client
|
|
15
|
+
from .platform import Platform
|
|
16
|
+
from .storage_admin import StorageAdminMixin
|
|
17
|
+
from .auth import TokenGenerator
|
|
18
|
+
from .exceptions import MC5APIError
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class PCStorageClient(MC5Client, StorageAdminMixin):
|
|
22
|
+
"""
|
|
23
|
+
PC Storage Admin Client with advanced squad management capabilities.
|
|
24
|
+
|
|
25
|
+
This client uses storage scopes to provide admin-level squad management
|
|
26
|
+
including deletion, comprehensive updates, and audit capabilities.
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
username: Optional[str] = None,
|
|
32
|
+
password: Optional[str] = None,
|
|
33
|
+
device_id: Optional[str] = None,
|
|
34
|
+
scope: str = "alert auth chat leaderboard_ro lobby message session social config storage storage_restricted storage_admin tracking_bi feed storage leaderboard_admin social_eve social soc transaction schedule lottery voice matchmaker",
|
|
35
|
+
auto_refresh: bool = True,
|
|
36
|
+
timeout: int = 30
|
|
37
|
+
):
|
|
38
|
+
"""
|
|
39
|
+
Initialize PC Storage Admin Client.
|
|
40
|
+
|
|
41
|
+
Args:
|
|
42
|
+
username: MC5 username (default: game:mc5_system)
|
|
43
|
+
password: Account password (default: admin)
|
|
44
|
+
device_id: Device ID (generated if not provided)
|
|
45
|
+
scope: Permission scopes with storage capabilities
|
|
46
|
+
auto_refresh: Whether to auto-refresh tokens
|
|
47
|
+
timeout: Request timeout in seconds
|
|
48
|
+
debug_mode: Enable debug mode
|
|
49
|
+
"""
|
|
50
|
+
# Default to game:mc5_system for storage admin
|
|
51
|
+
if username is None:
|
|
52
|
+
username = "game:mc5_system"
|
|
53
|
+
if password is None:
|
|
54
|
+
password = "admin"
|
|
55
|
+
|
|
56
|
+
# Set storage-specific attributes before calling parent constructor
|
|
57
|
+
self._storage_scope = scope
|
|
58
|
+
self._device_id = device_id or ""
|
|
59
|
+
self._username = username
|
|
60
|
+
self._password = password
|
|
61
|
+
|
|
62
|
+
# Initialize with PC platform
|
|
63
|
+
super().__init__(
|
|
64
|
+
username=username,
|
|
65
|
+
password=password,
|
|
66
|
+
platform=Platform.PC,
|
|
67
|
+
client_id="1875:55979:6.0.0a:windows:windows",
|
|
68
|
+
auto_refresh=auto_refresh,
|
|
69
|
+
timeout=timeout
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
# Override token generation for storage admin
|
|
73
|
+
self.token_generator = TokenGenerator()
|
|
74
|
+
|
|
75
|
+
# Set storage base URL
|
|
76
|
+
self._storage_base_url = "https://eur-seshat.gameloft.com"
|
|
77
|
+
|
|
78
|
+
def authenticate(
|
|
79
|
+
self,
|
|
80
|
+
username: Optional[str] = None,
|
|
81
|
+
password: Optional[str] = None,
|
|
82
|
+
device_id: Optional[str] = None
|
|
83
|
+
) -> Dict[str, Any]:
|
|
84
|
+
"""
|
|
85
|
+
Authenticate with storage admin scopes.
|
|
86
|
+
|
|
87
|
+
Args:
|
|
88
|
+
username: Username (uses instance default if None)
|
|
89
|
+
password: Password (uses instance default if None)
|
|
90
|
+
device_id: Device ID (uses instance default if None)
|
|
91
|
+
|
|
92
|
+
Returns:
|
|
93
|
+
Authentication response with storage scopes
|
|
94
|
+
"""
|
|
95
|
+
username = username or self._username
|
|
96
|
+
password = password or self._password
|
|
97
|
+
device_id = device_id or self._device_id
|
|
98
|
+
|
|
99
|
+
try:
|
|
100
|
+
token_data = self.token_generator.generate_token(
|
|
101
|
+
username=username,
|
|
102
|
+
password=password,
|
|
103
|
+
device_id=device_id,
|
|
104
|
+
scope=self._storage_scope
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
self._token_data = token_data
|
|
108
|
+
self._username = username
|
|
109
|
+
|
|
110
|
+
# Update session headers
|
|
111
|
+
self.session.headers.update({
|
|
112
|
+
"User-Agent": f"MC5-PC-Storage-Admin/1.0.17",
|
|
113
|
+
"Accept": "application/json",
|
|
114
|
+
"Content-Type": "application/json"
|
|
115
|
+
})
|
|
116
|
+
|
|
117
|
+
return token_data
|
|
118
|
+
|
|
119
|
+
except Exception as e:
|
|
120
|
+
raise MC5APIError(f"Storage admin authentication failed: {e}")
|
|
121
|
+
|
|
122
|
+
def get_storage_info(self) -> Dict[str, Any]:
|
|
123
|
+
"""
|
|
124
|
+
Get current storage admin information.
|
|
125
|
+
|
|
126
|
+
Returns:
|
|
127
|
+
Storage admin information including scopes and capabilities
|
|
128
|
+
"""
|
|
129
|
+
if not self._token_data:
|
|
130
|
+
return {"error": "Not authenticated"}
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
"username": self._username,
|
|
134
|
+
"platform": self.get_platform_info()["platform"],
|
|
135
|
+
"scopes": self._token_data.get("scopes", []),
|
|
136
|
+
"client_id": self.client_id,
|
|
137
|
+
"has_storage": "storage" in self._token_data.get("scopes", []),
|
|
138
|
+
"has_storage_restricted": "storage_restricted" in self._token_data.get("scopes", []),
|
|
139
|
+
"has_storage_admin": "storage_admin" in self._token_data.get("scopes", []),
|
|
140
|
+
"capabilities": self._get_storage_capabilities()
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
def _get_storage_capabilities(self) -> List[str]:
|
|
144
|
+
"""Get available storage capabilities based on scopes."""
|
|
145
|
+
scopes = self._token_data.get("scopes", [])
|
|
146
|
+
capabilities = []
|
|
147
|
+
|
|
148
|
+
if "storage" in scopes:
|
|
149
|
+
capabilities.extend([
|
|
150
|
+
"read_squad_info",
|
|
151
|
+
"update_squad_info",
|
|
152
|
+
"get_all_squads",
|
|
153
|
+
"get_squad_audit_log"
|
|
154
|
+
])
|
|
155
|
+
|
|
156
|
+
if "storage_restricted" in scopes:
|
|
157
|
+
capabilities.extend([
|
|
158
|
+
"limited_squad_updates",
|
|
159
|
+
"read_squad_audit_log"
|
|
160
|
+
])
|
|
161
|
+
|
|
162
|
+
if "storage_admin" in scopes:
|
|
163
|
+
capabilities.extend([
|
|
164
|
+
"delete_squad",
|
|
165
|
+
"batch_delete_squads",
|
|
166
|
+
"transfer_squad_ownership",
|
|
167
|
+
"full_squad_management",
|
|
168
|
+
"admin_audit_access"
|
|
169
|
+
])
|
|
170
|
+
|
|
171
|
+
return capabilities
|
|
172
|
+
|
|
173
|
+
def quick_squad_management_report(self, squad_id: str) -> Dict[str, Any]:
|
|
174
|
+
"""
|
|
175
|
+
Generate a comprehensive squad management report.
|
|
176
|
+
|
|
177
|
+
Args:
|
|
178
|
+
squad_id: Squad ID to analyze
|
|
179
|
+
|
|
180
|
+
Returns:
|
|
181
|
+
Comprehensive squad report
|
|
182
|
+
"""
|
|
183
|
+
try:
|
|
184
|
+
# Get squad info
|
|
185
|
+
squad_info = self.get_squad_storage_info(squad_id)
|
|
186
|
+
|
|
187
|
+
# Get audit log if available
|
|
188
|
+
audit_log = None
|
|
189
|
+
if self._has_storage_capability("get_squad_audit_log"):
|
|
190
|
+
try:
|
|
191
|
+
audit_log = self.get_squad_audit_log(squad_id, limit=10)
|
|
192
|
+
except:
|
|
193
|
+
audit_log = {"error": "Audit log not available"}
|
|
194
|
+
|
|
195
|
+
return {
|
|
196
|
+
"squad_id": squad_id,
|
|
197
|
+
"squad_info": squad_info,
|
|
198
|
+
"audit_log": audit_log,
|
|
199
|
+
"storage_capabilities": self._get_storage_capabilities(),
|
|
200
|
+
"available_actions": self._get_available_actions(squad_id)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
except Exception as e:
|
|
204
|
+
return {"error": f"Failed to generate report: {e}"}
|
|
205
|
+
|
|
206
|
+
def _has_storage_capability(self, capability: str) -> bool:
|
|
207
|
+
"""Check if a specific storage capability is available."""
|
|
208
|
+
return capability in self._get_storage_capabilities()
|
|
209
|
+
|
|
210
|
+
def _get_available_actions(self, squad_id: str) -> List[str]:
|
|
211
|
+
"""Get available actions for a squad based on storage capabilities."""
|
|
212
|
+
actions = []
|
|
213
|
+
|
|
214
|
+
if self._has_storage_capability("read_squad_info"):
|
|
215
|
+
actions.append("read_info")
|
|
216
|
+
|
|
217
|
+
if self._has_storage_capability("update_squad_info"):
|
|
218
|
+
actions.append("update_info")
|
|
219
|
+
|
|
220
|
+
if self._has_storage_capability("delete_squad"):
|
|
221
|
+
actions.append("delete")
|
|
222
|
+
|
|
223
|
+
if self._has_storage_capability("transfer_squad_ownership"):
|
|
224
|
+
actions.append("transfer_ownership")
|
|
225
|
+
|
|
226
|
+
if self._has_storage_capability("get_squad_audit_log"):
|
|
227
|
+
actions.append("view_audit_log")
|
|
228
|
+
|
|
229
|
+
return actions
|