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,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