mc5-api-client 1.0.15__py3-none-any.whl → 1.0.17__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.
@@ -15,18 +15,73 @@ messaging, and more.
15
15
 
16
16
  from typing import Optional, Dict, Any
17
17
 
18
- __version__ = "1.0.15"
18
+ __version__ = "1.0.16"
19
19
  __author__ = "Chizoba"
20
20
  __email__ = "chizoba2026@hotmail.com"
21
21
  __license__ = "MIT"
22
22
 
23
+ from .telemetry import telemetry, report_error, report_usage, is_telemetry_enabled
24
+ from .debug import debug_mode, debug_print, debug_function, analyze_error, print_error_analysis
25
+ from .platform import Platform, get_platform_config, get_android_anonymous_credential, get_pc_anonymous_credential
26
+ from .squad_battle import SquadBattleMixin
27
+ from .transfer_quick import (
28
+ quick_generate_transfer_code,
29
+ quick_check_transfer_status,
30
+ quick_get_device_linking_guide,
31
+ quick_link_device_workflow,
32
+ quick_validate_transfer_code,
33
+ quick_force_new_code,
34
+ quick_transfer_status_summary
35
+ )
36
+ from .account_quick import (
37
+ quick_get_account_info,
38
+ quick_get_device_history,
39
+ quick_get_credential_summary,
40
+ quick_analyze_account_security,
41
+ quick_get_account_overview,
42
+ quick_export_account_data
43
+ )
44
+ from .alerts_quick import (
45
+ quick_start_alert_stream,
46
+ quick_test_alert_connection,
47
+ quick_start_alerts_with_discord,
48
+ quick_monitor_alerts,
49
+ create_alert_callback
50
+ )
51
+ from .federation_quick import (
52
+ quick_get_active_sessions,
53
+ quick_get_my_sessions,
54
+ quick_get_session_statistics,
55
+ quick_check_session_status,
56
+ quick_get_server_type_sessions,
57
+ quick_analyze_session_activity
58
+ )
59
+ from .squad_battle_quick import (
60
+ quick_create_squad_battle_room,
61
+ quick_post_squad_battle_result,
62
+ quick_get_squad_battle_history,
63
+ quick_analyze_squad_performance,
64
+ quick_get_squad_wall_messages,
65
+ quick_send_squad_battle_notification,
66
+ quick_post_squad_wall_message
67
+ )
23
68
  from .simple_client import (
24
69
  SimpleMC5Client,
25
70
  batch_search_players,
26
71
  clan_cleanup,
27
72
  monitor_clan_activity,
28
73
  quick_search,
29
- quick_kick
74
+ quick_kick,
75
+ quick_remove_friend,
76
+ quick_send_friend_request,
77
+ quick_check_friend_status,
78
+ quick_accept_friend_request,
79
+ quick_sign_up_for_event,
80
+ quick_get_event_leaderboard,
81
+ quick_get_my_event_rank,
82
+ quick_get_squad_invitations,
83
+ quick_accept_squad_invitation,
84
+ quick_decline_squad_invitation
30
85
  )
31
86
  from .client import MC5Client
32
87
  from .auth import TokenGenerator
@@ -100,6 +155,56 @@ __all__ = [
100
155
  "monitor_clan_activity",
101
156
  "quick_search",
102
157
  "quick_kick",
158
+ "quick_remove_friend",
159
+ "quick_send_friend_request",
160
+ "quick_check_friend_status",
161
+ "quick_accept_friend_request",
162
+ "quick_sign_up_for_event",
163
+ "quick_get_event_leaderboard",
164
+ "quick_get_my_event_rank",
165
+ "quick_get_squad_invitations",
166
+ "quick_accept_squad_invitation",
167
+ "quick_decline_squad_invitation",
168
+ "quick_create_squad_battle_room",
169
+ "quick_post_squad_battle_result",
170
+ "quick_get_squad_battle_history",
171
+ "quick_analyze_squad_performance",
172
+ "quick_get_squad_wall_messages",
173
+ "quick_send_squad_battle_notification",
174
+ "quick_post_squad_wall_message",
175
+ "quick_get_active_sessions",
176
+ "quick_get_my_sessions",
177
+ "quick_get_session_statistics",
178
+ "quick_check_session_status",
179
+ "quick_get_server_type_sessions",
180
+ "quick_analyze_session_activity",
181
+ "quick_start_alert_stream",
182
+ "quick_test_alert_connection",
183
+ "quick_start_alerts_with_discord",
184
+ "quick_monitor_alerts",
185
+ "create_alert_callback",
186
+ "quick_get_account_info",
187
+ "quick_get_device_history",
188
+ "quick_get_credential_summary",
189
+ "quick_analyze_account_security",
190
+ "quick_get_account_overview",
191
+ "quick_export_account_data",
192
+ "quick_generate_transfer_code",
193
+ "quick_check_transfer_status",
194
+ "quick_get_device_linking_guide",
195
+ "quick_link_device_workflow",
196
+ "quick_validate_transfer_code",
197
+ "quick_force_new_code",
198
+ "quick_transfer_status_summary",
199
+ "telemetry",
200
+ "report_error",
201
+ "report_usage",
202
+ "is_telemetry_enabled",
203
+ "debug_mode",
204
+ "debug_print",
205
+ "debug_function",
206
+ "analyze_error",
207
+ "print_error_analysis",
103
208
  "create_admin_client",
104
209
  "create_user_client",
105
210
  "quick_add_squad_rating",
@@ -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