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,348 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# ────────────[ CHIZOBA ]────────────────────────────
|
|
3
|
+
# | Email : chizoba2026@hotmail.com
|
|
4
|
+
# | File : transfer.py
|
|
5
|
+
# | License | MIT License © 2026 Chizoba
|
|
6
|
+
# | Brief | Account transfer and device linking functionality
|
|
7
|
+
# ────────────────★─────────────────────────────────
|
|
8
|
+
|
|
9
|
+
import json
|
|
10
|
+
from typing import Optional, Dict, Any
|
|
11
|
+
from datetime import datetime, timezone
|
|
12
|
+
from .telemetry import report_error, report_usage
|
|
13
|
+
from .debug import debug_function, debug_print
|
|
14
|
+
|
|
15
|
+
class TransferMixin:
|
|
16
|
+
"""Mixin class for account transfer and device linking functionality."""
|
|
17
|
+
|
|
18
|
+
@debug_function
|
|
19
|
+
def generate_transfer_code(self) -> Dict[str, Any]:
|
|
20
|
+
"""
|
|
21
|
+
Generate a transfer code for account linking.
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Transfer code information with expiration time
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
url = f"{self.BASE_URLS['janus']}/users/me/transfer_code"
|
|
28
|
+
|
|
29
|
+
data = {
|
|
30
|
+
"access_token": self._token_data["access_token"]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
debug_print("🔄 Generating transfer code for account linking", "info")
|
|
34
|
+
result = self._make_request("POST", url, data=data)
|
|
35
|
+
|
|
36
|
+
if result:
|
|
37
|
+
transfer_code = result.get("value")
|
|
38
|
+
expiration = result.get("expiration")
|
|
39
|
+
|
|
40
|
+
debug_print(f"✅ Transfer code generated: {transfer_code}", "success")
|
|
41
|
+
debug_print(f"⏰ Expires: {expiration}", "info")
|
|
42
|
+
|
|
43
|
+
# Parse expiration time
|
|
44
|
+
expiration_dt = None
|
|
45
|
+
if expiration:
|
|
46
|
+
try:
|
|
47
|
+
expiration_dt = datetime.fromisoformat(expiration.replace('Z', '+00:00'))
|
|
48
|
+
except ValueError:
|
|
49
|
+
expiration_dt = None
|
|
50
|
+
|
|
51
|
+
report_usage("generate_transfer_code", True, 0, {
|
|
52
|
+
"has_code": bool(transfer_code),
|
|
53
|
+
"has_expiration": bool(expiration),
|
|
54
|
+
"code_length": len(transfer_code) if transfer_code else 0
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
"transfer_code": transfer_code,
|
|
59
|
+
"expiration": expiration,
|
|
60
|
+
"expiration_datetime": expiration_dt,
|
|
61
|
+
"is_expired": expiration_dt and expiration_dt <= datetime.now(timezone.utc) if expiration_dt else False,
|
|
62
|
+
"instructions": (
|
|
63
|
+
"On the new device, go to the Options menu and select 'Link a device.' "
|
|
64
|
+
"Then, select 'This is the NEW DEVICE.' Enter the below code into the new device."
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return {"error": "Failed to generate transfer code"}
|
|
69
|
+
|
|
70
|
+
except Exception as e:
|
|
71
|
+
debug_print(f"❌ Failed to generate transfer code: {e}", "error")
|
|
72
|
+
report_error(e, "generate_transfer_code", {})
|
|
73
|
+
raise
|
|
74
|
+
|
|
75
|
+
@debug_function
|
|
76
|
+
def check_transfer_code_status(self) -> Dict[str, Any]:
|
|
77
|
+
"""
|
|
78
|
+
Check if there's an existing transfer code.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
Status of existing transfer code or None
|
|
82
|
+
"""
|
|
83
|
+
try:
|
|
84
|
+
# Try to generate a code to check status
|
|
85
|
+
result = self.generate_transfer_code()
|
|
86
|
+
|
|
87
|
+
if result.get("transfer_code"):
|
|
88
|
+
return {
|
|
89
|
+
"has_active_code": True,
|
|
90
|
+
"transfer_code": result["transfer_code"],
|
|
91
|
+
"expiration": result["expiration"],
|
|
92
|
+
"expiration_datetime": result["expiration_datetime"],
|
|
93
|
+
"is_expired": result["is_expired"],
|
|
94
|
+
"status": "active"
|
|
95
|
+
}
|
|
96
|
+
else:
|
|
97
|
+
return {
|
|
98
|
+
"has_active_code": False,
|
|
99
|
+
"status": "no_code"
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
except Exception as e:
|
|
103
|
+
# Check if it's a conflict (existing code)
|
|
104
|
+
if "409" in str(e) or "Conflict" in str(e):
|
|
105
|
+
debug_print("⚠️ Existing transfer code found", "warning")
|
|
106
|
+
return {
|
|
107
|
+
"has_active_code": True,
|
|
108
|
+
"status": "existing_code",
|
|
109
|
+
"error": "You already have a valid transfer code. Please use it, or wait till it expires."
|
|
110
|
+
}
|
|
111
|
+
else:
|
|
112
|
+
debug_print(f"❌ Failed to check transfer code status: {e}", "error")
|
|
113
|
+
report_error(e, "check_transfer_code_status", {})
|
|
114
|
+
raise
|
|
115
|
+
|
|
116
|
+
@debug_function
|
|
117
|
+
def get_transfer_code_info(self) -> Dict[str, Any]:
|
|
118
|
+
"""
|
|
119
|
+
Get detailed information about transfer code status.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Comprehensive transfer code information
|
|
123
|
+
"""
|
|
124
|
+
try:
|
|
125
|
+
debug_print("🔍 Checking transfer code status", "info")
|
|
126
|
+
|
|
127
|
+
# Check current status
|
|
128
|
+
status = self.check_transfer_code_status()
|
|
129
|
+
|
|
130
|
+
if status.get("has_active_code"):
|
|
131
|
+
transfer_code = status.get("transfer_code")
|
|
132
|
+
expiration = status.get("expiration")
|
|
133
|
+
expiration_dt = status.get("expiration_datetime")
|
|
134
|
+
|
|
135
|
+
# Calculate time remaining
|
|
136
|
+
time_remaining = None
|
|
137
|
+
if expiration_dt and not status.get("is_expired"):
|
|
138
|
+
now = datetime.now(timezone.utc)
|
|
139
|
+
time_delta = expiration_dt - now
|
|
140
|
+
time_remaining = {
|
|
141
|
+
"total_seconds": int(time_delta.total_seconds()),
|
|
142
|
+
"minutes": int(time_delta.total_seconds() // 60),
|
|
143
|
+
"hours": int(time_delta.total_seconds() // 3600),
|
|
144
|
+
"formatted": f"{int(time_delta.total_seconds() // 60)} minutes"
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
info = {
|
|
148
|
+
"has_active_code": True,
|
|
149
|
+
"transfer_code": transfer_code,
|
|
150
|
+
"expiration": expiration,
|
|
151
|
+
"expiration_datetime": expiration_dt,
|
|
152
|
+
"is_expired": status.get("is_expired", False),
|
|
153
|
+
"time_remaining": time_remaining,
|
|
154
|
+
"status": "active",
|
|
155
|
+
"instructions": (
|
|
156
|
+
"1. On the new device, open MC5\n"
|
|
157
|
+
"2. Go to Options menu\n"
|
|
158
|
+
"3. Select 'Link a device'\n"
|
|
159
|
+
"4. Select 'This is the NEW DEVICE'\n"
|
|
160
|
+
"5. Enter this code: " + transfer_code
|
|
161
|
+
)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
debug_print(f"✅ Active transfer code found: {transfer_code}", "success")
|
|
165
|
+
if time_remaining:
|
|
166
|
+
debug_print(f"⏰ Time remaining: {time_remaining['formatted']}", "info")
|
|
167
|
+
|
|
168
|
+
return info
|
|
169
|
+
else:
|
|
170
|
+
debug_print("ℹ️ No active transfer code found", "info")
|
|
171
|
+
return {
|
|
172
|
+
"has_active_code": False,
|
|
173
|
+
"status": "no_code",
|
|
174
|
+
"instructions": "Generate a new transfer code to link this account to another device."
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
except Exception as e:
|
|
178
|
+
debug_print(f"❌ Failed to get transfer code info: {e}", "error")
|
|
179
|
+
report_error(e, "get_transfer_code_info", {})
|
|
180
|
+
raise
|
|
181
|
+
|
|
182
|
+
@debug_function
|
|
183
|
+
def generate_new_transfer_code(self) -> Dict[str, Any]:
|
|
184
|
+
"""
|
|
185
|
+
Force generate a new transfer code (may override existing one).
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
New transfer code information
|
|
189
|
+
"""
|
|
190
|
+
try:
|
|
191
|
+
debug_print("🔄 Attempting to generate new transfer code", "info")
|
|
192
|
+
|
|
193
|
+
# First check if there's an existing code
|
|
194
|
+
status = self.check_transfer_code_status()
|
|
195
|
+
|
|
196
|
+
if status.get("has_active_code") and not status.get("is_expired"):
|
|
197
|
+
debug_print("⚠️ Active code exists, attempting to override", "warning")
|
|
198
|
+
|
|
199
|
+
# Generate new code
|
|
200
|
+
result = self.generate_transfer_code()
|
|
201
|
+
|
|
202
|
+
if result.get("transfer_code"):
|
|
203
|
+
debug_print(f"✅ New transfer code generated: {result['transfer_code']}", "success")
|
|
204
|
+
return result
|
|
205
|
+
else:
|
|
206
|
+
debug_print("❌ Failed to generate new transfer code", "error")
|
|
207
|
+
return result
|
|
208
|
+
|
|
209
|
+
except Exception as e:
|
|
210
|
+
debug_print(f"❌ Failed to generate new transfer code: {e}", "error")
|
|
211
|
+
report_error(e, "generate_new_transfer_code", {})
|
|
212
|
+
raise
|
|
213
|
+
|
|
214
|
+
@debug_function
|
|
215
|
+
def validate_transfer_code_format(self, transfer_code: str) -> Dict[str, Any]:
|
|
216
|
+
"""
|
|
217
|
+
Validate transfer code format.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
transfer_code: Transfer code to validate
|
|
221
|
+
|
|
222
|
+
Returns:
|
|
223
|
+
Validation result
|
|
224
|
+
"""
|
|
225
|
+
try:
|
|
226
|
+
# Basic format validation
|
|
227
|
+
if not transfer_code:
|
|
228
|
+
return {
|
|
229
|
+
"valid": False,
|
|
230
|
+
"error": "Transfer code cannot be empty"
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
# Check length (typically 8 characters based on example)
|
|
234
|
+
if len(transfer_code) != 8:
|
|
235
|
+
return {
|
|
236
|
+
"valid": False,
|
|
237
|
+
"error": f"Transfer code must be 8 characters, got {len(transfer_code)}"
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
# Check character pattern (alphanumeric)
|
|
241
|
+
if not transfer_code.isalnum():
|
|
242
|
+
return {
|
|
243
|
+
"valid": False,
|
|
244
|
+
"error": "Transfer code must contain only letters and numbers"
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return {
|
|
248
|
+
"valid": True,
|
|
249
|
+
"transfer_code": transfer_code,
|
|
250
|
+
"length": len(transfer_code),
|
|
251
|
+
"format": "alphanumeric"
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
except Exception as e:
|
|
255
|
+
debug_print(f"❌ Failed to validate transfer code: {e}", "error")
|
|
256
|
+
return {
|
|
257
|
+
"valid": False,
|
|
258
|
+
"error": f"Validation error: {str(e)}"
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
@debug_function
|
|
262
|
+
def get_device_linking_guide(self) -> Dict[str, Any]:
|
|
263
|
+
"""
|
|
264
|
+
Get complete device linking guide.
|
|
265
|
+
|
|
266
|
+
Returns:
|
|
267
|
+
Step-by-step instructions for device linking
|
|
268
|
+
"""
|
|
269
|
+
try:
|
|
270
|
+
# Get current transfer code info
|
|
271
|
+
code_info = self.get_transfer_code_info()
|
|
272
|
+
|
|
273
|
+
guide = {
|
|
274
|
+
"title": "MC5 Account Transfer & Device Linking Guide",
|
|
275
|
+
"steps": [
|
|
276
|
+
{
|
|
277
|
+
"step": 1,
|
|
278
|
+
"title": "Generate Transfer Code",
|
|
279
|
+
"description": "Generate a transfer code from your current device",
|
|
280
|
+
"action": "Use generate_transfer_code() function"
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
"step": 2,
|
|
284
|
+
"title": "Open MC5 on New Device",
|
|
285
|
+
"description": "Launch MC5 on the device you want to link",
|
|
286
|
+
"action": "Start MC5 application"
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
"step": 3,
|
|
290
|
+
"title": "Access Options Menu",
|
|
291
|
+
"description": "Navigate to the Options menu in MC5",
|
|
292
|
+
"action": "Tap on Options/Settings"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"step": 4,
|
|
296
|
+
"title": "Select Link Device",
|
|
297
|
+
"description": "Choose 'Link a device' from the options",
|
|
298
|
+
"action": "Tap on 'Link a device'"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
"step": 5,
|
|
302
|
+
"title": "Choose New Device",
|
|
303
|
+
"description": "Select 'This is the NEW DEVICE'",
|
|
304
|
+
"action": "Tap on 'This is the NEW DEVICE'"
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
"step": 6,
|
|
308
|
+
"title": "Enter Transfer Code",
|
|
309
|
+
"description": "Input the generated transfer code",
|
|
310
|
+
"action": f"Enter code: {code_info.get('transfer_code', 'Generate code first')}"
|
|
311
|
+
},
|
|
312
|
+
{
|
|
313
|
+
"step": 7,
|
|
314
|
+
"title": "Complete Linking",
|
|
315
|
+
"description": "Wait for the linking process to complete",
|
|
316
|
+
"action": "Follow on-screen instructions"
|
|
317
|
+
}
|
|
318
|
+
],
|
|
319
|
+
"current_code_info": code_info,
|
|
320
|
+
"important_notes": [
|
|
321
|
+
"Transfer codes expire after 10 minutes",
|
|
322
|
+
"Only one transfer code can be active at a time",
|
|
323
|
+
"The code links your entire account (progress, purchases, etc.)",
|
|
324
|
+
"Both devices must have internet connection during transfer"
|
|
325
|
+
],
|
|
326
|
+
"troubleshooting": [
|
|
327
|
+
{
|
|
328
|
+
"issue": "Code not working",
|
|
329
|
+
"solution": "Generate a new transfer code and try again"
|
|
330
|
+
},
|
|
331
|
+
{
|
|
332
|
+
"issue": "Code expired",
|
|
333
|
+
"solution": "Wait for the old code to expire or generate a new one"
|
|
334
|
+
},
|
|
335
|
+
{
|
|
336
|
+
"issue": "409 Conflict error",
|
|
337
|
+
"solution": "You already have an active code, use it or wait for expiration"
|
|
338
|
+
}
|
|
339
|
+
]
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
debug_print("📖 Device linking guide generated", "success")
|
|
343
|
+
return guide
|
|
344
|
+
|
|
345
|
+
except Exception as e:
|
|
346
|
+
debug_print(f"❌ Failed to generate guide: {e}", "error")
|
|
347
|
+
report_error(e, "get_device_linking_guide", {})
|
|
348
|
+
raise
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# ────────────[ CHIZOBA ]────────────────────────────
|
|
3
|
+
# | Email : chizoba2026@hotmail.com
|
|
4
|
+
# | File : transfer_quick.py
|
|
5
|
+
# | License | MIT License © 2026 Chizoba
|
|
6
|
+
# | Brief : Quick functions for account transfer and device linking
|
|
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_generate_transfer_code(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
16
|
+
"""
|
|
17
|
+
Quick function to generate a transfer code for account linking.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
username: MC5 username
|
|
21
|
+
password: MC5 password
|
|
22
|
+
|
|
23
|
+
Returns:
|
|
24
|
+
Transfer code information or None if failed
|
|
25
|
+
"""
|
|
26
|
+
try:
|
|
27
|
+
with SimpleMC5Client(username, password) as client:
|
|
28
|
+
if client.connect():
|
|
29
|
+
transfer_info = client.client.generate_transfer_code()
|
|
30
|
+
|
|
31
|
+
if transfer_info.get("transfer_code"):
|
|
32
|
+
code = transfer_info["transfer_code"]
|
|
33
|
+
expiration = transfer_info["expiration"]
|
|
34
|
+
|
|
35
|
+
debug_print(f"✅ Transfer code generated: {code}", "success")
|
|
36
|
+
debug_print(f"⏰ Expires: {expiration}", "info")
|
|
37
|
+
debug_print("📱 Use this code on your new device to link your account", "info")
|
|
38
|
+
|
|
39
|
+
return transfer_info
|
|
40
|
+
else:
|
|
41
|
+
debug_print("❌ Failed to generate transfer code", "error")
|
|
42
|
+
return None
|
|
43
|
+
return None
|
|
44
|
+
except Exception as e:
|
|
45
|
+
debug_print(f"❌ Failed to generate transfer code: {e}", "error")
|
|
46
|
+
return None
|
|
47
|
+
|
|
48
|
+
@debug_function
|
|
49
|
+
def quick_check_transfer_status(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
50
|
+
"""
|
|
51
|
+
Quick function to check transfer code status.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
username: MC5 username
|
|
55
|
+
password: MC5 password
|
|
56
|
+
|
|
57
|
+
Returns:
|
|
58
|
+
Transfer code status or None if failed
|
|
59
|
+
"""
|
|
60
|
+
try:
|
|
61
|
+
with SimpleMC5Client(username, password) as client:
|
|
62
|
+
if client.connect():
|
|
63
|
+
status = client.client.get_transfer_code_info()
|
|
64
|
+
|
|
65
|
+
if status.get("has_active_code"):
|
|
66
|
+
code = status["transfer_code"]
|
|
67
|
+
time_remaining = status.get("time_remaining", {})
|
|
68
|
+
|
|
69
|
+
debug_print(f"✅ Active transfer code found: {code}", "success")
|
|
70
|
+
|
|
71
|
+
if time_remaining and not status.get("is_expired"):
|
|
72
|
+
debug_print(f"⏰ Time remaining: {time_remaining.get('formatted', 'Unknown')}", "info")
|
|
73
|
+
else:
|
|
74
|
+
debug_print("⚠️ Code has expired", "warning")
|
|
75
|
+
|
|
76
|
+
return status
|
|
77
|
+
else:
|
|
78
|
+
debug_print("ℹ️ No active transfer code found", "info")
|
|
79
|
+
return status
|
|
80
|
+
return None
|
|
81
|
+
except Exception as e:
|
|
82
|
+
debug_print(f"❌ Failed to check transfer status: {e}", "error")
|
|
83
|
+
return None
|
|
84
|
+
|
|
85
|
+
@debug_function
|
|
86
|
+
def quick_get_device_linking_guide(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
87
|
+
"""
|
|
88
|
+
Quick function to get complete device linking guide.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
username: MC5 username
|
|
92
|
+
password: MC5 password
|
|
93
|
+
|
|
94
|
+
Returns:
|
|
95
|
+
Device linking guide or None if failed
|
|
96
|
+
"""
|
|
97
|
+
try:
|
|
98
|
+
with SimpleMC5Client(username, password) as client:
|
|
99
|
+
if client.connect():
|
|
100
|
+
guide = client.client.get_device_linking_guide()
|
|
101
|
+
|
|
102
|
+
debug_print("📖 Device linking guide generated", "success")
|
|
103
|
+
|
|
104
|
+
# Show current code status
|
|
105
|
+
code_info = guide.get("current_code_info", {})
|
|
106
|
+
if code_info.get("has_active_code"):
|
|
107
|
+
debug_print(f"🔑 Current code: {code_info.get('transfer_code')}", "info")
|
|
108
|
+
debug_print(f"⏰ Expires: {code_info.get('expiration')}", "info")
|
|
109
|
+
else:
|
|
110
|
+
debug_print("ℹ️ No active code - generate one first", "info")
|
|
111
|
+
|
|
112
|
+
return guide
|
|
113
|
+
return None
|
|
114
|
+
except Exception as e:
|
|
115
|
+
debug_print(f"❌ Failed to get linking guide: {e}", "error")
|
|
116
|
+
return None
|
|
117
|
+
|
|
118
|
+
@debug_function
|
|
119
|
+
def quick_link_device_workflow(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
120
|
+
"""
|
|
121
|
+
Quick function for complete device linking workflow.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
username: MC5 username
|
|
125
|
+
password: MC5 password
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Complete workflow results or None if failed
|
|
129
|
+
"""
|
|
130
|
+
try:
|
|
131
|
+
with SimpleMC5Client(username, password) as client:
|
|
132
|
+
if client.connect():
|
|
133
|
+
debug_print("🔄 Starting device linking workflow", "info")
|
|
134
|
+
|
|
135
|
+
# Step 1: Check existing code
|
|
136
|
+
debug_print("📋 Step 1: Checking existing transfer code...")
|
|
137
|
+
status = client.client.get_transfer_code_info()
|
|
138
|
+
|
|
139
|
+
if status.get("has_active_code") and not status.get("is_expired"):
|
|
140
|
+
debug_print(f"✅ Found active code: {status['transfer_code']}", "success")
|
|
141
|
+
time_remaining = status.get("time_remaining", {})
|
|
142
|
+
if time_remaining:
|
|
143
|
+
debug_print(f"⏰ Time remaining: {time_remaining.get('formatted')}", "info")
|
|
144
|
+
|
|
145
|
+
workflow_result = {
|
|
146
|
+
"status": "existing_code",
|
|
147
|
+
"transfer_code": status["transfer_code"],
|
|
148
|
+
"expiration": status["expiration"],
|
|
149
|
+
"time_remaining": time_remaining,
|
|
150
|
+
"instructions": status["instructions"]
|
|
151
|
+
}
|
|
152
|
+
else:
|
|
153
|
+
# Step 2: Generate new code
|
|
154
|
+
debug_print("📋 Step 2: Generating new transfer code...")
|
|
155
|
+
transfer_info = client.client.generate_transfer_code()
|
|
156
|
+
|
|
157
|
+
if transfer_info.get("transfer_code"):
|
|
158
|
+
debug_print(f"✅ New code generated: {transfer_info['transfer_code']}", "success")
|
|
159
|
+
|
|
160
|
+
workflow_result = {
|
|
161
|
+
"status": "new_code",
|
|
162
|
+
"transfer_code": transfer_info["transfer_code"],
|
|
163
|
+
"expiration": transfer_info["expiration"],
|
|
164
|
+
"instructions": transfer_info["instructions"]
|
|
165
|
+
}
|
|
166
|
+
else:
|
|
167
|
+
debug_print("❌ Failed to generate new code", "error")
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
# Step 3: Get linking guide
|
|
171
|
+
debug_print("📋 Step 3: Getting linking instructions...")
|
|
172
|
+
guide = client.client.get_device_linking_guide()
|
|
173
|
+
|
|
174
|
+
workflow_result.update({
|
|
175
|
+
"linking_guide": guide,
|
|
176
|
+
"steps": guide.get("steps", []),
|
|
177
|
+
"important_notes": guide.get("important_notes", [])
|
|
178
|
+
})
|
|
179
|
+
|
|
180
|
+
debug_print("✅ Device linking workflow completed", "success")
|
|
181
|
+
return workflow_result
|
|
182
|
+
return None
|
|
183
|
+
except Exception as e:
|
|
184
|
+
debug_print(f"❌ Failed to complete linking workflow: {e}", "error")
|
|
185
|
+
return None
|
|
186
|
+
|
|
187
|
+
@debug_function
|
|
188
|
+
def quick_validate_transfer_code(transfer_code: str) -> Optional[Dict[str, Any]]:
|
|
189
|
+
"""
|
|
190
|
+
Quick function to validate transfer code format.
|
|
191
|
+
|
|
192
|
+
Args:
|
|
193
|
+
transfer_code: Transfer code to validate
|
|
194
|
+
|
|
195
|
+
Returns:
|
|
196
|
+
Validation result or None if failed
|
|
197
|
+
"""
|
|
198
|
+
try:
|
|
199
|
+
from .transfer import TransferMixin
|
|
200
|
+
|
|
201
|
+
# Create a temporary client instance for validation
|
|
202
|
+
client = TransferMixin()
|
|
203
|
+
client._token_data = {"access_token": "dummy"} # Set minimal token data
|
|
204
|
+
|
|
205
|
+
validation = client.validate_transfer_code_format(transfer_code)
|
|
206
|
+
|
|
207
|
+
if validation.get("valid"):
|
|
208
|
+
debug_print(f"✅ Transfer code format is valid", "success")
|
|
209
|
+
else:
|
|
210
|
+
debug_print(f"❌ Invalid transfer code: {validation.get('error')}", "error")
|
|
211
|
+
|
|
212
|
+
return validation
|
|
213
|
+
except Exception as e:
|
|
214
|
+
debug_print(f"❌ Failed to validate transfer code: {e}", "error")
|
|
215
|
+
return None
|
|
216
|
+
|
|
217
|
+
@debug_function
|
|
218
|
+
def quick_force_new_code(username: str, password: str) -> Optional[Dict[str, Any]]:
|
|
219
|
+
"""
|
|
220
|
+
Quick function to force generate a new transfer code.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
username: MC5 username
|
|
224
|
+
password: MC5 password
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
New transfer code information or None if failed
|
|
228
|
+
"""
|
|
229
|
+
try:
|
|
230
|
+
with SimpleMC5Client(username, password) as client:
|
|
231
|
+
if client.connect():
|
|
232
|
+
debug_print("🔄 Forcing new transfer code generation...", "info")
|
|
233
|
+
|
|
234
|
+
result = client.client.generate_new_transfer_code()
|
|
235
|
+
|
|
236
|
+
if result.get("transfer_code"):
|
|
237
|
+
debug_print(f"✅ New transfer code: {result['transfer_code']}", "success")
|
|
238
|
+
debug_print(f"⏰ Expires: {result['expiration']}", "info")
|
|
239
|
+
debug_print("⚠️ This may have overridden any existing code", "warning")
|
|
240
|
+
|
|
241
|
+
return result
|
|
242
|
+
else:
|
|
243
|
+
debug_print("❌ Failed to force generate new code", "error")
|
|
244
|
+
return None
|
|
245
|
+
return None
|
|
246
|
+
except Exception as e:
|
|
247
|
+
debug_print(f"❌ Failed to force new code generation: {e}", "error")
|
|
248
|
+
return None
|
|
249
|
+
|
|
250
|
+
@debug_function
|
|
251
|
+
def quick_transfer_status_summary(username: str, password: str) -> Optional[str]:
|
|
252
|
+
"""
|
|
253
|
+
Quick function to get a human-readable transfer status summary.
|
|
254
|
+
|
|
255
|
+
Args:
|
|
256
|
+
username: MC5 username
|
|
257
|
+
password: MC5 password
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
Human-readable status summary or None if failed
|
|
261
|
+
"""
|
|
262
|
+
try:
|
|
263
|
+
status = quick_check_transfer_status(username, password)
|
|
264
|
+
|
|
265
|
+
if status:
|
|
266
|
+
if status.get("has_active_code"):
|
|
267
|
+
if status.get("is_expired"):
|
|
268
|
+
return f"⚠️ Transfer code expired: {status.get('transfer_code')}"
|
|
269
|
+
else:
|
|
270
|
+
code = status.get("transfer_code")
|
|
271
|
+
time_remaining = status.get("time_remaining", {})
|
|
272
|
+
time_str = time_remaining.get('formatted', 'Unknown time')
|
|
273
|
+
return f"✅ Active code: {code} (expires in {time_str})"
|
|
274
|
+
else:
|
|
275
|
+
return "ℹ️ No active transfer code - generate one to link a device"
|
|
276
|
+
else:
|
|
277
|
+
return "❌ Failed to check transfer status"
|
|
278
|
+
except Exception as e:
|
|
279
|
+
debug_print(f"❌ Failed to get status summary: {e}", "error")
|
|
280
|
+
return None
|