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,352 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# ────────────[ CHIZOBA ]────────────────────────────
|
|
3
|
+
# | Email : chizoba2026@hotmail.com
|
|
4
|
+
# | File : account.py
|
|
5
|
+
# | License | MIT License © 2026 Chizoba
|
|
6
|
+
# | Brief | User account management and analytics
|
|
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 AccountMixin:
|
|
15
|
+
"""Mixin class for user account management and analytics."""
|
|
16
|
+
|
|
17
|
+
@debug_function
|
|
18
|
+
def get_account_info(self) -> Dict[str, Any]:
|
|
19
|
+
"""
|
|
20
|
+
Get comprehensive user account information.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
Complete account data including credentials, installations, and device history
|
|
24
|
+
"""
|
|
25
|
+
try:
|
|
26
|
+
url = f"{self.BASE_URLS['janus']}/users/me"
|
|
27
|
+
|
|
28
|
+
params = {
|
|
29
|
+
"access_token": self._token_data["access_token"]
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
debug_print("👤 Getting comprehensive account information", "info")
|
|
33
|
+
result = self._make_request("GET", url, params=params)
|
|
34
|
+
|
|
35
|
+
if result:
|
|
36
|
+
# Analyze account data
|
|
37
|
+
account_analysis = self._analyze_account_data(result)
|
|
38
|
+
|
|
39
|
+
debug_print(f"✅ Account info retrieved: {len(result.get('credentials', []))} credentials, {len(result.get('installations', []))} installations", "success")
|
|
40
|
+
|
|
41
|
+
report_usage("get_account_info", True, 0, {
|
|
42
|
+
"credentials_count": len(result.get('credentials', [])),
|
|
43
|
+
"installations_count": len(result.get('installations', [])),
|
|
44
|
+
"has_alias": bool(result.get('alias')),
|
|
45
|
+
"is_ghost": result.get('is_ghost', False)
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
"account_data": result,
|
|
50
|
+
"analysis": account_analysis
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {"account_data": {}, "analysis": {}}
|
|
54
|
+
|
|
55
|
+
except Exception as e:
|
|
56
|
+
debug_print(f"❌ Failed to get account info: {e}", "error")
|
|
57
|
+
report_error(e, "get_account_info", {})
|
|
58
|
+
raise
|
|
59
|
+
|
|
60
|
+
def _analyze_account_data(self, account_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
61
|
+
"""
|
|
62
|
+
Analyze account data for insights.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
account_data: Raw account data from API
|
|
66
|
+
|
|
67
|
+
Returns:
|
|
68
|
+
Analysis results and statistics
|
|
69
|
+
"""
|
|
70
|
+
try:
|
|
71
|
+
credentials = account_data.get("credentials", [])
|
|
72
|
+
installations = account_data.get("installations", [])
|
|
73
|
+
alias = account_data.get("alias", [])
|
|
74
|
+
|
|
75
|
+
# Analyze credentials
|
|
76
|
+
credential_analysis = self._analyze_credentials(credentials)
|
|
77
|
+
|
|
78
|
+
# Analyze installations
|
|
79
|
+
installation_analysis = self._analyze_installations(installations)
|
|
80
|
+
|
|
81
|
+
# Analyze account security
|
|
82
|
+
security_analysis = self._analyze_security(account_data)
|
|
83
|
+
|
|
84
|
+
analysis = {
|
|
85
|
+
"credentials": credential_analysis,
|
|
86
|
+
"installations": installation_analysis,
|
|
87
|
+
"security": security_analysis,
|
|
88
|
+
"account_summary": {
|
|
89
|
+
"account_id": account_data.get("account"),
|
|
90
|
+
"total_credentials": len(credentials),
|
|
91
|
+
"total_installations": len(installations),
|
|
92
|
+
"has_alias": bool(alias),
|
|
93
|
+
"alias_count": len(alias),
|
|
94
|
+
"is_ghost": account_data.get("is_ghost", False),
|
|
95
|
+
"last_login": account_data.get("last_login")
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return analysis
|
|
100
|
+
|
|
101
|
+
except Exception as e:
|
|
102
|
+
debug_print(f"❌ Failed to analyze account data: {e}", "error")
|
|
103
|
+
return {}
|
|
104
|
+
|
|
105
|
+
def _analyze_credentials(self, credentials: List[str]) -> Dict[str, Any]:
|
|
106
|
+
"""
|
|
107
|
+
Analyze user credentials.
|
|
108
|
+
|
|
109
|
+
Args:
|
|
110
|
+
credentials: List of credential strings
|
|
111
|
+
|
|
112
|
+
Returns:
|
|
113
|
+
Credential analysis
|
|
114
|
+
"""
|
|
115
|
+
try:
|
|
116
|
+
# Count credential types
|
|
117
|
+
credential_types = {}
|
|
118
|
+
microsoft_count = 0
|
|
119
|
+
anonymous_count = 0
|
|
120
|
+
|
|
121
|
+
for cred in credentials:
|
|
122
|
+
if ":" in cred:
|
|
123
|
+
cred_type = cred.split(":")[0]
|
|
124
|
+
credential_types[cred_type] = credential_types.get(cred_type, 0) + 1
|
|
125
|
+
|
|
126
|
+
if cred_type == "microsoftgraph":
|
|
127
|
+
microsoft_count += 1
|
|
128
|
+
elif cred_type == "anonymous":
|
|
129
|
+
anonymous_count += 1
|
|
130
|
+
|
|
131
|
+
# Extract unique anonymous IDs
|
|
132
|
+
anonymous_ids = []
|
|
133
|
+
for cred in credentials:
|
|
134
|
+
if cred.startswith("anonymous:"):
|
|
135
|
+
anon_id = cred.split(":")[1]
|
|
136
|
+
if anon_id not in anonymous_ids:
|
|
137
|
+
anonymous_ids.append(anon_id)
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
"total_credentials": len(credentials),
|
|
141
|
+
"credential_types": credential_types,
|
|
142
|
+
"microsoft_credentials": microsoft_count,
|
|
143
|
+
"anonymous_credentials": anonymous_count,
|
|
144
|
+
"unique_anonymous_ids": len(anonymous_ids),
|
|
145
|
+
"latest_anonymous_id": anonymous_ids[-1] if anonymous_ids else None
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
except Exception as e:
|
|
149
|
+
debug_print(f"❌ Failed to analyze credentials: {e}", "error")
|
|
150
|
+
return {"total_credentials": len(credentials)}
|
|
151
|
+
|
|
152
|
+
def _analyze_installations(self, installations: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
153
|
+
"""
|
|
154
|
+
Analyze device installations.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
installations: List of installation data
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Installation analysis
|
|
161
|
+
"""
|
|
162
|
+
try:
|
|
163
|
+
# Count by model
|
|
164
|
+
models = {}
|
|
165
|
+
countries = {}
|
|
166
|
+
languages = {}
|
|
167
|
+
resolutions = {}
|
|
168
|
+
firmware_versions = {}
|
|
169
|
+
|
|
170
|
+
valid_installations = 0
|
|
171
|
+
|
|
172
|
+
for install in installations:
|
|
173
|
+
if install.get("model"): # Only count valid installations
|
|
174
|
+
valid_installations += 1
|
|
175
|
+
|
|
176
|
+
model = install.get("model", "Unknown")
|
|
177
|
+
models[model] = models.get(model, 0) + 1
|
|
178
|
+
|
|
179
|
+
country = install.get("country", "Unknown")
|
|
180
|
+
countries[country] = countries.get(country, 0) + 1
|
|
181
|
+
|
|
182
|
+
language = install.get("language", "Unknown")
|
|
183
|
+
languages[language] = languages.get(language, 0) + 1
|
|
184
|
+
|
|
185
|
+
resolution = install.get("resolution", "Unknown")
|
|
186
|
+
resolutions[resolution] = resolutions.get(resolution, 0) + 1
|
|
187
|
+
|
|
188
|
+
firmware = install.get("firmware", "Unknown")
|
|
189
|
+
firmware_versions[firmware] = firmware_versions.get(firmware, 0) + 1
|
|
190
|
+
|
|
191
|
+
# Find most common values
|
|
192
|
+
most_common_model = max(models.items(), key=lambda x: x[1]) if models else ("Unknown", 0)
|
|
193
|
+
most_common_country = max(countries.items(), key=lambda x: x[1]) if countries else ("Unknown", 0)
|
|
194
|
+
most_common_language = max(languages.items(), key=lambda x: x[1]) if languages else ("Unknown", 0)
|
|
195
|
+
|
|
196
|
+
return {
|
|
197
|
+
"total_installations": len(installations),
|
|
198
|
+
"valid_installations": valid_installations,
|
|
199
|
+
"device_models": models,
|
|
200
|
+
"countries": countries,
|
|
201
|
+
"languages": languages,
|
|
202
|
+
"resolutions": resolutions,
|
|
203
|
+
"firmware_versions": firmware_versions,
|
|
204
|
+
"most_common_model": most_common_model[0],
|
|
205
|
+
"most_common_model_count": most_common_model[1],
|
|
206
|
+
"most_common_country": most_common_country[0],
|
|
207
|
+
"most_common_language": most_common_language[0]
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
except Exception as e:
|
|
211
|
+
debug_print(f"❌ Failed to analyze installations: {e}", "error")
|
|
212
|
+
return {"total_installations": len(installations)}
|
|
213
|
+
|
|
214
|
+
def _analyze_security(self, account_data: Dict[str, Any]) -> Dict[str, Any]:
|
|
215
|
+
"""
|
|
216
|
+
Analyze account security.
|
|
217
|
+
|
|
218
|
+
Args:
|
|
219
|
+
account_data: Account data
|
|
220
|
+
|
|
221
|
+
Returns:
|
|
222
|
+
Security analysis
|
|
223
|
+
"""
|
|
224
|
+
try:
|
|
225
|
+
credentials = account_data.get("credentials", [])
|
|
226
|
+
installations = account_data.get("installations", [])
|
|
227
|
+
|
|
228
|
+
# Security metrics
|
|
229
|
+
security_score = 100
|
|
230
|
+
security_issues = []
|
|
231
|
+
|
|
232
|
+
# Check for multiple anonymous credentials
|
|
233
|
+
anonymous_count = sum(1 for cred in credentials if cred.startswith("anonymous:"))
|
|
234
|
+
if anonymous_count > 10:
|
|
235
|
+
security_score -= 10
|
|
236
|
+
security_issues.append(f"High number of anonymous credentials: {anonymous_count}")
|
|
237
|
+
|
|
238
|
+
# Check for installations from many countries
|
|
239
|
+
countries = set()
|
|
240
|
+
for install in installations:
|
|
241
|
+
if install.get("country"):
|
|
242
|
+
countries.add(install["country"])
|
|
243
|
+
|
|
244
|
+
if len(countries) > 5:
|
|
245
|
+
security_score -= 15
|
|
246
|
+
security_issues.append(f"Installations from many countries: {len(countries)}")
|
|
247
|
+
|
|
248
|
+
# Check for ghost account
|
|
249
|
+
if account_data.get("is_ghost", False):
|
|
250
|
+
security_score -= 20
|
|
251
|
+
security_issues.append("Account is marked as ghost")
|
|
252
|
+
|
|
253
|
+
# Check for no recent login
|
|
254
|
+
last_login = account_data.get("last_login")
|
|
255
|
+
if not last_login:
|
|
256
|
+
security_score -= 5
|
|
257
|
+
security_issues.append("No recent login recorded")
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
"security_score": max(0, security_score),
|
|
261
|
+
"security_issues": security_issues,
|
|
262
|
+
"risk_level": "Low" if security_score >= 80 else "Medium" if security_score >= 60 else "High",
|
|
263
|
+
"unique_countries": len(countries),
|
|
264
|
+
"anonymous_credential_count": anonymous_count
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
except Exception as e:
|
|
268
|
+
debug_print(f"❌ Failed to analyze security: {e}", "error")
|
|
269
|
+
return {"security_score": 0, "risk_level": "Unknown"}
|
|
270
|
+
|
|
271
|
+
@debug_function
|
|
272
|
+
def get_device_history(self) -> Dict[str, Any]:
|
|
273
|
+
"""
|
|
274
|
+
Get device installation history.
|
|
275
|
+
|
|
276
|
+
Returns:
|
|
277
|
+
Device history with timeline analysis
|
|
278
|
+
"""
|
|
279
|
+
try:
|
|
280
|
+
account_data = self.get_account_info()
|
|
281
|
+
installations = account_data.get("account_data", {}).get("installations", [])
|
|
282
|
+
|
|
283
|
+
# Sort by device_id to create timeline
|
|
284
|
+
device_timeline = []
|
|
285
|
+
seen_devices = set()
|
|
286
|
+
|
|
287
|
+
for install in installations:
|
|
288
|
+
device_id = install.get("device_id")
|
|
289
|
+
if device_id and device_id not in seen_devices:
|
|
290
|
+
seen_devices.add(device_id)
|
|
291
|
+
device_timeline.append({
|
|
292
|
+
"device_id": device_id,
|
|
293
|
+
"model": install.get("model"),
|
|
294
|
+
"country": install.get("country"),
|
|
295
|
+
"language": install.get("language"),
|
|
296
|
+
"resolution": install.get("resolution"),
|
|
297
|
+
"firmware": install.get("firmware")
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
debug_print(f"📱 Device history: {len(device_timeline)} unique devices", "success")
|
|
301
|
+
|
|
302
|
+
return {
|
|
303
|
+
"device_timeline": device_timeline,
|
|
304
|
+
"total_unique_devices": len(device_timeline),
|
|
305
|
+
"device_summary": {
|
|
306
|
+
"most_recent_device": device_timeline[-1] if device_timeline else None,
|
|
307
|
+
"device_countries": list(set(d.get("country", "Unknown") for d in device_timeline if d.get("country")))
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
except Exception as e:
|
|
312
|
+
debug_print(f"❌ Failed to get device history: {e}", "error")
|
|
313
|
+
report_error(e, "get_device_history", {})
|
|
314
|
+
raise
|
|
315
|
+
|
|
316
|
+
@debug_function
|
|
317
|
+
def get_credential_summary(self) -> Dict[str, Any]:
|
|
318
|
+
"""
|
|
319
|
+
Get credential summary and analysis.
|
|
320
|
+
|
|
321
|
+
Returns:
|
|
322
|
+
Credential summary with security analysis
|
|
323
|
+
"""
|
|
324
|
+
try:
|
|
325
|
+
account_data = self.get_account_info()
|
|
326
|
+
credentials = account_data.get("account_data", {}).get("credentials", [])
|
|
327
|
+
|
|
328
|
+
# Extract credential details
|
|
329
|
+
credential_details = []
|
|
330
|
+
for i, cred in enumerate(credentials):
|
|
331
|
+
if ":" in cred:
|
|
332
|
+
cred_type, cred_value = cred.split(":", 1)
|
|
333
|
+
credential_details.append({
|
|
334
|
+
"index": i,
|
|
335
|
+
"type": cred_type,
|
|
336
|
+
"value": cred_value[:20] + "..." if len(cred_value) > 20 else cred_value,
|
|
337
|
+
"full_value": cred
|
|
338
|
+
})
|
|
339
|
+
|
|
340
|
+
debug_print(f"🔐 Credential summary: {len(credentials)} credentials analyzed", "success")
|
|
341
|
+
|
|
342
|
+
return {
|
|
343
|
+
"credential_count": len(credentials),
|
|
344
|
+
"credential_details": credential_details,
|
|
345
|
+
"credential_types": list(set(d["type"] for d in credential_details)),
|
|
346
|
+
"latest_credential": credential_details[-1] if credential_details else None
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
except Exception as e:
|
|
350
|
+
debug_print(f"❌ Failed to get credential summary: {e}", "error")
|
|
351
|
+
report_error(e, "get_credential_summary", {})
|
|
352
|
+
raise
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# ────────────[ CHIZOBA ]────────────────────────────
|
|
3
|
+
# | Email : chizoba2026@hotmail.com
|
|
4
|
+
# | File : account_quick.py
|
|
5
|
+
# | License | MIT License © 2026 Chizoba
|
|
6
|
+
# | Brief | Quick functions for account management
|
|
7
|
+
# ────────────────★─────────────────────────────────
|
|
8
|
+
|
|
9
|
+
from typing import Optional, Dict, Any
|
|
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_account_info(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
16
|
+
"""
|
|
17
|
+
Quick function to get comprehensive account information.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
username: MC5 username
|
|
21
|
+
password: MC5 password
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Complete account information with analysis or None if failed
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
with SimpleMC5Client(username, password) as client:
|
|
28
|
+
if client.connect():
|
|
29
|
+
account_info = client.client.get_account_info()
|
|
30
|
+
|
|
31
|
+
# Display summary
|
|
32
|
+
analysis = account_info.get('analysis', {})
|
|
33
|
+
summary = analysis.get('account_summary', {})
|
|
34
|
+
|
|
35
|
+
debug_print(f"✅ Account info retrieved", "success")
|
|
36
|
+
debug_print(f"👤 Account ID: {summary.get('account_id', 'Unknown')}", "info")
|
|
37
|
+
debug_print(f"🔐 Credentials: {summary.get('total_credentials', 0)}", "info")
|
|
38
|
+
debug_print(f"📱 Installations: {summary.get('total_installations', 0)}", "info")
|
|
39
|
+
debug_print(f"🏷️ Aliases: {summary.get('alias_count', 0)}", "info")
|
|
40
|
+
debug_print(f"👻 Ghost Account: {summary.get('is_ghost', False)}", "info")
|
|
41
|
+
|
|
42
|
+
return account_info
|
|
43
|
+
return None
|
|
44
|
+
except Exception as e:
|
|
45
|
+
debug_print(f"❌ Failed to get account info: {e}", "error")
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
@debug_function
|
|
49
|
+
def quick_get_device_history(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
50
|
+
"""
|
|
51
|
+
Quick function to get device installation history.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
username: MC5 username
|
|
55
|
+
password: MC5 password
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Device history with timeline or None if failed
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
with SimpleMC5Client(username, password) as client:
|
|
62
|
+
if client.connect():
|
|
63
|
+
device_history = client.client.get_device_history()
|
|
64
|
+
|
|
65
|
+
total_devices = device_history.get('total_unique_devices', 0)
|
|
66
|
+
debug_print(f"✅ Device history retrieved: {total_devices} unique devices", "success")
|
|
67
|
+
|
|
68
|
+
# Show recent devices
|
|
69
|
+
timeline = device_history.get('device_timeline', [])
|
|
70
|
+
for i, device in enumerate(timeline[-3:], 1): # Show last 3
|
|
71
|
+
model = device.get('model', 'Unknown')
|
|
72
|
+
country = device.get('country', 'Unknown')
|
|
73
|
+
debug_print(f" Device {i}: {model} ({country})", "info")
|
|
74
|
+
|
|
75
|
+
return device_history
|
|
76
|
+
return None
|
|
77
|
+
except Exception as e:
|
|
78
|
+
debug_print(f"❌ Failed to get device history: {e}", "error")
|
|
79
|
+
return None
|
|
80
|
+
|
|
81
|
+
@debug_function
|
|
82
|
+
def quick_get_credential_summary(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
83
|
+
"""
|
|
84
|
+
Quick function to get credential summary.
|
|
85
|
+
|
|
86
|
+
Args:
|
|
87
|
+
username: MC5 username
|
|
88
|
+
password: MC5 password
|
|
89
|
+
|
|
90
|
+
Returns:
|
|
91
|
+
Credential summary or None if failed
|
|
92
|
+
"""
|
|
93
|
+
try:
|
|
94
|
+
with SimpleMC5Client(username, password) as client:
|
|
95
|
+
if client.connect():
|
|
96
|
+
credential_summary = client.client.get_credential_summary()
|
|
97
|
+
|
|
98
|
+
credential_count = credential_summary.get('credential_count', 0)
|
|
99
|
+
debug_print(f"✅ Credential summary: {credential_count} credentials", "success")
|
|
100
|
+
|
|
101
|
+
# Show credential types
|
|
102
|
+
cred_types = credential_summary.get('credential_types', [])
|
|
103
|
+
for cred_type in cred_types:
|
|
104
|
+
debug_print(f" Type: {cred_type}", "info")
|
|
105
|
+
|
|
106
|
+
return credential_summary
|
|
107
|
+
return None
|
|
108
|
+
except Exception as e:
|
|
109
|
+
debug_print(f"❌ Failed to get credential summary: {e}", "error")
|
|
110
|
+
return None
|
|
111
|
+
|
|
112
|
+
@debug_function
|
|
113
|
+
def quick_analyze_account_security(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
114
|
+
"""
|
|
115
|
+
Quick function to analyze account security.
|
|
116
|
+
|
|
117
|
+
Args:
|
|
118
|
+
username: MC5 username
|
|
119
|
+
password: MC5 password
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Security analysis or None if failed
|
|
123
|
+
"""
|
|
124
|
+
try:
|
|
125
|
+
with SimpleMC5Client(username, password) as client:
|
|
126
|
+
if client.connect():
|
|
127
|
+
account_info = client.client.get_account_info()
|
|
128
|
+
security_analysis = account_info.get('analysis', {}).get('security', {})
|
|
129
|
+
|
|
130
|
+
security_score = security_analysis.get('security_score', 0)
|
|
131
|
+
risk_level = security_analysis.get('risk_level', 'Unknown')
|
|
132
|
+
issues = security_analysis.get('security_issues', [])
|
|
133
|
+
|
|
134
|
+
debug_print(f"✅ Security analysis completed", "success")
|
|
135
|
+
debug_print(f"🔒 Security Score: {security_score}/100", "info")
|
|
136
|
+
debug_print(f"⚠️ Risk Level: {risk_level}", "info")
|
|
137
|
+
|
|
138
|
+
if issues:
|
|
139
|
+
debug_print("🚨 Security Issues:", "warning")
|
|
140
|
+
for issue in issues:
|
|
141
|
+
debug_print(f" • {issue}", "warning")
|
|
142
|
+
else:
|
|
143
|
+
debug_print("✅ No security issues detected", "success")
|
|
144
|
+
|
|
145
|
+
return security_analysis
|
|
146
|
+
return None
|
|
147
|
+
except Exception as e:
|
|
148
|
+
debug_print(f"❌ Failed to analyze security: {e}", "error")
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
@debug_function
|
|
152
|
+
def quick_get_account_overview(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
153
|
+
"""
|
|
154
|
+
Quick function to get complete account overview.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
username: MC5 username
|
|
158
|
+
password: MC5 password
|
|
159
|
+
|
|
160
|
+
Returns:
|
|
161
|
+
Complete account overview or None if failed
|
|
162
|
+
"""
|
|
163
|
+
try:
|
|
164
|
+
with SimpleMC5Client(username, password) as client:
|
|
165
|
+
if client.connect():
|
|
166
|
+
# Get all account data
|
|
167
|
+
account_info = client.client.get_account_info()
|
|
168
|
+
device_history = client.client.get_device_history()
|
|
169
|
+
credential_summary = client.client.get_credential_summary()
|
|
170
|
+
|
|
171
|
+
# Create overview
|
|
172
|
+
analysis = account_info.get('analysis', {})
|
|
173
|
+
account_summary = analysis.get('account_summary', {})
|
|
174
|
+
installation_analysis = analysis.get('installations', {})
|
|
175
|
+
security_analysis = analysis.get('security', {})
|
|
176
|
+
|
|
177
|
+
overview = {
|
|
178
|
+
"account_summary": account_summary,
|
|
179
|
+
"device_summary": {
|
|
180
|
+
"total_devices": device_history.get('total_unique_devices', 0),
|
|
181
|
+
"most_common_model": installation_analysis.get('most_common_model'),
|
|
182
|
+
"most_common_country": installation_analysis.get('most_common_country')
|
|
183
|
+
},
|
|
184
|
+
"credential_summary": {
|
|
185
|
+
"total_credentials": credential_summary.get('credential_count', 0),
|
|
186
|
+
"credential_types": credential_summary.get('credential_types', [])
|
|
187
|
+
},
|
|
188
|
+
"security_summary": {
|
|
189
|
+
"security_score": security_analysis.get('security_score', 0),
|
|
190
|
+
"risk_level": security_analysis.get('risk_level', 'Unknown'),
|
|
191
|
+
"security_issues": security_analysis.get('security_issues', [])
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
debug_print(f"✅ Account overview completed", "success")
|
|
196
|
+
debug_print(f"👤 Account: {account_summary.get('account_id', 'Unknown')}", "info")
|
|
197
|
+
debug_print(f"📱 Devices: {overview['device_summary']['total_devices']}", "info")
|
|
198
|
+
debug_print(f"🔐 Credentials: {overview['credential_summary']['total_credentials']}", "info")
|
|
199
|
+
debug_print(f"🔒 Security: {overview['security_summary']['security_score']}/100 ({overview['security_summary']['risk_level']})", "info")
|
|
200
|
+
|
|
201
|
+
return overview
|
|
202
|
+
return None
|
|
203
|
+
except Exception as e:
|
|
204
|
+
debug_print(f"❌ Failed to get account overview: {e}", "error")
|
|
205
|
+
return None
|
|
206
|
+
|
|
207
|
+
@debug_function
|
|
208
|
+
def quick_export_account_data(username: str, password: str, filename: str = "account_data.json") -> bool:
|
|
209
|
+
"""
|
|
210
|
+
Quick function to export complete account data to file.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
username: MC5 username
|
|
214
|
+
password: MC5 password
|
|
215
|
+
filename: Output filename
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
True if successful, False otherwise
|
|
219
|
+
"""
|
|
220
|
+
try:
|
|
221
|
+
with SimpleMC5Client(username, password) as client:
|
|
222
|
+
if client.connect():
|
|
223
|
+
# Get complete account data
|
|
224
|
+
account_info = client.client.get_account_info()
|
|
225
|
+
device_history = client.client.get_device_history()
|
|
226
|
+
credential_summary = client.client.get_credential_summary()
|
|
227
|
+
|
|
228
|
+
# Combine all data
|
|
229
|
+
export_data = {
|
|
230
|
+
"export_timestamp": __import__('datetime').datetime.now().isoformat(),
|
|
231
|
+
"account_info": account_info,
|
|
232
|
+
"device_history": device_history,
|
|
233
|
+
"credential_summary": credential_summary
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
# Write to file
|
|
237
|
+
import json
|
|
238
|
+
with open(filename, 'w', encoding='utf-8') as f:
|
|
239
|
+
json.dump(export_data, f, indent=2, ensure_ascii=False)
|
|
240
|
+
|
|
241
|
+
debug_print(f"✅ Account data exported to {filename}", "success")
|
|
242
|
+
return True
|
|
243
|
+
return False
|
|
244
|
+
except Exception as e:
|
|
245
|
+
debug_print(f"❌ Failed to export account data: {e}", "error")
|
|
246
|
+
return False
|