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.
@@ -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