pycraft-tools 1.0.0__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.
help_manager.py ADDED
@@ -0,0 +1,4335 @@
1
+ """
2
+ ================================================================================
3
+ 📦 ПОМОЩНИК ДЛЯ PYTHON (help_manager.py)
4
+ ================================================================================
5
+ Всё в одном файле: голос, базы данных, музыка, парсинг, автоматизация и ИИ.
6
+ Автор: Юсуф
7
+ ================================================================================
8
+ """
9
+
10
+ # =============================================================================
11
+ # ВСТРОЕННЫЕ МОДУЛИ (лёгкие, импортируем сразу)
12
+ # =============================================================================
13
+ import os
14
+ import sys
15
+ import time
16
+ import random
17
+ import functools
18
+ import threading
19
+ import json
20
+ from datetime import datetime
21
+ import socket
22
+ import subprocess
23
+ import webbrowser
24
+ import tempfile
25
+ import sqlite3
26
+ import math
27
+ import statistics
28
+ import string
29
+ from typing import Any, Callable, Optional, Tuple, List, Union
30
+ from urllib.parse import quote
31
+ import shutil
32
+
33
+ # =============================================================================
34
+ # КЛАСС Manager – 30+ УНИВЕРСАЛЬНЫХ ФУНКЦИЙ
35
+ # =============================================================================
36
+ class Manager:
37
+ """
38
+ ⚡ СТАТИЧЕСКИЕ МЕТОДЫ — вызывай напрямую без создания объекта:
39
+ Manager.название_функции()
40
+
41
+ 🎯 Возможности класса:
42
+ - Парсинг веб-страниц и получение погоды
43
+ - Голосовой ввод и озвучка текста
44
+ - Воспроизведение музыки и звуковые сигналы
45
+ - Работа с файлами и JSON
46
+ - Время и дата в любом формате
47
+ - Генерация случайных чисел и выбор случайных элементов
48
+ - Проверка интернета и скачивание файлов
49
+ - Очистка консоли и задержки
50
+ - Поиск и открытие видео на YouTube
51
+ """
52
+
53
+ # ------------------------------ 🌐 ПАРСИНГ И ПОГОДА ------------------------------
54
+ @staticmethod
55
+ def get_text_with_url(url, class_name):
56
+ """
57
+ 🔍 ПАРСИНГ: Извлекает текст элемента по CSS-классу со страницы.
58
+
59
+ Параметры:
60
+ url (str): полный URL страницы (например, "https://example.com")
61
+ class_name (str): имя CSS-класса, содержимое которого нужно извлечь
62
+
63
+ Возвращает:
64
+ str: текст элемента или сообщение об ошибке
65
+
66
+ 📝 Пример:
67
+ text = Manager.get_text_with_url("https://example.com", "article__title")
68
+ print(text) # Заголовок статьи
69
+
70
+ 🎯 Возможности:
71
+ - Извлечение цен, названий, описаний с сайтов
72
+ - Сбор информации для ботов и парсеров
73
+ """
74
+ import requests
75
+ from bs4 import BeautifulSoup
76
+
77
+ try:
78
+ encoded_url = quote(url, safe=':/?&=')
79
+ headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'}
80
+ response = requests.get(encoded_url, headers=headers, timeout=10)
81
+ response.raise_for_status()
82
+ soup = BeautifulSoup(response.content, 'html.parser')
83
+ element = soup.find(class_=class_name)
84
+ return element.text.strip() if element else f"❌ Класс '{class_name}' не найден"
85
+ except Exception as e:
86
+ return f"❌ Ошибка: {e}"
87
+
88
+ @staticmethod
89
+ def get_weather(city="Москва", api="28c9d95c5e0b423d23e81c1d43c10cf0"):
90
+ """
91
+ ☁️ ПОГОДА: Возвращает словарь с температурой, ветром, влажностью.
92
+
93
+ Параметры:
94
+ city (str): название города (по умолчанию "Москва")
95
+ api (str): API-ключ OpenWeatherMap (встроен запасной)
96
+
97
+ Возвращает:
98
+ dict: словарь с ключами "Температура", "Ощущается", "Описание",
99
+ "Влажность", "Ветер" или строку с ошибкой
100
+
101
+ 📝 Пример:
102
+ weather = Manager.get_weather("Москва")
103
+ print(weather["Температура"]) # +15 °C
104
+ print(weather["Влажность"]) # 65 %
105
+
106
+ 🎯 Возможности:
107
+ - Прогноз для любого города мира
108
+ - Можно встроить в голосового ассистента
109
+ - Автоматические уведомления о погоде
110
+ """
111
+ import requests
112
+
113
+ url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api}&units=metric&lang=ru"
114
+ try:
115
+ response = requests.get(url, timeout=10)
116
+ response.raise_for_status()
117
+ data = response.json()
118
+ return {
119
+ "Температура": f"{round(data['main']['temp'])} °C",
120
+ "Ощущается": f"{round(data['main']['feels_like'])} °C",
121
+ "Описание": data['weather'][0]['description'],
122
+ "Влажность": f"{data['main']['humidity']} %",
123
+ "Ветер": f"{data['wind']['speed']} м/с"
124
+ }
125
+ except Exception as e:
126
+ return f"❌ Ошибка погоды: {e}"
127
+
128
+ # ------------------------------ 🎙️ ГОЛОС И ЗВУК ------------------------------
129
+ @staticmethod
130
+ def listen_text(seconds=5):
131
+ """
132
+ 🎤 ГОЛОСОВОЙ ВВОД: Слушает микрофон и возвращает распознанный текст.
133
+
134
+ Параметры:
135
+ seconds (int): сколько секунд слушать (по умолчанию 5)
136
+
137
+ Возвращает:
138
+ str: распознанный текст или пустую строку, если ничего не услышано
139
+
140
+ 📝 Пример:
141
+ command = Manager.listen_text(5)
142
+ if command:
143
+ print(f"Вы сказали: {command}")
144
+ else:
145
+ print("Ничего не услышано")
146
+
147
+ 🎯 Возможности:
148
+ - Голосовое управление ботом
149
+ - Команды для ассистента
150
+ - Распознавание русского языка
151
+ - Автоматическая настройка шумоподавления
152
+ """
153
+ import speech_recognition as sr
154
+
155
+ r = sr.Recognizer()
156
+ try:
157
+ with sr.Microphone() as source:
158
+ r.adjust_for_ambient_noise(source)
159
+ audio = r.listen(source, timeout=seconds, phrase_time_limit=seconds)
160
+ return r.recognize_google(audio, language="ru-RU")
161
+ except:
162
+ return ""
163
+
164
+ @staticmethod
165
+ def say(text, lang='ru'):
166
+ """
167
+ 🔊 ОЗВУЧКА: Превращает текст в речь с помощью Google TTS.
168
+
169
+ Параметры:
170
+ text (str): текст для озвучки
171
+ lang (str): язык ('ru' — русский, 'en' — английский)
172
+
173
+ 📝 Пример:
174
+ Manager.say("Привет, я твой помощник!")
175
+ Manager.say("Hello, I am your assistant!", lang='en')
176
+
177
+ 🎯 Возможности:
178
+ - Голосовые подсказки и приветствия
179
+ - Озвучка уведомлений
180
+ - Поддержка множества языков
181
+ - Автоматическое удаление временных файлов
182
+ """
183
+ from gtts import gTTS
184
+ from pydub import AudioSegment
185
+ from pydub.playback import play
186
+
187
+ temp_file = None
188
+ try:
189
+ with tempfile.NamedTemporaryFile(suffix='.mp3', delete=False) as fp:
190
+ temp_file = fp.name
191
+ tts = gTTS(text=text, lang=lang)
192
+ tts.save(temp_file)
193
+ sound = AudioSegment.from_mp3(temp_file)
194
+ play(sound)
195
+ finally:
196
+ if temp_file and os.path.exists(temp_file):
197
+ os.unlink(temp_file)
198
+
199
+ @staticmethod
200
+ def play_music(file_path):
201
+ """
202
+ 🎵 МУЗЫКА: Воспроизводит аудиофайл (MP3, WAV, OGG) и ждёт окончания.
203
+
204
+ Параметры:
205
+ file_path (str): полный путь к аудиофайлу
206
+
207
+ 📝 Пример:
208
+ Manager.play_music("C:/Music/song.mp3")
209
+ Manager.play_music("background.wav")
210
+
211
+ 🎯 Возможности:
212
+ - Фоновое воспроизведение музыки
213
+ - Поддержка основных аудиоформатов (MP3, WAV, OGG)
214
+ - Блокирует выполнение до окончания трека
215
+ """
216
+ import pygame
217
+
218
+ try:
219
+ pygame.mixer.init()
220
+ pygame.mixer.music.load(file_path)
221
+ pygame.mixer.music.play()
222
+ while pygame.mixer.music.get_busy():
223
+ time.sleep(0.1)
224
+ except Exception as e:
225
+ print(f"❌ Ошибка воспроизведения: {e}")
226
+
227
+ # ------------------------------ 📂 ФАЙЛЫ И ПУТИ ------------------------------
228
+ @staticmethod
229
+ def search_file_path(relative_path):
230
+ """
231
+ 📁 ПОИСК ФАЙЛА: Возвращает полный путь к файлу относительно папки скрипта.
232
+
233
+ Параметры:
234
+ relative_path (str): относительный путь к файлу (например, "config.json")
235
+
236
+ Возвращает:
237
+ str: полный абсолютный путь к файлу
238
+
239
+ 📝 Пример:
240
+ full_path = Manager.search_file_path("config.json")
241
+ # Вернёт что-то вроде: C:\\Users\\Yusuf\\Programming\\Пайтон\\projects\\config.json
242
+
243
+ 🎯 Возможности:
244
+ - Удобная работа с файлами в проекте
245
+ - Не нужно прописывать длинные абсолютные пути
246
+ - Кроссплатформенность (Windows/Linux/Mac)
247
+ """
248
+ base = os.path.dirname(os.path.abspath(__file__))
249
+ return os.path.join(base, relative_path)
250
+
251
+ @staticmethod
252
+ def open_file(relative_path):
253
+ """
254
+ 🚀 ЗАПУСК ФАЙЛА: Открывает Python-скрипт в новом окне консоли.
255
+
256
+ Параметры:
257
+ relative_path (str): путь к Python-файлу относительно папки скрипта
258
+
259
+ 📝 Пример:
260
+ Manager.open_file("bot.py")
261
+ Manager.open_file("utils/helper.py")
262
+
263
+ 🎯 Возможности:
264
+ - Запуск модулей из главного скрипта
265
+ - Параллельная работа нескольких программ
266
+ - Новое окно консоли для каждого запуска
267
+ """
268
+ full = Manager.search_file_path(relative_path)
269
+ os.system(f'start cmd /k python "{full}"')
270
+
271
+ @staticmethod
272
+ def search_and_open_youtube(query, max_results=1):
273
+ """
274
+ 🔍 ОТКРЫТЬ ВИДЕО НА YOUTUBE: Ищет видео по запросу и открывает в браузере.
275
+
276
+ Параметры:
277
+ query (str): строка для поиска (название песни, видео и т.д.)
278
+ max_results (int): сколько результатов искать (по умолчанию 1)
279
+
280
+ Возвращает:
281
+ str или False: URL открытого видео или False, если ничего не найдено
282
+
283
+ 📝 Пример:
284
+ url = Manager.search_and_open_youtube("Morgenshtern Cadillac")
285
+ if url:
286
+ print(f"Открыто: {url}")
287
+
288
+ 🎯 Возможности:
289
+ - Быстрый доступ к музыке и видео
290
+ - Можно использовать в голосовом ассистенте
291
+ """
292
+ from youtube_search import YoutubeSearch
293
+
294
+ results = YoutubeSearch(query, max_results=max_results).to_dict()
295
+ if results:
296
+ video_url = f"https://youtube.com{results[0]['url_suffix']}"
297
+ webbrowser.open(video_url)
298
+ return video_url
299
+ return False
300
+
301
+ @staticmethod
302
+ def load_json(relative_path):
303
+ """
304
+ 📂 ЗАГРУЗКА JSON: Читает JSON-файл и возвращает словарь.
305
+
306
+ Параметры:
307
+ relative_path (str): путь к JSON-файлу относительно папки скрипта
308
+
309
+ Возвращает:
310
+ dict или None: содержимое JSON-файла в виде словаря, или None при ошибке
311
+
312
+ 📝 Пример:
313
+ data = Manager.load_json("settings.json")
314
+ if data:
315
+ print(data["theme"]) # "dark"
316
+ print(data["volume"]) # 80
317
+
318
+ 🎯 Возможности:
319
+ - Хранение настроек приложения
320
+ - Сохранение данных бота
321
+ - Чтение конфигурационных файлов
322
+ """
323
+ full = Manager.search_file_path(relative_path)
324
+ try:
325
+ with open(full, 'r', encoding='utf-8') as f:
326
+ return json.load(f)
327
+ except Exception as e:
328
+ print(f"❌ Ошибка загрузки JSON: {e}")
329
+ return None
330
+
331
+ @staticmethod
332
+ def save_json(relative_path, data, mode="w"):
333
+ """
334
+ 💾 СОХРАНЕНИЕ JSON: Записывает данные в JSON-файл.
335
+
336
+ Параметры:
337
+ relative_path (str): путь к JSON-файлу относительно папки скрипта
338
+ data (dict): данные для сохранения
339
+ mode (str): режим записи ("w" — перезапись, "a" — добавление)
340
+
341
+ Возвращает:
342
+ bool: True при успехе, False при ошибке
343
+
344
+ 📝 Пример:
345
+ settings = {"theme": "dark", "volume": 80}
346
+ Manager.save_json("settings.json", settings)
347
+ print("Настройки сохранены!")
348
+
349
+ 🎯 Возможности:
350
+ - Сохранение прогресса игрока
351
+ - Экспорт данных
352
+ - Создание резервных копий
353
+ """
354
+ full = Manager.search_file_path(relative_path)
355
+ try:
356
+ with open(full, mode, encoding='utf-8') as f:
357
+ json.dump(data, f, ensure_ascii=False, indent=4)
358
+ return True
359
+ except Exception as e:
360
+ print(f"❌ Ошибка сохранения JSON: {e}")
361
+ return False
362
+
363
+ # ------------------------------ ⏰ ВРЕМЯ И ДАТА ------------------------------
364
+ @staticmethod
365
+ def now(format="%Y-%m-%d %H:%M:%S"):
366
+ """
367
+ 🕐 ТЕКУЩЕЕ ВРЕМЯ: Возвращает дату и время в нужном формате.
368
+
369
+ Параметры:
370
+ format (str): формат даты/времени (по умолчанию "ГГГГ-ММ-ДД ЧЧ:ММ:СС")
371
+
372
+ Возвращает:
373
+ str: отформатированная строка с текущим временем
374
+
375
+ 📝 Пример:
376
+ print(Manager.now()) # 2026-04-03 15:30:00
377
+ print(Manager.now("%H:%M")) # 15:30
378
+ print(Manager.now("%d.%m.%Y")) # 03.04.2026
379
+
380
+ 🎯 Возможности:
381
+ - Логирование событий с временными метками
382
+ - Отображение времени в интерфейсе
383
+ - Создание уникальных имён файлов
384
+ """
385
+ return datetime.now().strftime(format)
386
+
387
+ @staticmethod
388
+ def today():
389
+ """
390
+ 📅 СЕГОДНЯШНЯЯ ДАТА: Возвращает строку в формате ГГГГ-ММ-ДД.
391
+
392
+ Возвращает:
393
+ str: сегодняшняя дата (например, "2026-04-03")
394
+
395
+ 📝 Пример:
396
+ date = Manager.today()
397
+ print(f"Сегодня: {date}") # Сегодня: 2026-04-03
398
+
399
+ # Использование в имени файла
400
+ backup_name = f"backup_{Manager.today()}.json"
401
+ Manager.save_json(backup_name, data)
402
+
403
+ 🎯 Возможности:
404
+ - Формирование ежедневных отчётов
405
+ - Именование бэкапов по дате
406
+ - Проверка актуальности данных
407
+ """
408
+ return datetime.now().strftime("%Y-%m-%d")
409
+
410
+ # ------------------------------ 🎲 СЛУЧАЙНОСТИ ------------------------------
411
+ @staticmethod
412
+ def randint(a, b):
413
+ """
414
+ 🎲 СЛУЧАЙНОЕ ЧИСЛО: Возвращает случайное целое число в диапазоне от a до b (включительно).
415
+
416
+ Параметры:
417
+ a (int): нижняя граница диапазона
418
+ b (int): верхняя граница диапазона
419
+
420
+ Возвращает:
421
+ int: случайное целое число
422
+
423
+ 📝 Пример:
424
+ dice = Manager.randint(1, 6)
425
+ print(f"На кубике выпало: {dice}")
426
+
427
+ bonus = Manager.randint(10, 50)
428
+ print(f"Случайный бонус: +{bonus} золота")
429
+
430
+ 🎯 Возможности:
431
+ - Генерация бонусов в игре
432
+ - Создание случайных паролей
433
+ - Случайные задержки в автоматизации
434
+ """
435
+ return random.randint(a, b)
436
+
437
+ @staticmethod
438
+ def choice(seq):
439
+ """
440
+ 🎲 СЛУЧАЙНЫЙ ВЫБОР: Возвращает случайный элемент из последовательности.
441
+
442
+ Параметры:
443
+ seq (list/tuple/str): последовательность элементов
444
+
445
+ Возвращает:
446
+ любой тип: случайный элемент из последовательности
447
+
448
+ 📝 Пример:
449
+ fruit = Manager.choice(["яблоко", "банан", "груша"])
450
+ print(f"Сегодня едим: {fruit}")
451
+
452
+ # Случайный ответ бота
453
+ answers = ["Привет!", "Здравствуй!", "Рад тебя видеть!"]
454
+ greeting = Manager.choice(answers)
455
+
456
+ 🎯 Возможности:
457
+ - Случайные ответы бота
458
+ - Выбор случайного задания или уровня
459
+ - Разнообразие в автоматических сообщениях
460
+ """
461
+ return random.choice(seq)
462
+
463
+ # ------------------------------ 🌐 СЕТЬ ------------------------------
464
+ @staticmethod
465
+ def is_connected():
466
+ """
467
+ 🌐 ПРОВЕРКА ИНТЕРНЕТА: Проверяет наличие интернет-соединения.
468
+
469
+ Возвращает:
470
+ bool: True если интернет есть, False если нет
471
+
472
+ 📝 Пример:
473
+ if Manager.is_connected():
474
+ weather = Manager.get_weather("Москва")
475
+ print(weather)
476
+ else:
477
+ print("Нет интернета, работаем офлайн")
478
+
479
+ 🎯 Возможности:
480
+ - Проверка перед API-запросами
481
+ - Переключение в офлайн-режим
482
+ - Диагностика сетевых проблем
483
+ """
484
+ try:
485
+ socket.create_connection(("8.8.8.8", 53), timeout=3)
486
+ return True
487
+ except:
488
+ return False
489
+
490
+ @staticmethod
491
+ def download_file(url, save_path):
492
+ """
493
+ 📥 СКАЧИВАНИЕ ФАЙЛА: Загружает файл из интернета и сохраняет локально.
494
+
495
+ Параметры:
496
+ url (str): прямая ссылка на файл
497
+ save_path (str): путь для сохранения относительно папки скрипта
498
+
499
+ Возвращает:
500
+ bool: True при успешном скачивании, False при ошибке
501
+
502
+ 📝 Пример:
503
+ success = Manager.download_file(
504
+ "https://site.com/photo.jpg",
505
+ "images/photo.jpg"
506
+ )
507
+ if success:
508
+ print("Файл скачан!")
509
+
510
+ 🎯 Возможности:
511
+ - Обновление данных бота
512
+ - Скачивание обновлений программы
513
+ - Сохранение изображений и документов
514
+ """
515
+ import requests
516
+
517
+ try:
518
+ response = requests.get(url, stream=True, timeout=15)
519
+ response.raise_for_status()
520
+ full = Manager.search_file_path(save_path)
521
+ with open(full, 'wb') as f:
522
+ for chunk in response.iter_content(chunk_size=8192):
523
+ f.write(chunk)
524
+ return True
525
+ except Exception as e:
526
+ print(f"❌ Ошибка скачивания: {e}")
527
+ return False
528
+
529
+ # ------------------------------ 🛠️ ПРОЧЕЕ ------------------------------
530
+ @staticmethod
531
+ def clear_console():
532
+ """
533
+ 🧹 ОЧИСТКА КОНСОЛИ: Очищает экран терминала.
534
+
535
+ 📝 Пример:
536
+ Manager.clear_console()
537
+ print("Новый чистый вывод!")
538
+
539
+ 🎯 Возможности:
540
+ - Красивый интерфейс командной строки
541
+ - Скрытие старых сообщений
542
+ - Подготовка к новому выводу
543
+ """
544
+ os.system('cls' if os.name == 'nt' else 'clear')
545
+
546
+ @staticmethod
547
+ def wait(seconds, message="Ждём..."):
548
+ """
549
+ ⏳ ЗАДЕРЖКА: Пауза на указанное время с отображением сообщения.
550
+
551
+ Параметры:
552
+ seconds (int/float): время задержки в секундах
553
+ message (str): сообщение, которое будет показано на время ожидания
554
+
555
+ 📝 Пример:
556
+ Manager.wait(3, "Загрузка данных...")
557
+ print("Готово!")
558
+
559
+ Manager.wait(1.5) # просто подождать полторы секунды
560
+
561
+ 🎯 Возможности:
562
+ - Паузы между действиями в автоматизации
563
+ - Ожидание загрузки данных
564
+ - Таймеры обратного отсчёта
565
+ """
566
+ print(message)
567
+ time.sleep(seconds)
568
+
569
+ @staticmethod
570
+ def beep(Gh=1000, time=500):
571
+ """
572
+ 🔊 СИГНАЛ: Издаёт системный звуковой сигнал.
573
+
574
+ Параметры:
575
+ Gh (int): частота звука в Герцах (по умолчанию 1000)
576
+ time (int): длительность в миллисекундах (по умолчанию 500)
577
+
578
+ 📝 Пример:
579
+ Manager.beep(1000, 500) # звук 1000 Гц длительностью 500 мс
580
+ Manager.beep(500, 200) # низкий короткий сигнал
581
+ Manager.beep(2000, 1000) # высокий длинный сигнал
582
+
583
+ 🎯 Возможности:
584
+ - Звуковые уведомления об ошибках
585
+ - Сигналы о завершении длительных операций
586
+ - Звуковая обратная связь в программах
587
+ """
588
+ from winsound import Beep
589
+
590
+ try:
591
+ Beep(Gh, time)
592
+ except:
593
+ print("❌ Не удалось воспроизвести звуковой сигнал")
594
+
595
+ # =============================================================================
596
+ # КЛАСС FolderUtils – РАБОТА С ПАПКАМИ И ФАЙЛОВОЙ СИСТЕМОЙ
597
+ # =============================================================================
598
+ class FolderUtils:
599
+ """
600
+ 📁 РАБОТА С ПАПКАМИ: создание, перемещение, поиск, информация.
601
+
602
+ 📝 Примеры:
603
+ # Создать папку
604
+ FolderUtils.create("my_project/scripts")
605
+
606
+ # Переместить файл
607
+ FolderUtils.move("old.txt", "archive/old.txt")
608
+
609
+ # Найти все Python-файлы
610
+ py_files = FolderUtils.find_files(".", ".py")
611
+
612
+ # Получить имя файла из пути
613
+ name = FolderUtils.get_filename("C:/Users/file.txt") # "file.txt"
614
+
615
+ # Текущая директория
616
+ print(FolderUtils.current_dir())
617
+
618
+ # Размер папки
619
+ size = FolderUtils.get_size("my_project") # в байтах
620
+
621
+ 🎯 Возможности:
622
+ - Создание и удаление папок
623
+ - Перемещение и копирование файлов/папок
624
+ - Поиск файлов по расширению
625
+ - Получение информации о файлах и папках
626
+ - Работа с путями
627
+ """
628
+
629
+ @staticmethod
630
+ def create(path):
631
+ """
632
+ 📂 СОЗДАТЬ ПАПКУ: Создаёт папку (и все промежуточные, если нужно).
633
+
634
+ Параметры:
635
+ path (str): путь к создаваемой папке
636
+
637
+ Возвращает:
638
+ bool: True если создана, False если уже существует
639
+
640
+ 📝 Пример:
641
+ FolderUtils.create("my_project/data")
642
+ FolderUtils.create("backups/2024/январь")
643
+ """
644
+ import os
645
+ if not os.path.exists(path):
646
+ os.makedirs(path)
647
+ print(f"📂 Папка создана: {path}")
648
+ return True
649
+ else:
650
+ print(f"📂 Папка уже существует: {path}")
651
+ return False
652
+
653
+ @staticmethod
654
+ def delete(path):
655
+ """
656
+ 🗑️ УДАЛИТЬ ПАПКУ: Удаляет папку со всем содержимым (осторожно!).
657
+
658
+ Параметры:
659
+ path (str): путь к папке
660
+
661
+ Возвращает:
662
+ bool: True если удалена, False если ошибка
663
+
664
+ 📝 Пример:
665
+ FolderUtils.delete("temp_files")
666
+ """
667
+ import shutil
668
+ try:
669
+ if os.path.exists(path):
670
+ shutil.rmtree(path)
671
+ print(f"🗑️ Папка удалена: {path}")
672
+ return True
673
+ else:
674
+ print(f"❌ Папка не найдена: {path}")
675
+ return False
676
+ except Exception as e:
677
+ print(f"❌ Ошибка удаления: {e}")
678
+ return False
679
+
680
+ @staticmethod
681
+ def move(src, dst):
682
+ """
683
+ 📦 ПЕРЕМЕСТИТЬ: Перемещает файл или папку в новое место.
684
+
685
+ Параметры:
686
+ src (str): исходный путь
687
+ dst (str): путь назначения
688
+
689
+ Возвращает:
690
+ bool: True при успехе
691
+
692
+ 📝 Пример:
693
+ FolderUtils.move("old_file.txt", "archive/old_file.txt")
694
+ FolderUtils.move("my_folder", "backup/my_folder")
695
+ """
696
+ import shutil
697
+ try:
698
+ shutil.move(src, dst)
699
+ print(f"📦 Перемещено: {src} → {dst}")
700
+ return True
701
+ except Exception as e:
702
+ print(f"❌ Ошибка перемещения: {e}")
703
+ return False
704
+
705
+ @staticmethod
706
+ def copy(src, dst):
707
+ """
708
+ 📋 КОПИРОВАТЬ: Копирует файл или папку.
709
+
710
+ Параметры:
711
+ src (str): исходный путь
712
+ dst (str): путь назначения
713
+
714
+ Возвращает:
715
+ bool: True при успехе
716
+
717
+ 📝 Пример:
718
+ FolderUtils.copy("config.json", "backup/config.json")
719
+ """
720
+ import shutil
721
+ try:
722
+ if os.path.isdir(src):
723
+ shutil.copytree(src, dst)
724
+ else:
725
+ shutil.copy2(src, dst)
726
+ print(f"📋 Скопировано: {src} → {dst}")
727
+ return True
728
+ except Exception as e:
729
+ print(f"❌ Ошибка копирования: {e}")
730
+ return False
731
+
732
+ @staticmethod
733
+ def find_files(directory, extension=None):
734
+ """
735
+ 🔍 НАЙТИ ФАЙЛЫ: Ищет все файлы в папке (и подпапках).
736
+
737
+ Параметры:
738
+ directory (str): папка для поиска
739
+ extension (str, optional): фильтр по расширению (например, ".py", ".txt")
740
+
741
+ Возвращает:
742
+ list[str]: список полных путей к найденным файлам
743
+
744
+ 📝 Пример:
745
+ # Все файлы
746
+ all_files = FolderUtils.find_files("my_project")
747
+
748
+ # Только Python-файлы
749
+ py_files = FolderUtils.find_files("my_project", ".py")
750
+
751
+ # Только текстовые
752
+ txt_files = FolderUtils.find_files("my_project", ".txt")
753
+ """
754
+ import os
755
+ result = []
756
+ for root, dirs, files in os.walk(directory):
757
+ for file in files:
758
+ if extension is None or file.endswith(extension):
759
+ result.append(os.path.join(root, file))
760
+ print(f"🔍 Найдено {len(result)} файлов" + (f" с расширением {extension}" if extension else ""))
761
+ return result
762
+
763
+ @staticmethod
764
+ def find_folders(directory):
765
+ """
766
+ 📁 НАЙТИ ПАПКИ: Возвращает список всех подпапок.
767
+
768
+ Параметры:
769
+ directory (str): папка для поиска
770
+
771
+ Возвращает:
772
+ list[str]: список путей к подпапкам
773
+
774
+ 📝 Пример:
775
+ folders = FolderUtils.find_folders("my_project")
776
+ for folder in folders:
777
+ print(folder)
778
+ """
779
+ import os
780
+ result = []
781
+ for root, dirs, files in os.walk(directory):
782
+ for d in dirs:
783
+ result.append(os.path.join(root, d))
784
+ print(f"📁 Найдено {len(result)} подпапок")
785
+ return result
786
+
787
+ @staticmethod
788
+ def current_dir():
789
+ """
790
+ 📍 ТЕКУЩАЯ ПАПКА: Возвращает путь к текущей рабочей директории.
791
+
792
+ Возвращает:
793
+ str: полный путь
794
+
795
+ 📝 Пример:
796
+ where_am_i = FolderUtils.current_dir()
797
+ print(f"Скрипт запущен из: {where_am_i}")
798
+ """
799
+ import os
800
+ return os.getcwd()
801
+
802
+ @staticmethod
803
+ def get_filename(path):
804
+ """
805
+ 📄 ИМЯ ФАЙЛА: Извлекает имя файла из полного пути.
806
+
807
+ Параметры:
808
+ path (str): полный путь к файлу
809
+
810
+ Возвращает:
811
+ str: имя файла с расширением
812
+
813
+ 📝 Пример:
814
+ name = FolderUtils.get_filename("C:/Users/Yusuf/doc.txt") # "doc.txt"
815
+ name = FolderUtils.get_filename("/home/user/file.py") # "file.py"
816
+ """
817
+ import os
818
+ return os.path.basename(path)
819
+
820
+ @staticmethod
821
+ def get_extension(path):
822
+ """
823
+ 🔤 РАСШИРЕНИЕ ФАЙЛА: Извлекает расширение файла.
824
+
825
+ Параметры:
826
+ path (str): путь к файлу
827
+
828
+ Возвращает:
829
+ str: расширение с точкой (например, ".txt", ".py")
830
+
831
+ 📝 Пример:
832
+ ext = FolderUtils.get_extension("photo.jpg") # ".jpg"
833
+ ext = FolderUtils.get_extension("script.py") # ".py"
834
+ """
835
+ import os
836
+ return os.path.splitext(path)[1]
837
+
838
+ @staticmethod
839
+ def get_filename_without_ext(path):
840
+ """
841
+ 📄 ИМЯ БЕЗ РАСШИРЕНИЯ: Извлекает имя файла без расширения.
842
+
843
+ Параметры:
844
+ path (str): путь к файлу
845
+
846
+ Возвращает:
847
+ str: имя файла без расширения
848
+
849
+ 📝 Пример:
850
+ name = FolderUtils.get_filename_without_ext("photo.jpg") # "photo"
851
+ name = FolderUtils.get_filename_without_ext("script.py") # "script"
852
+ """
853
+ import os
854
+ return os.path.splitext(os.path.basename(path))[0]
855
+
856
+ @staticmethod
857
+ def get_parent(path):
858
+ """
859
+ 📂 РОДИТЕЛЬСКАЯ ПАПКА: Возвращает путь к родительской папке.
860
+
861
+ Параметры:
862
+ path (str): путь к файлу или папке
863
+
864
+ Возвращает:
865
+ str: путь к родительской папке
866
+
867
+ 📝 Пример:
868
+ parent = FolderUtils.get_parent("C:/Users/Yusuf/doc.txt") # "C:/Users/Yusuf"
869
+ parent = FolderUtils.get_parent("/home/user/projects") # "/home/user"
870
+ """
871
+ import os
872
+ return os.path.dirname(path)
873
+
874
+ @staticmethod
875
+ def get_size(path):
876
+ """
877
+ 📏 РАЗМЕР: Вычисляет размер файла или папки (всего содержимого).
878
+
879
+ Параметры:
880
+ path (str): путь к файлу или папке
881
+
882
+ Возвращает:
883
+ int: размер в байтах
884
+
885
+ 📝 Пример:
886
+ size_bytes = FolderUtils.get_size("my_project")
887
+ size_mb = size_bytes / (1024 * 1024)
888
+ print(f"Размер проекта: {size_mb:.2f} MB")
889
+ """
890
+ import os
891
+ total = 0
892
+ if os.path.isfile(path):
893
+ return os.path.getsize(path)
894
+ elif os.path.isdir(path):
895
+ for root, dirs, files in os.walk(path):
896
+ for f in files:
897
+ fp = os.path.join(root, f)
898
+ if os.path.exists(fp):
899
+ total += os.path.getsize(fp)
900
+ return total
901
+
902
+ @staticmethod
903
+ def format_size(size_bytes):
904
+ """
905
+ 📊 ФОРМАТИРОВАТЬ РАЗМЕР: Превращает байты в читаемую строку.
906
+
907
+ Параметры:
908
+ size_bytes (int): размер в байтах
909
+
910
+ Возвращает:
911
+ str: читаемая строка (например, "2.5 MB")
912
+
913
+ 📝 Пример:
914
+ size = FolderUtils.get_size("my_project")
915
+ print(FolderUtils.format_size(size)) # "145.3 MB"
916
+ """
917
+ for unit in ['B', 'KB', 'MB', 'GB', 'TB']:
918
+ if size_bytes < 1024:
919
+ return f"{size_bytes:.1f} {unit}"
920
+ size_bytes /= 1024
921
+ return f"{size_bytes:.1f} PB"
922
+
923
+ @staticmethod
924
+ def exists(path):
925
+ """
926
+ ❓ СУЩЕСТВУЕТ?: Проверяет, существует ли файл или папка.
927
+
928
+ Параметры:
929
+ path (str): путь для проверки
930
+
931
+ Возвращает:
932
+ bool: True если существует
933
+
934
+ 📝 Пример:
935
+ if FolderUtils.exists("config.json"):
936
+ print("Конфиг найден!")
937
+ else:
938
+ print("Конфиг отсутствует")
939
+ """
940
+ import os
941
+ return os.path.exists(path)
942
+
943
+ @staticmethod
944
+ def is_file(path):
945
+ """
946
+ 📄 ЭТО ФАЙЛ?: Проверяет, является ли путь файлом.
947
+
948
+ Параметры:
949
+ path (str): путь для проверки
950
+
951
+ Возвращает:
952
+ bool: True если это файл
953
+
954
+ 📝 Пример:
955
+ if FolderUtils.is_file("script.py"):
956
+ print("Это файл, можно читать")
957
+ """
958
+ import os
959
+ return os.path.isfile(path)
960
+
961
+ @staticmethod
962
+ def is_folder(path):
963
+ """
964
+ 📁 ЭТО ПАПКА?: Проверяет, является ли путь папкой.
965
+
966
+ Параметры:
967
+ path (str): путь для проверки
968
+
969
+ Возвращает:
970
+ bool: True если это папка
971
+
972
+ 📝 Пример:
973
+ if FolderUtils.is_folder("my_project"):
974
+ contents = FolderUtils.find_files("my_project")
975
+ """
976
+ import os
977
+ return os.path.isdir(path)
978
+
979
+ @staticmethod
980
+ def list_contents(directory):
981
+ """
982
+ 📋 СОДЕРЖИМОЕ ПАПКИ: Возвращает список файлов и папок в директории.
983
+
984
+ Параметры:
985
+ directory (str): путь к папке
986
+
987
+ Возвращает:
988
+ dict: {"files": [...], "folders": [...]}
989
+
990
+ 📝 Пример:
991
+ contents = FolderUtils.list_contents("my_project")
992
+ print(f"Файлов: {len(contents['files'])}")
993
+ print(f"Папок: {len(contents['folders'])}")
994
+ """
995
+ import os
996
+ files = []
997
+ folders = []
998
+ try:
999
+ for item in os.listdir(directory):
1000
+ full_path = os.path.join(directory, item)
1001
+ if os.path.isfile(full_path):
1002
+ files.append(item)
1003
+ elif os.path.isdir(full_path):
1004
+ folders.append(item)
1005
+ except Exception as e:
1006
+ print(f"❌ Ошибка чтения папки: {e}")
1007
+ return {"files": files, "folders": folders}
1008
+
1009
+ # =============================================================================
1010
+ # КЛАСС Console – РАБОТА С КОНСОЛЬЮ (ЦВЕТА, КУРСОР, ПРОГРЕСС)
1011
+ # =============================================================================
1012
+ class Console:
1013
+ """
1014
+ 🖥️ РАБОТА С КОНСОЛЬЮ: цвета, управление курсором, прогресс-бары и скрытие окна.
1015
+
1016
+ Все методы статические — вызывай напрямую:
1017
+ Console.hide()
1018
+ Console.success("Готово!")
1019
+ Console.progress_bar(100, "Загрузка")
1020
+
1021
+ 🎯 Возможности класса:
1022
+ - Скрытие и показ консольного окна
1023
+ - Цветной вывод текста (16 цветов + стили)
1024
+ - Управление положением курсора
1025
+ - Прогресс-бары для длительных операций
1026
+ - Быстрые сообщения: success, error, warning, info
1027
+ - Красивые заголовки на всю ширину консоли
1028
+ """
1029
+
1030
+ # ──────────────────────────────────────────────
1031
+ # СКРЫТИЕ / ПОКАЗ КОНСОЛИ
1032
+ # ──────────────────────────────────────────────
1033
+ @staticmethod
1034
+ def hide():
1035
+ """
1036
+ 👻 СКРЫТЬ КОНСОЛЬ: Мгновенно скрывает окно консоли, перезапуская скрипт в фоне.
1037
+
1038
+ 📝 Пример:
1039
+ Console.hide()
1040
+ # Скрипт продолжит работу, но окна не будет видно
1041
+
1042
+ 🎯 Возможности:
1043
+ - Создание фоновых сервисов
1044
+ - Скрытие технического вывода от пользователя
1045
+ - Работа программы в трее
1046
+ """
1047
+ if '--hidden' not in sys.argv:
1048
+ pythonw_path = sys.executable.replace('python.exe', 'pythonw.exe')
1049
+ if os.path.exists(pythonw_path):
1050
+ subprocess.Popen(
1051
+ [pythonw_path] + sys.argv + ['--hidden'],
1052
+ creationflags=0x00000008 | 0x08000000,
1053
+ stdout=subprocess.DEVNULL,
1054
+ stderr=subprocess.DEVNULL,
1055
+ stdin=subprocess.DEVNULL
1056
+ )
1057
+ sys.exit(0)
1058
+
1059
+ @staticmethod
1060
+ def show():
1061
+ """
1062
+ 📺 ПОКАЗАТЬ КОНСОЛЬ: Создаёт новую консоль и привязывает к ней процесс.
1063
+
1064
+ 📝 Пример:
1065
+ Console.show()
1066
+ print("Консоль снова видна!")
1067
+
1068
+ 🎯 Возможности:
1069
+ - Восстановление скрытой консоли
1070
+ - Отладка фоновых процессов
1071
+ """
1072
+ import ctypes
1073
+
1074
+ kernel32 = ctypes.WinDLL('kernel32')
1075
+ kernel32.AllocConsole()
1076
+
1077
+ # ──────────────────────────────────────────────
1078
+ # ЦВЕТА И СТИЛИ
1079
+ # ──────────────────────────────────────────────
1080
+ @staticmethod
1081
+ def color(text, fg=None, bg=None, style=None):
1082
+ """
1083
+ 🎨 ЦВЕТНОЙ ТЕКСТ: Окрашивает текст для вывода в консоль.
1084
+
1085
+ Параметры:
1086
+ text (str): текст для окрашивания
1087
+ fg (str): цвет текста ('red', 'green', 'yellow', 'blue', 'magenta', 'cyan', 'white')
1088
+ bg (str): цвет фона ('bg_red', 'bg_green', 'bg_blue', ...)
1089
+ style (str): стиль текста ('bold', 'underline', 'italic', 'blink', ...)
1090
+
1091
+ Возвращает:
1092
+ str: строка с ANSI-кодами для цветного вывода
1093
+
1094
+ 📝 Пример:
1095
+ print(Console.color("Ошибка!", "red", style="bold"))
1096
+ print(Console.color("Успех!", "green"))
1097
+ print(Console.color("Важно", "yellow", "bg_blue", "underline"))
1098
+
1099
+ 🎯 Возможности:
1100
+ - Выделение важных сообщений
1101
+ - Цветные логи и отчёты
1102
+ - Красивый интерфейс командной строки
1103
+ """
1104
+ colors = {
1105
+ 'black': '30', 'red': '31', 'green': '32', 'yellow': '33',
1106
+ 'blue': '34', 'magenta': '35', 'cyan': '36', 'white': '37',
1107
+ 'bright_black': '90', 'bright_red': '91', 'bright_green': '92',
1108
+ 'bright_yellow': '93', 'bright_blue': '94', 'bright_magenta': '95',
1109
+ 'bright_cyan': '96', 'bright_white': '97',
1110
+ 'bg_black': '40', 'bg_red': '41', 'bg_green': '42', 'bg_yellow': '43',
1111
+ 'bg_blue': '44', 'bg_magenta': '45', 'bg_cyan': '46', 'bg_white': '47',
1112
+ 'bg_bright_black': '100', 'bg_bright_red': '101', 'bg_bright_green': '102',
1113
+ 'bg_bright_yellow': '103', 'bg_bright_blue': '104', 'bg_bright_magenta': '105',
1114
+ 'bg_bright_cyan': '106', 'bg_bright_white': '107',
1115
+ 'bold': '1', 'dim': '2', 'italic': '3', 'underline': '4',
1116
+ 'blink': '5', 'reverse': '7', 'hidden': '8', 'strikethrough': '9'
1117
+ }
1118
+ codes = []
1119
+ if fg and fg in colors:
1120
+ codes.append(colors[fg])
1121
+ if bg and bg in colors:
1122
+ codes.append(colors[bg])
1123
+ if style and style in colors:
1124
+ codes.append(colors[style])
1125
+ if codes:
1126
+ return f'\033[{";".join(codes)}m{text}\033[0m'
1127
+ return text
1128
+
1129
+ # ──────────────────────────────────────────────
1130
+ # УПРАВЛЕНИЕ КОНСОЛЬЮ
1131
+ # ──────────────────────────────────────────────
1132
+ @staticmethod
1133
+ def clear():
1134
+ """
1135
+ 🧹 ОЧИСТКА: Полностью очищает экран консоли.
1136
+
1137
+ 📝 Пример:
1138
+ Console.clear()
1139
+ print("Чистый экран!")
1140
+
1141
+ 🎯 Возможности:
1142
+ - Очистка перед новым выводом
1143
+ - Удаление старых сообщений
1144
+ """
1145
+ os.system('cls' if os.name == 'nt' else 'clear')
1146
+
1147
+ @staticmethod
1148
+ def move_to(x, y):
1149
+ """
1150
+ ➡️ ПЕРЕМЕСТИТЬ КУРСОР: Перемещает курсор в указанную позицию (x, y).
1151
+
1152
+ Параметры:
1153
+ x (int): столбец (начиная с 1)
1154
+ y (int): строка (начиная с 1)
1155
+
1156
+ 📝 Пример:
1157
+ Console.move_to(10, 5)
1158
+ print("Текст в позиции 10,5")
1159
+
1160
+ 🎯 Возможности:
1161
+ - Создание интерфейсов в консоли
1162
+ - Обновление данных на экране без очистки
1163
+ """
1164
+ sys.stdout.write(f'\033[{y};{x}H')
1165
+ sys.stdout.flush()
1166
+
1167
+ @staticmethod
1168
+ def move_up(n=1):
1169
+ """
1170
+ ⬆️ ВВЕРХ: Перемещает курсор вверх на n строк.
1171
+
1172
+ Параметры:
1173
+ n (int): количество строк для перемещения
1174
+
1175
+ 📝 Пример:
1176
+ Console.move_up(3)
1177
+ print("Этот текст будет на 3 строки выше")
1178
+ """
1179
+ sys.stdout.write(f'\033[{n}A')
1180
+ sys.stdout.flush()
1181
+
1182
+ @staticmethod
1183
+ def move_down(n=1):
1184
+ """
1185
+ ⬇️ ВНИЗ: Перемещает курсор вниз на n строк.
1186
+
1187
+ Параметры:
1188
+ n (int): количество строк для перемещения
1189
+
1190
+ 📝 Пример:
1191
+ Console.move_down(2)
1192
+ print("Этот текст будет на 2 строки ниже")
1193
+ """
1194
+ sys.stdout.write(f'\033[{n}B')
1195
+ sys.stdout.flush()
1196
+
1197
+ @staticmethod
1198
+ def hide_cursor():
1199
+ """
1200
+ 🙈 СКРЫТЬ КУРСОР: Делает мигающий курсор невидимым.
1201
+
1202
+ 📝 Пример:
1203
+ Console.hide_cursor()
1204
+ # Курсор исчезнет
1205
+
1206
+ 🎯 Возможности:
1207
+ - Улучшение визуала для анимаций
1208
+ - Скрытие курсора во время загрузки
1209
+ """
1210
+ sys.stdout.write('\033[?25l')
1211
+ sys.stdout.flush()
1212
+
1213
+ @staticmethod
1214
+ def show_cursor():
1215
+ """
1216
+ 👁️ ПОКАЗАТЬ КУРСОР: Возвращает видимость мигающего курсора.
1217
+
1218
+ 📝 Пример:
1219
+ Console.show_cursor()
1220
+ # Курсор снова виден
1221
+ """
1222
+ sys.stdout.write('\033[?25h')
1223
+ sys.stdout.flush()
1224
+
1225
+ @staticmethod
1226
+ def save_pos():
1227
+ """
1228
+ 💾 СОХРАНИТЬ ПОЗИЦИЮ: Сохраняет текущую позицию курсора.
1229
+
1230
+ 📝 Пример:
1231
+ Console.save_pos()
1232
+ Console.move_to(1, 10)
1233
+ print("Временный вывод")
1234
+ Console.restore_pos()
1235
+ print("Вернулись обратно")
1236
+
1237
+ 🎯 Возможности:
1238
+ - Временный вывод без потери позиции
1239
+ - Создание сложных консольных интерфейсов
1240
+ """
1241
+ sys.stdout.write('\033[s')
1242
+ sys.stdout.flush()
1243
+
1244
+ @staticmethod
1245
+ def restore_pos():
1246
+ """
1247
+ 🔄 ВОССТАНОВИТЬ ПОЗИЦИЮ: Возвращает курсор на сохранённую позицию.
1248
+
1249
+ 📝 Пример:
1250
+ Console.restore_pos()
1251
+ """
1252
+ sys.stdout.write('\033[u')
1253
+ sys.stdout.flush()
1254
+
1255
+ # ──────────────────────────────────────────────
1256
+ # ПОЛЕЗНЫЕ УТИЛИТЫ
1257
+ # ──────────────────────────────────────────────
1258
+ @staticmethod
1259
+ def size():
1260
+ """
1261
+ 📏 РАЗМЕР КОНСОЛИ: Возвращает размер консоли (ширина, высота).
1262
+
1263
+ Возвращает:
1264
+ tuple: (ширина, высота) в символах
1265
+
1266
+ 📝 Пример:
1267
+ w, h = Console.size()
1268
+ print(f"Консоль: {w}x{h} символов")
1269
+
1270
+ 🎯 Возможности:
1271
+ - Адаптивный вывод под размер окна
1272
+ - Центрирование текста
1273
+ """
1274
+ try:
1275
+ return os.get_terminal_size().columns, os.get_terminal_size().lines
1276
+ except:
1277
+ return 80, 24
1278
+
1279
+ @staticmethod
1280
+ def center(text):
1281
+ """
1282
+ 🎯 ЦЕНТРИРОВАНИЕ: Центрирует текст по ширине консоли.
1283
+
1284
+ Параметры:
1285
+ text (str): текст для центрирования
1286
+
1287
+ Возвращает:
1288
+ str: текст, дополненный пробелами до ширины консоли
1289
+
1290
+ 📝 Пример:
1291
+ centered = Console.center("Добро пожаловать!")
1292
+ print(centered)
1293
+ """
1294
+ w, h = Console.size()
1295
+ return text.center(w)
1296
+
1297
+ @staticmethod
1298
+ def progress_bar(total, prefix="", suffix="", length=50, fill="█"):
1299
+ """
1300
+ 📊 ПРОГРЕСС-БАР: Показывает анимированный индикатор выполнения.
1301
+
1302
+ Параметры:
1303
+ total (int): общее количество итераций
1304
+ prefix (str): текст перед прогресс-баром
1305
+ suffix (str): текст после прогресс-бара
1306
+ length (int): длина прогресс-бара в символах
1307
+ fill (str): символ заполнения
1308
+
1309
+ 📝 Пример:
1310
+ Console.progress_bar(100, "Загрузка:", "Пожалуйста, подождите")
1311
+ # Выведет: Загрузка: [████████████░░░░░░░░░░░░░░░░░░░░░░░░] 40.0% Пожалуйста, подождите
1312
+
1313
+ 🎯 Возможности:
1314
+ - Отображение прогресса загрузки
1315
+ - Визуализация выполнения длительных операций
1316
+ - Информирование пользователя о статусе
1317
+ """
1318
+ for i in range(total + 1):
1319
+ percent = i / total
1320
+ filled = int(length * percent)
1321
+ bar = fill * filled + "░" * (length - filled)
1322
+ sys.stdout.write(f"\r{prefix} [{bar}] {percent:.1%} {suffix}")
1323
+ sys.stdout.flush()
1324
+ if i < total:
1325
+ time.sleep(0.05)
1326
+ print()
1327
+
1328
+ # ──────────────────────────────────────────────
1329
+ # ОФОРМЛЕНИЕ ТЕКСТА (быстрые сообщения)
1330
+ # ──────────────────────────────────────────────
1331
+ @staticmethod
1332
+ def success(text):
1333
+ """
1334
+ ✅ УСПЕХ: Выводит зелёное сообщение об успехе.
1335
+
1336
+ Параметры:
1337
+ text (str): текст сообщения
1338
+
1339
+ 📝 Пример:
1340
+ Console.success("Файл сохранён!")
1341
+ # Выведет: ✓ Файл сохранён! (зелёным цветом)
1342
+
1343
+ 🎯 Возможности:
1344
+ - Уведомления об успешных операциях
1345
+ - Позитивная обратная связь пользователю
1346
+ """
1347
+ print(Console.color(f"✓ {text}", "green"))
1348
+
1349
+ @staticmethod
1350
+ def error(text):
1351
+ """
1352
+ ❌ ОШИБКА: Выводит красное сообщение об ошибке.
1353
+
1354
+ Параметры:
1355
+ text (str): текст сообщения
1356
+
1357
+ 📝 Пример:
1358
+ Console.error("Не удалось подключиться к серверу!")
1359
+ # Выведет: ✗ Не удалось подключиться к серверу! (красным цветом)
1360
+
1361
+ 🎯 Возможности:
1362
+ - Отображение критических ошибок
1363
+ - Привлечение внимания к проблемам
1364
+ """
1365
+ print(Console.color(f"✗ {text}", "red"))
1366
+
1367
+ @staticmethod
1368
+ def warning(text):
1369
+ """
1370
+ ⚠️ ПРЕДУПРЕЖДЕНИЕ: Выводит жёлтое предупреждение.
1371
+
1372
+ Параметры:
1373
+ text (str): текст предупреждения
1374
+
1375
+ 📝 Пример:
1376
+ Console.warning("Батарея ниже 10%!")
1377
+ # Выведет: ⚠ Батарея ниже 10%! (жёлтым цветом)
1378
+
1379
+ 🎯 Возможности:
1380
+ - Некритичные предупреждения
1381
+ - Напоминания и советы
1382
+ """
1383
+ print(Console.color(f"⚠ {text}", "yellow"))
1384
+
1385
+ @staticmethod
1386
+ def info(text):
1387
+ """
1388
+ ℹ️ ИНФОРМАЦИЯ: Выводит голубое информационное сообщение.
1389
+
1390
+ Параметры:
1391
+ text (str): текст сообщения
1392
+
1393
+ 📝 Пример:
1394
+ Console.info("Сервер запущен на порту 8080")
1395
+ # Выведет: ℹ Сервер запущен на порту 8080 (голубым цветом)
1396
+
1397
+ 🎯 Возможности:
1398
+ - Информационные сообщения
1399
+ - Статус выполнения операций
1400
+ """
1401
+ print(Console.color(f"ℹ {text}", "cyan"))
1402
+
1403
+ @staticmethod
1404
+ def header(text, symbol="="):
1405
+ """
1406
+ 📌 ЗАГОЛОВОК: Выводит жирный заголовок на всю ширину консоли.
1407
+
1408
+ Параметры:
1409
+ text (str): текст заголовка
1410
+ symbol (str): символ для линии (по умолчанию "=")
1411
+
1412
+ 📝 Пример:
1413
+ Console.header("МОЯ ПРОГРАММА")
1414
+ # Выведет:
1415
+ # ============================================
1416
+ # МОЯ ПРОГРАММА
1417
+ # ============================================
1418
+
1419
+ 🎯 Возможности:
1420
+ - Оформление разделов программы
1421
+ - Красивые заголовки в отчётах
1422
+ """
1423
+ w, h = Console.size()
1424
+ line = symbol * w
1425
+ print(Console.color(line, "blue", style="bold"))
1426
+ print(Console.color(text.center(w), "blue", style="bold"))
1427
+ print(Console.color(line, "blue", style="bold"))
1428
+
1429
+
1430
+ # =============================================================================
1431
+ # КЛАСС BazaDB – УНИВЕРСАЛЬНАЯ РАБОТА С SQLite
1432
+ # =============================================================================
1433
+ class BazaDB:
1434
+ """
1435
+ 🗄️ БАЗА ДАННЫХ SQLite — лёгкая, быстрая, без сервера.
1436
+
1437
+ 📝 Пример использования:
1438
+ db = BazaDB("mybase.db")
1439
+ db.create_table("users", ["name", "age"])
1440
+ db.insert("users", {"name": "Юсуф", "age": 12})
1441
+ users = db.get_all("users")
1442
+ db.close()
1443
+
1444
+ 🎯 Возможности класса:
1445
+ - Создание и удаление таблиц
1446
+ - Добавление, поиск, обновление и удаление записей
1447
+ - Поиск по точному совпадению и по части текста
1448
+ - Статистика и список таблиц
1449
+ - Полная очистка и удаление таблиц
1450
+ """
1451
+
1452
+ def __init__(self, db_name):
1453
+ """
1454
+ 🚀 ПОДКЛЮЧЕНИЕ: Открывает или создаёт файл базы данных SQLite.
1455
+
1456
+ Параметры:
1457
+ db_name (str): имя файла базы данных (например, "mybase.db")
1458
+
1459
+ 📝 Пример:
1460
+ db = BazaDB("shop.db")
1461
+ db = BazaDB(":memory:") # база в оперативной памяти
1462
+
1463
+ 🎯 Возможности:
1464
+ - Автоматическое создание файла, если его нет
1465
+ - Поддержка баз в памяти (для тестов)
1466
+ """
1467
+ self.conn = sqlite3.connect(db_name)
1468
+ self.cursor = self.conn.cursor()
1469
+ print(f"✅ Подключено к базе: {db_name}")
1470
+
1471
+ def create_table(self, table_name, columns, drop_if_exists=False):
1472
+ """
1473
+ 📦 СОЗДАТЬ ТАБЛИЦУ: Создаёт новую таблицу с указанными колонками.
1474
+
1475
+ Параметры:
1476
+ table_name (str): имя таблицы
1477
+ columns (list): список имён колонок (id добавится автоматически)
1478
+ drop_if_exists (bool): удалить таблицу, если она уже существует
1479
+
1480
+ 📝 Пример:
1481
+ db.create_table("products", ["name", "price", "quantity"])
1482
+ db.create_table("users", ["email", "password"], drop_if_exists=True)
1483
+
1484
+ 🎯 Возможности:
1485
+ - Автоматическое добавление PRIMARY KEY колонки id
1486
+ - Все колонки создаются с типом TEXT (для универсальности)
1487
+ - Опциональное удаление существующей таблицы
1488
+ """
1489
+ if drop_if_exists:
1490
+ self.cursor.execute(f'DROP TABLE IF EXISTS {table_name}')
1491
+ cols = "id INTEGER PRIMARY KEY AUTOINCREMENT"
1492
+ for col in columns:
1493
+ cols += f", {col} TEXT"
1494
+ self.cursor.execute(f'CREATE TABLE IF NOT EXISTS {table_name} ({cols})')
1495
+ self.conn.commit()
1496
+ print(f" 📦 Таблица '{table_name}' создана")
1497
+
1498
+ def insert(self, table_name, data):
1499
+ """
1500
+ ➕ ДОБАВИТЬ ЗАПИСЬ: Вставляет новую строку в таблицу.
1501
+
1502
+ Параметры:
1503
+ table_name (str): имя таблицы
1504
+ data (dict): словарь {колонка: значение}
1505
+
1506
+ Возвращает:
1507
+ int: ID добавленной записи
1508
+
1509
+ 📝 Пример:
1510
+ new_id = db.insert("users", {"name": "Анна", "age": "25"})
1511
+ print(f"Добавлен пользователь с ID={new_id}")
1512
+
1513
+ 🎯 Возможности:
1514
+ - Автоматическая генерация ID
1515
+ - Частичное заполнение (можно указать не все колонки)
1516
+ """
1517
+ columns = ', '.join(data.keys())
1518
+ placeholders = ', '.join(['?' for _ in data])
1519
+ values = tuple(data.values())
1520
+ query = f'INSERT INTO {table_name} ({columns}) VALUES ({placeholders})'
1521
+ self.cursor.execute(query, values)
1522
+ self.conn.commit()
1523
+ last_id = self.cursor.lastrowid
1524
+ print(f" ➕ Добавлена запись ID={last_id} в '{table_name}'")
1525
+ return last_id
1526
+
1527
+ def get_all(self, table_name):
1528
+ """
1529
+ 📋 ПОЛУЧИТЬ ВСЁ: Возвращает все записи из таблицы.
1530
+
1531
+ Параметры:
1532
+ table_name (str): имя таблицы
1533
+
1534
+ Возвращает:
1535
+ list[dict]: список словарей с данными
1536
+
1537
+ 📝 Пример:
1538
+ all_users = db.get_all("users")
1539
+ for user in all_users:
1540
+ print(f"{user['id']}: {user['name']}, {user['age']} лет")
1541
+
1542
+ 🎯 Возможности:
1543
+ - Выгрузка всех данных для отчётов
1544
+ - Отображение содержимого таблицы
1545
+ """
1546
+ self.cursor.execute(f'SELECT * FROM {table_name}')
1547
+ rows = self.cursor.fetchall()
1548
+ self.cursor.execute(f'PRAGMA table_info({table_name})')
1549
+ columns = [col[1] for col in self.cursor.fetchall()]
1550
+ result = [dict(zip(columns, row)) for row in rows]
1551
+ print(f" 📋 Получено {len(result)} записей из '{table_name}'")
1552
+ return result
1553
+
1554
+ def find(self, table_name, column, value):
1555
+ """
1556
+ 🔍 ПОИСК ПО ТОЧНОМУ ЗНАЧЕНИЮ: Ищет записи, где column = value.
1557
+
1558
+ Параметры:
1559
+ table_name (str): имя таблицы
1560
+ column (str): имя колонки для поиска
1561
+ value (str): точное значение для поиска
1562
+
1563
+ Возвращает:
1564
+ list[dict]: список найденных записей
1565
+
1566
+ 📝 Пример:
1567
+ adults = db.find("users", "age", "18")
1568
+ for adult in adults:
1569
+ print(adult["name"])
1570
+
1571
+ 🎯 Возможности:
1572
+ - Фильтрация по точному значению
1573
+ - Поиск пользователей по email, имени и т.д.
1574
+ """
1575
+ query = f'SELECT * FROM {table_name} WHERE {column} = ?'
1576
+ self.cursor.execute(query, (value,))
1577
+ rows = self.cursor.fetchall()
1578
+ self.cursor.execute(f'PRAGMA table_info({table_name})')
1579
+ columns = [col[1] for col in self.cursor.fetchall()]
1580
+ result = [dict(zip(columns, row)) for row in rows]
1581
+ print(f" 🔍 Найдено {len(result)} записей")
1582
+ return result
1583
+
1584
+ def search(self, table_name, column, text):
1585
+ """
1586
+ 🔎 ПОИСК ПО ЧАСТИ ТЕКСТА: Ищет записи, где column содержит text.
1587
+
1588
+ Параметры:
1589
+ table_name (str): имя таблицы
1590
+ column (str): имя колонки для поиска
1591
+ text (str): текст для поиска (регистр важен!)
1592
+
1593
+ Возвращает:
1594
+ list[dict]: список найденных записей
1595
+
1596
+ 📝 Пример:
1597
+ results = db.search("users", "name", "юс")
1598
+ # Найдёт: Юсуф, Люся и т.д.
1599
+
1600
+ 🎯 Возможности:
1601
+ - Нечёткий поиск по подстроке
1602
+ - Автодополнение и подсказки
1603
+ """
1604
+ query = f'SELECT * FROM {table_name} WHERE {column} LIKE ?'
1605
+ self.cursor.execute(query, (f'%{text}%',))
1606
+ rows = self.cursor.fetchall()
1607
+ self.cursor.execute(f'PRAGMA table_info({table_name})')
1608
+ columns = [col[1] for col in self.cursor.fetchall()]
1609
+ result = [dict(zip(columns, row)) for row in rows]
1610
+ print(f" 🔎 Найдено {len(result)} записей")
1611
+ return result
1612
+
1613
+ def update(self, table_name, id, data):
1614
+ """
1615
+ ✏️ ОБНОВИТЬ ЗАПИСЬ: Обновляет данные в строке по ID.
1616
+
1617
+ Параметры:
1618
+ table_name (str): имя таблицы
1619
+ id (int): ID записи для обновления
1620
+ data (dict): словарь {колонка: новое_значение}
1621
+
1622
+ Возвращает:
1623
+ bool: True если обновлено, False если запись не найдена
1624
+
1625
+ 📝 Пример:
1626
+ db.update("users", 1, {"age": "13", "name": "Юсуф"})
1627
+ # Обновит возраст и имя у пользователя с ID=1
1628
+
1629
+ 🎯 Возможности:
1630
+ - Частичное обновление (только указанные поля)
1631
+ - Массовое обновление при необходимости
1632
+ """
1633
+ set_clause = ', '.join([f'{col} = ?' for col in data])
1634
+ values = tuple(data.values()) + (id,)
1635
+ query = f'UPDATE {table_name} SET {set_clause} WHERE id = ?'
1636
+ self.cursor.execute(query, values)
1637
+ self.conn.commit()
1638
+ if self.cursor.rowcount:
1639
+ print(f" ✏️ Обновлена запись ID={id}")
1640
+ return True
1641
+ print(f" ❌ Запись ID={id} не найдена")
1642
+ return False
1643
+
1644
+ def delete(self, table_name, id):
1645
+ """
1646
+ 🗑️ УДАЛИТЬ ПО ID: Удаляет запись с указанным ID.
1647
+
1648
+ Параметры:
1649
+ table_name (str): имя таблицы
1650
+ id (int): ID записи для удаления
1651
+
1652
+ Возвращает:
1653
+ bool: True если удалено, False если запись не найдена
1654
+
1655
+ 📝 Пример:
1656
+ if db.delete("users", 5):
1657
+ print("Пользователь удалён")
1658
+ else:
1659
+ print("Пользователь не найден")
1660
+
1661
+ 🎯 Возможности:
1662
+ - Точечное удаление записей
1663
+ - Очистка устаревших данных
1664
+ """
1665
+ self.cursor.execute(f'DELETE FROM {table_name} WHERE id = ?', (id,))
1666
+ self.conn.commit()
1667
+ if self.cursor.rowcount:
1668
+ print(f" 🗑️ Удалена запись ID={id}")
1669
+ return True
1670
+ print(f" ❌ Запись ID={id} не найдена")
1671
+ return False
1672
+
1673
+ def clear_table(self, table_name):
1674
+ """
1675
+ 🧹 ОЧИСТИТЬ ТАБЛИЦУ: Удаляет все записи, но структура таблицы остаётся.
1676
+
1677
+ Параметры:
1678
+ table_name (str): имя таблицы
1679
+
1680
+ 📝 Пример:
1681
+ db.clear_table("temp_logs")
1682
+ print("Временные логи очищены")
1683
+
1684
+ 🎯 Возможности:
1685
+ - Быстрая очистка всех данных
1686
+ - Сохранение структуры таблицы для дальнейшего использования
1687
+ """
1688
+ self.cursor.execute(f'DELETE FROM {table_name}')
1689
+ self.conn.commit()
1690
+ print(f" 🧹 Таблица '{table_name}' очищена")
1691
+
1692
+ def drop_table(self, table_name):
1693
+ """
1694
+ 💥 УДАЛИТЬ ТАБЛИЦУ: Полностью удаляет таблицу со всеми данными и структурой.
1695
+
1696
+ Параметры:
1697
+ table_name (str): имя таблицы для удаления
1698
+
1699
+ 📝 Пример:
1700
+ db.drop_table("old_backups")
1701
+ # Таблица old_backups полностью удалена
1702
+
1703
+ 🎯 Возможности:
1704
+ - Удаление устаревших таблиц
1705
+ - Очистка схемы базы данных
1706
+ """
1707
+ self.cursor.execute(f'DROP TABLE IF EXISTS {table_name}')
1708
+ self.conn.commit()
1709
+ print(f" 💥 Таблица '{table_name}' удалена")
1710
+
1711
+ def stats(self, table_name):
1712
+ """
1713
+ 📊 СТАТИСТИКА: Показывает количество записей и список колонок таблицы.
1714
+
1715
+ Параметры:
1716
+ table_name (str): имя таблицы
1717
+
1718
+ Возвращает:
1719
+ dict: словарь с ключами "total" (количество записей) и "columns" (список колонок)
1720
+
1721
+ 📝 Пример:
1722
+ stats = db.stats("users")
1723
+ print(f"Всего пользователей: {stats['total']}")
1724
+ print(f"Поля: {', '.join(stats['columns'])}")
1725
+
1726
+ 🎯 Возможности:
1727
+ - Мониторинг размера таблиц
1728
+ - Проверка структуры данных
1729
+ """
1730
+ self.cursor.execute(f'SELECT COUNT(*) FROM {table_name}')
1731
+ total = self.cursor.fetchone()[0]
1732
+ self.cursor.execute(f'PRAGMA table_info({table_name})')
1733
+ columns = [col[1] for col in self.cursor.fetchall()]
1734
+ print(f"\n📊 Статистика '{table_name}':")
1735
+ print(f" Записей: {total}")
1736
+ print(f" Колонки: {', '.join(columns)}")
1737
+ return {"total": total, "columns": columns}
1738
+
1739
+ def show_tables(self):
1740
+ """
1741
+ 📁 ПОКАЗАТЬ ТАБЛИЦЫ: Выводит список всех таблиц в базе данных.
1742
+
1743
+ 📝 Пример:
1744
+ db.show_tables()
1745
+ # Выведет:
1746
+ # 📋 Таблицы в базе:
1747
+ # 📁 users
1748
+ # 📁 products
1749
+ # 📁 orders
1750
+
1751
+ 🎯 Возможности:
1752
+ - Просмотр структуры базы данных
1753
+ - Проверка наличия нужных таблиц
1754
+ """
1755
+ self.cursor.execute("SELECT name FROM sqlite_master WHERE type='table'")
1756
+ tables = self.cursor.fetchall()
1757
+ print("\n📋 Таблицы в базе:")
1758
+ for t in tables:
1759
+ print(f" 📁 {t[0]}")
1760
+
1761
+ def close(self):
1762
+ """
1763
+ 🔌 ЗАКРЫТЬ СОЕДИНЕНИЕ: Обязательно вызывать в конце работы с базой.
1764
+
1765
+ 📝 Пример:
1766
+ db.close()
1767
+ print("Работа с базой завершена")
1768
+
1769
+ 🎯 Возможности:
1770
+ - Корректное завершение работы
1771
+ - Сохранение всех изменений
1772
+ - Освобождение системных ресурсов
1773
+ """
1774
+ self.conn.close()
1775
+ print("🔌 Соединение закрыто")
1776
+
1777
+
1778
+ # =============================================================================
1779
+ # КЛАСС Music – МУЗЫКАЛЬНЫЙ ПЛЕЕР С ЯНДЕКС МУЗЫКОЙ
1780
+ # =============================================================================
1781
+ class Music:
1782
+ """
1783
+ 🎵 МУЗЫКАЛЬНЫЙ ПЛЕЕР — управление Яндекс.Музыкой через VLC.
1784
+
1785
+ 📝 Пример использования:
1786
+ from help_manager import Music
1787
+
1788
+ player = Music() # Создать плеер
1789
+ player.play("Imagine Dragons") # Включить трек
1790
+ player.pause() # Пауза / продолжить
1791
+ player.volume(80) # Установить громкость
1792
+ player.add_to_queue("Eminem - Mockingbird") # Добавить в очередь
1793
+ player.next() # Следующий трек
1794
+ player.play_my_wave() # Запустить Мою волну
1795
+ player.search_playlist("рок") # Найти и включить плейлист
1796
+ player.stop() # Остановить
1797
+
1798
+ 🎯 Возможности класса:
1799
+ - Воспроизведение треков по названию
1800
+ - Управление очередью (добавление, переключение, очистка)
1801
+ - Пауза / продолжение / остановка
1802
+ - Регулировка громкости (0-100)
1803
+ - Режим «Моя волна» (любимые треки/рекомендации)
1804
+ - Поиск и воспроизведение плейлистов по названию
1805
+ - Кэширование ссылок на треки (ускорение повторного поиска)
1806
+ - Автоматическое переключение треков в очереди
1807
+ """
1808
+
1809
+ def __init__(self, token=None):
1810
+ """
1811
+ 🎧 ИНИЦИАЛИЗАЦИЯ ПЛЕЕРА: Подключается к Яндекс.Музыке.
1812
+
1813
+ Параметры:
1814
+ token (str, optional): токен Яндекс.Музыки. Если не указан, используется встроенный.
1815
+
1816
+ 📝 Пример:
1817
+ player = Music() # с встроенным токеном
1818
+ player = Music(token="y0_AgAAAA...") # со своим токеном
1819
+
1820
+ 🎯 Возможности:
1821
+ - Автоматическая загрузка кэша треков
1822
+ - Готовность к воспроизведению сразу после создания
1823
+ """
1824
+ from yandex_music import Client
1825
+
1826
+ self.token = token or "y0__xDMkd7CBhje-AYg2Zr6-xYw3peC3QeDw6D9crPyCF1q5ruJOitnYr6esA"
1827
+ if not self.token:
1828
+ raise ValueError("❌ Токен не найден! Укажи token или создай .env файл")
1829
+
1830
+ self.client = Client(self.token).init()
1831
+ self.player = None
1832
+ self.current_track = None
1833
+ self._is_playing = False
1834
+ self.paused = False
1835
+ self.volume_level = 50
1836
+ self.queue = [] # Очередь треков: [{'name': ..., 'url': ...}, ...]
1837
+ self.queue_index = 0 # Индекс текущего трека
1838
+ self._cache = {} # Кэш ссылок
1839
+ import vlc
1840
+ self.vlc_instance = vlc.Instance('--quiet')
1841
+ self._cache_file = "track_cache.json"
1842
+ self._load_cache()
1843
+
1844
+ print("✅ Музыкальный плеер готов")
1845
+
1846
+ # ========== Внутренние методы (не вызываются напрямую) ==========
1847
+
1848
+ def _load_cache(self):
1849
+ """
1850
+ 📦 ЗАГРУЗКА КЭША: Загружает кэшированные ссылки на треки из файла.
1851
+ Удаляет записи старше 3 часов.
1852
+
1853
+ (Вызывается автоматически при создании плеера)
1854
+ """
1855
+ if os.path.exists(self._cache_file):
1856
+ try:
1857
+ with open(self._cache_file, 'r', encoding='utf-8') as f:
1858
+ raw_cache = json.load(f)
1859
+ now = time.time()
1860
+ self._cache = {}
1861
+ for query, data in raw_cache.items():
1862
+ if isinstance(data, dict) and now - data.get('timestamp', 0) < 10800: # 3 часа
1863
+ self._cache[query] = data['url']
1864
+ print(f"📦 Загружен кэш: {len(self._cache)} треков")
1865
+ except:
1866
+ self._cache = {}
1867
+ else:
1868
+ self._cache = {}
1869
+
1870
+ def _save_cache(self):
1871
+ """
1872
+ 💾 СОХРАНЕНИЕ КЭША: Сохраняет кэшированные ссылки в JSON-файл.
1873
+
1874
+ (Вызывается автоматически при добавлении новых треков в кэш)
1875
+ """
1876
+ try:
1877
+ data_to_save = {}
1878
+ now = time.time()
1879
+ for query, url in self._cache.items():
1880
+ data_to_save[query] = {'url': url, 'timestamp': now}
1881
+ with open(self._cache_file, 'w', encoding='utf-8') as f:
1882
+ json.dump(data_to_save, f, ensure_ascii=False, indent=4)
1883
+ except Exception as e:
1884
+ print(f"⚠️ Не удалось сохранить кэш: {e}")
1885
+
1886
+ def _get_track_url(self, query):
1887
+ """
1888
+ 🔗 ПОЛУЧЕНИЕ ССЫЛКИ: Получает прямую ссылку на трек (из кэша или через API Яндекса).
1889
+
1890
+ Параметры:
1891
+ query (str): название трека (например, "Morgenshtern Cadillac")
1892
+
1893
+ Возвращает:
1894
+ str или None: URL трека или None, если трек не найден
1895
+
1896
+ (Внутренний метод)
1897
+ """
1898
+ if query in self._cache:
1899
+ return self._cache[query]
1900
+
1901
+ search = self.client.search(query)
1902
+ if not search.tracks or not search.tracks.results:
1903
+ return None
1904
+
1905
+ track = search.tracks.results[0]
1906
+ full_track = self.client.tracks([track.id])[0]
1907
+
1908
+ url = None
1909
+ try:
1910
+ info = full_track.get_download_info()
1911
+ if info:
1912
+ url = info[0].get_direct_link()
1913
+ except:
1914
+ pass
1915
+ if not url:
1916
+ try:
1917
+ url = full_track.download_url
1918
+ except:
1919
+ pass
1920
+
1921
+ if url:
1922
+ self._cache[query] = url
1923
+ self._save_cache()
1924
+ return url
1925
+
1926
+ def _play_url(self, url, track_name):
1927
+ """
1928
+ ▶️ ЗАПУСК ТРЕКА: Запускает воспроизведение трека в VLC.
1929
+
1930
+ Параметры:
1931
+ url (str): прямая ссылка на трек
1932
+ track_name (str): название трека для отображения
1933
+
1934
+ (Внутренний метод)
1935
+ """
1936
+ if self.player:
1937
+ self.player.stop()
1938
+
1939
+ self.player = self.vlc_instance.media_player_new()
1940
+ media = self.vlc_instance.media_new(url)
1941
+ self.player.set_media(media)
1942
+ self.player.play()
1943
+ self.player.audio_set_volume(self.volume_level)
1944
+ self._is_playing = True
1945
+ self.paused = False
1946
+ self.current_track = track_name
1947
+ print(f"▶️ Сейчас играет: {track_name}")
1948
+
1949
+ def _wait_for_end(self):
1950
+ """
1951
+ ⏳ ОЖИДАНИЕ ОКОНЧАНИЯ: Ждёт, пока текущий трек доиграет до конца.
1952
+
1953
+ (Внутренний метод, используется когда wait=True)
1954
+ """
1955
+ import vlc
1956
+ if not self.player:
1957
+ return
1958
+ for _ in range(50):
1959
+ if self.player.get_state() == vlc.State.Playing:
1960
+ break
1961
+ time.sleep(0.1)
1962
+ while True:
1963
+ state = self.player.get_state()
1964
+ if state in (vlc.State.Ended, vlc.State.Stopped, vlc.State.Error):
1965
+ break
1966
+ time.sleep(0.3)
1967
+ time.sleep(0.2)
1968
+
1969
+ # ========== Базовое управление ==========
1970
+
1971
+ def play(self, query, wait=True):
1972
+ """
1973
+ ▶️ ВКЛЮЧИТЬ ТРЕК: Ищет трек по названию и запускает воспроизведение.
1974
+ Очищает предыдущую очередь.
1975
+
1976
+ Параметры:
1977
+ query (str): название трека (например, "Miyagi Captain")
1978
+ wait (bool): ждать ли окончания трека (по умолчанию True)
1979
+
1980
+ Возвращает:
1981
+ bool: True, если трек найден и запущен, иначе False
1982
+
1983
+ 📝 Пример:
1984
+ player.play("Basta - Sansara") # запустить и ждать
1985
+ player.play("Imagine Dragons", wait=False) # запустить и не ждать
1986
+
1987
+ 🎯 Возможности:
1988
+ - Автоматический поиск трека на Яндекс.Музыке
1989
+ - Кэширование ссылок для быстрого повтора
1990
+ """
1991
+ url = self._get_track_url(query)
1992
+ if not url:
1993
+ print(f"❌ Не найден: {query}")
1994
+ return False
1995
+
1996
+ self.queue = [{'name': query, 'url': url}]
1997
+ self.queue_index = 0
1998
+ self._play_url(url, query)
1999
+ if wait:
2000
+ self._wait_for_end()
2001
+ return True
2002
+
2003
+ def pause(self):
2004
+ """
2005
+ ⏸️ ПАУЗА / ПРОДОЛЖИТЬ: Ставит трек на паузу или продолжает воспроизведение.
2006
+
2007
+ 📝 Пример:
2008
+ player.pause() # если играл — пауза, если на паузе — продолжит
2009
+
2010
+ 🎯 Возможности:
2011
+ - Удобное управление без запоминания состояния
2012
+ """
2013
+ if self.player:
2014
+ if self._is_playing:
2015
+ self.player.pause()
2016
+ self._is_playing = False
2017
+ self.paused = True
2018
+ print("⏸️ Пауза")
2019
+ else:
2020
+ self.player.play()
2021
+ self._is_playing = True
2022
+ self.paused = False
2023
+ print("▶️ Продолжение")
2024
+
2025
+ def stop(self):
2026
+ """
2027
+ ⏹️ ОСТАНОВИТЬ: Полностью останавливает воспроизведение.
2028
+
2029
+ 📝 Пример:
2030
+ player.stop()
2031
+
2032
+ 🎯 Возможности:
2033
+ - Полная остановка плеера
2034
+ - Сброс текущего трека
2035
+ """
2036
+ if self.player:
2037
+ self.player.stop()
2038
+ self._is_playing = False
2039
+ self.paused = False
2040
+ self.current_track = None
2041
+ print("⏹️ Остановлено")
2042
+
2043
+ def next(self, wait=True):
2044
+ """
2045
+ ⏭️ СЛЕДУЮЩИЙ ТРЕК: Переключает на следующий трек в очереди.
2046
+
2047
+ Параметры:
2048
+ wait (bool): ждать ли окончания трека (по умолчанию True)
2049
+
2050
+ Возвращает:
2051
+ bool: True, если переключение выполнено, иначе False
2052
+
2053
+ 📝 Пример:
2054
+ player.next() # следующий и ждать
2055
+ player.next(wait=False) # следующий без ожидания
2056
+
2057
+ 🎯 Возможности:
2058
+ - Автоматическое переключение плейлистов
2059
+ """
2060
+ if not self.queue or self.queue_index + 1 >= len(self.queue):
2061
+ self.stop()
2062
+ print("⏹️ Это был последний трек в очереди")
2063
+ return False
2064
+ self.queue_index += 1
2065
+ track = self.queue[self.queue_index]
2066
+ self._play_url(track['url'], track['name'])
2067
+ if wait:
2068
+ self._wait_for_end()
2069
+ return True
2070
+
2071
+ def prev(self, wait=True):
2072
+ """
2073
+ ⏮️ ПРЕДЫДУЩИЙ ТРЕК: Переключает на предыдущий трек в очереди.
2074
+
2075
+ Параметры:
2076
+ wait (bool): ждать ли окончания трека (по умолчанию True)
2077
+
2078
+ Возвращает:
2079
+ bool: True, если переключение выполнено, иначе False
2080
+
2081
+ 📝 Пример:
2082
+ player.prev()
2083
+ player.prev(wait=False)
2084
+ """
2085
+ if not self.queue or self.queue_index <= 0:
2086
+ print("⏮️ Это первый трек в очереди")
2087
+ return False
2088
+ self.queue_index -= 1
2089
+ track = self.queue[self.queue_index]
2090
+ self._play_url(track['url'], track['name'])
2091
+ if wait:
2092
+ self._wait_for_end()
2093
+ return True
2094
+
2095
+ def volume(self, level):
2096
+ """
2097
+ 🔊 ГРОМКОСТЬ: Устанавливает громкость воспроизведения.
2098
+
2099
+ Параметры:
2100
+ level (int): число от 0 (без звука) до 100 (максимум)
2101
+
2102
+ 📝 Пример:
2103
+ player.volume(80) # громкость 80%
2104
+ player.volume(0) # без звука
2105
+
2106
+ 🎯 Возможности:
2107
+ - Плавная регулировка громкости
2108
+ - Автоматическое ограничение диапазона 0-100
2109
+ """
2110
+ self.volume_level = max(0, min(100, level))
2111
+ if self.player:
2112
+ self.player.audio_set_volume(self.volume_level)
2113
+ print(f"🔊 Громкость: {self.volume_level}%")
2114
+
2115
+ def volume_up(self, step=10):
2116
+ """
2117
+ 🔊➕ ГРОМЧЕ: Увеличивает громкость на указанный шаг.
2118
+
2119
+ Параметры:
2120
+ step (int): на сколько увеличить (по умолчанию 10)
2121
+
2122
+ 📝 Пример:
2123
+ player.volume_up() # +10%
2124
+ player.volume_up(20) # +20%
2125
+ """
2126
+ self.volume(self.volume_level + step)
2127
+
2128
+ def volume_down(self, step=10):
2129
+ """
2130
+ 🔊➖ ТИШЕ: Уменьшает громкость на указанный шаг.
2131
+
2132
+ Параметры:
2133
+ step (int): на сколько уменьшить (по умолчанию 10)
2134
+
2135
+ 📝 Пример:
2136
+ player.volume_down() # -10%
2137
+ player.volume_down(5) # -5%
2138
+ """
2139
+ self.volume(self.volume_level - step)
2140
+
2141
+ # ========== Очередь ==========
2142
+
2143
+ def add_to_queue(self, query):
2144
+ """
2145
+ ➕ ДОБАВИТЬ В ОЧЕРЕДЬ: Добавляет трек в конец очереди (не прерывая текущий).
2146
+
2147
+ Параметры:
2148
+ query (str): название трека
2149
+
2150
+ Возвращает:
2151
+ bool: True, если трек найден и добавлен, иначе False
2152
+
2153
+ 📝 Пример:
2154
+ player.add_to_queue("Eminem - Lose Yourself")
2155
+ player.add_to_queue("Miyagi - Captain")
2156
+
2157
+ 🎯 Возможности:
2158
+ - Составление плейлиста на лету
2159
+ - Не прерывает текущее воспроизведение
2160
+ """
2161
+ url = self._get_track_url(query)
2162
+ if not url:
2163
+ print(f"❌ Не найден: {query}")
2164
+ return False
2165
+ self.queue.append({'name': query, 'url': url})
2166
+ print(f"➕ В очередь добавлен: {query}")
2167
+ return True
2168
+
2169
+ def show_queue(self):
2170
+ """
2171
+ 📋 ПОКАЗАТЬ ОЧЕРЕДЬ: Выводит текущую очередь треков.
2172
+
2173
+ 📝 Пример:
2174
+ player.show_queue()
2175
+ # Выведет:
2176
+ # 📋 ОЧЕРЕДЬ (3 треков):
2177
+ # ▶️ 1. Miyagi - Captain
2178
+ # 2. Basta - Sansara
2179
+ # 3. Eminem - Lose Yourself
2180
+
2181
+ 🎯 Возможности:
2182
+ - Просмотр плейлиста
2183
+ - Контроль порядка треков
2184
+ """
2185
+ if not self.queue:
2186
+ print("📭 Очередь пуста")
2187
+ return
2188
+ print(f"\n📋 ОЧЕРЕДЬ ({len(self.queue)} треков):")
2189
+ for i, track in enumerate(self.queue):
2190
+ mark = "▶️" if i == self.queue_index else " "
2191
+ print(f" {mark} {i+1}. {track['name']}")
2192
+
2193
+ def clear_queue(self):
2194
+ """
2195
+ 🧹 ОЧИСТИТЬ ОЧЕРЕДЬ: Удаляет все треки из очереди.
2196
+
2197
+ 📝 Пример:
2198
+ player.clear_queue()
2199
+ player.show_queue() # 📭 Очередь пуста
2200
+
2201
+ 🎯 Возможности:
2202
+ - Быстрая очистка плейлиста
2203
+ - Подготовка к новому набору треков
2204
+ """
2205
+ self.queue = []
2206
+ self.queue_index = 0
2207
+ print("🧹 Очередь очищена")
2208
+
2209
+ # ========== Моя волна и плейлисты ==========
2210
+
2211
+ def play_my_wave(self, limit=10, wait=True):
2212
+ """
2213
+ 🌊 НАСТОЯЩАЯ МОЯ ВОЛНА: Запускает бесконечный поток рекомендаций Яндекс.Музыки.
2214
+
2215
+ Использует station (radio) — те же алгоритмы, что и в приложении Яндекс.Музыки.
2216
+ Это НЕ просто лайкнутые треки, а уникальные рекомендации на основе твоих вкусов.
2217
+
2218
+ Параметры:
2219
+ limit (int): сколько треков загрузить (по умолчанию 10)
2220
+ wait (bool): ждать ли окончания первого трека
2221
+
2222
+ Возвращает:
2223
+ bool: True при успехе, иначе False
2224
+
2225
+ 📝 Пример:
2226
+ player.play_my_wave() # 10 треков
2227
+ player.play_my_wave(limit=5, wait=False) # 5 треков, не ждать
2228
+
2229
+ 🎯 Возможности:
2230
+ - Уникальные рекомендации как в приложении Яндекс.Музыки
2231
+ - Каждый запуск — новые треки
2232
+ - Учитывает твои вкусы и историю прослушивания
2233
+ """
2234
+ try:
2235
+ print("🎵 Загружаю «Мою волну» (рекомендации)...")
2236
+
2237
+ # Получаем станцию "Моя волна" — это настоящие рекомендации
2238
+ station = self.client.rotor_station_info_dashboard()
2239
+
2240
+ if not station or not station.stations:
2241
+ # Если не получилось — fallback на лайки
2242
+ print("⚠️ Станция недоступна, использую лайкнутые треки")
2243
+ return self._play_liked_tracks(limit, wait)
2244
+
2245
+ # Берём первую станцию (обычно это "Моя волна")
2246
+ my_wave = station.stations[0]
2247
+
2248
+ # Получаем треки из станции
2249
+ queue = self.client.rotor_station_tracks(my_wave.station)
2250
+
2251
+ if not queue or not hasattr(queue, 'sequence'):
2252
+ print("⚠️ Не удалось загрузить рекомендации, использую лайки")
2253
+ return self._play_liked_tracks(limit, wait)
2254
+
2255
+ self.queue = []
2256
+ for track_item in list(queue.sequence)[:limit]:
2257
+ try:
2258
+ track = track_item.track
2259
+ name = f"{track.title} — {track.artists[0].name}"
2260
+ url = self._get_track_url(name)
2261
+ if url:
2262
+ self.queue.append({'name': name, 'url': url})
2263
+ except:
2264
+ continue
2265
+
2266
+ if not self.queue:
2267
+ print("⚠️ Не удалось загрузить ссылки, использую лайки")
2268
+ return self._play_liked_tracks(limit, wait)
2269
+
2270
+ self.queue_index = 0
2271
+ self._play_url(self.queue[0]['url'], self.queue[0]['name'])
2272
+ print(f"✅ Загружено {len(self.queue)} треков из «Моей волны»")
2273
+ if wait:
2274
+ self._wait_for_end()
2275
+ return True
2276
+
2277
+ except Exception as e:
2278
+ print(f"⚠️ Ошибка загрузки рекомендаций: {e}")
2279
+ print("🔄 Переключаюсь на лайкнутые треки...")
2280
+ return self.play_liked_tracks(limit, wait)
2281
+
2282
+ def play_liked_tracks(self, limit=10, wait=True):
2283
+ """
2284
+ ❤️ ЗАПАСНОЙ ВАРИАНТ: Играет лайкнутые треки, если рекомендации недоступны.
2285
+ (Внутренний метод, вызывается автоматически)
2286
+ """
2287
+ try:
2288
+ print("🎵 Загружаю лайкнутые треки...")
2289
+ likes = self.client.users_likes_tracks()
2290
+ if not likes:
2291
+ print("❌ Нет лайкнутых треков.")
2292
+ return False
2293
+
2294
+ self.queue = []
2295
+ for like in likes[:limit]:
2296
+ track = like.fetch_track()
2297
+ name = f"{track.title} — {track.artists[0].name}"
2298
+ url = self._get_track_url(name)
2299
+ if url:
2300
+ self.queue.append({'name': name, 'url': url})
2301
+
2302
+ if not self.queue:
2303
+ print("❌ Не удалось загрузить треки")
2304
+ return False
2305
+
2306
+ self.queue_index = 0
2307
+ self._play_url(self.queue[0]['url'], self.queue[0]['name'])
2308
+ print(f"✅ Загружено {len(self.queue)} треков из лайков")
2309
+ if wait:
2310
+ self._wait_for_end()
2311
+ return True
2312
+ except Exception as e:
2313
+ print(f"❌ Ошибка: {e}")
2314
+ return False
2315
+
2316
+ def search_playlist(self, query, limit=10, wait=True):
2317
+ """
2318
+ 🔍 НАЙТИ ПЛЕЙЛИСТ: Находит плейлист по названию и включает его.
2319
+
2320
+ Параметры:
2321
+ query (str): название плейлиста (например, "рок музыка", "поп 2024")
2322
+ limit (int): количество загружаемых треков
2323
+ wait (bool): ждать ли окончания первого трека
2324
+
2325
+ Возвращает:
2326
+ bool: True при успехе, иначе False
2327
+
2328
+ 📝 Пример:
2329
+ player.search_playlist("поп музыка")
2330
+ player.search_playlist("рок", limit=20, wait=False)
2331
+
2332
+ 🎯 Возможности:
2333
+ - Поиск любого публичного плейлиста
2334
+ - Быстрое переключение настроения
2335
+ """
2336
+ try:
2337
+ print(f"🔍 Ищу плейлист: {query}")
2338
+ search = self.client.search(query)
2339
+ if not search.playlists or not search.playlists.results:
2340
+ print("❌ Плейлист не найден")
2341
+ return False
2342
+
2343
+ playlist = search.playlists.results[0]
2344
+ print(f"✅ Найден плейлист: {playlist.title}")
2345
+
2346
+ self.queue = []
2347
+
2348
+ # Пробуем получить треки через fetch_tracks (более надёжный способ)
2349
+ try:
2350
+ # У плейлиста может быть разная структура — пробуем оба варианта
2351
+ if hasattr(playlist, 'fetch_tracks'):
2352
+ tracks_data = playlist.fetch_tracks()
2353
+ elif hasattr(playlist, 'tracks'):
2354
+ tracks_data = playlist.tracks
2355
+ else:
2356
+ print("❌ Не удалось получить треки (неподдерживаемый формат плейлиста)")
2357
+ return False
2358
+
2359
+ # Ограничиваем количество
2360
+ track_list = list(tracks_data)[:limit]
2361
+
2362
+ for item in track_list:
2363
+ try:
2364
+ # Структура трека может быть разной
2365
+ if hasattr(item, 'track') and item.track:
2366
+ track_info = item.track
2367
+ elif hasattr(item, 'fetch_track'):
2368
+ track_info = item.fetch_track()
2369
+ else:
2370
+ continue
2371
+
2372
+ # Безопасно получаем название и исполнителя
2373
+ title = getattr(track_info, 'title', None)
2374
+ artists = getattr(track_info, 'artists', None)
2375
+
2376
+ if title and artists:
2377
+ artist_name = artists[0].name if artists else "Неизвестен"
2378
+ name = f"{title} — {artist_name}"
2379
+ elif title:
2380
+ name = title
2381
+ else:
2382
+ continue
2383
+
2384
+ url = self._get_track_url(name)
2385
+ if url:
2386
+ self.queue.append({'name': name, 'url': url})
2387
+ except:
2388
+ continue
2389
+
2390
+ except Exception as e:
2391
+ print(f"⚠️ Ошибка при загрузке треков: {e}")
2392
+ return False
2393
+
2394
+ if not self.queue:
2395
+ print("❌ Не удалось загрузить треки из плейлиста")
2396
+ return False
2397
+
2398
+ self.queue_index = 0
2399
+ self._play_url(self.queue[0]['url'], self.queue[0]['name'])
2400
+ print(f"✅ Загружено {len(self.queue)} треков из «{playlist.title}»")
2401
+ if wait:
2402
+ self._wait_for_end()
2403
+ return True
2404
+
2405
+ except Exception as e:
2406
+ print(f"❌ Ошибка: {e}")
2407
+ return False
2408
+
2409
+ def mood(self, mood_name, limit=5, wait=True):
2410
+ """
2411
+ 🎭 МУЗЫКА ПО НАСТРОЕНИЮ: Включает плейлист, соответствующий настроению.
2412
+
2413
+ Сначала пробует найти плейлист с настроением, если не получается —
2414
+ включает лайкнутые треки (Мою волну).
2415
+
2416
+ Параметры:
2417
+ mood_name (str): настроение ('happy', 'sad', 'energy', 'calm')
2418
+ limit (int): количество треков
2419
+ wait (bool): ждать ли окончания первого трека
2420
+
2421
+ Возвращает:
2422
+ bool: True при успехе, иначе False
2423
+
2424
+ 📝 Пример:
2425
+ player.mood("happy") # весёлая музыка
2426
+ player.mood("calm", limit=3) # спокойная, 3 трека
2427
+
2428
+ 🎯 Возможности:
2429
+ - Быстрый подбор музыки под настроение
2430
+ - Если плейлист не найден — включает Мою волну
2431
+ """
2432
+ print(f"🎭 Включаю музыку под настроение: {mood_name}")
2433
+
2434
+ # Сначала пробуем найти плейлист
2435
+ result = self.search_playlist(mood_name, limit, wait)
2436
+
2437
+ # Если не получилось — включаем Мою волну как fallback
2438
+ if not result:
2439
+ print(f"⚠️ Плейлист '{mood_name}' не загрузился, включаю «Мою волну»")
2440
+ result = self.play_my_wave(limit, wait)
2441
+
2442
+ return result
2443
+
2444
+ # ========== Информационные методы ==========
2445
+
2446
+ def is_now_playing(self):
2447
+ """
2448
+ ▶️ ИГРАЕТ СЕЙЧАС?: Проверяет, воспроизводится ли трек в данный момент.
2449
+
2450
+ Возвращает:
2451
+ bool: True если трек играет, False если пауза или остановлен
2452
+
2453
+ 📝 Пример:
2454
+ if player.is_now_playing():
2455
+ print("Сейчас играет:", player.get_current_track())
2456
+
2457
+ 🎯 Возможности:
2458
+ - Проверка состояния плеера
2459
+ - Условное выполнение действий
2460
+ """
2461
+ return self._is_playing
2462
+
2463
+ def get_current_track(self):
2464
+ """
2465
+ 🎵 ТЕКУЩИЙ ТРЕК: Возвращает название текущего трека.
2466
+
2467
+ Возвращает:
2468
+ str или None: название трека или None, если ничего не играет
2469
+
2470
+ 📝 Пример:
2471
+ track = player.get_current_track()
2472
+ if track:
2473
+ print(f"Сейчас играет: {track}")
2474
+
2475
+ 🎯 Возможности:
2476
+ - Отображение текущего трека в интерфейсе
2477
+ - Логирование прослушанной музыки
2478
+ """
2479
+ return self.current_track
2480
+
2481
+ def get_volume(self):
2482
+ """
2483
+ 🔊 ТЕКУЩАЯ ГРОМКОСТЬ: Возвращает текущий уровень громкости.
2484
+
2485
+ Возвращает:
2486
+ int: число от 0 до 100
2487
+
2488
+ 📝 Пример:
2489
+ vol = player.get_volume()
2490
+ print(f"Громкость: {vol}%")
2491
+
2492
+ 🎯 Возможности:
2493
+ - Сохранение предпочтений громкости
2494
+ - Отображение в интерфейсе
2495
+ """
2496
+ return self.volume_level
2497
+
2498
+ def get_queue_length(self):
2499
+ """
2500
+ 📏 ДЛИНА ОЧЕРЕДИ: Возвращает количество треков в очереди.
2501
+
2502
+ Возвращает:
2503
+ int: количество треков
2504
+
2505
+ 📝 Пример:
2506
+ count = player.get_queue_length()
2507
+ print(f"В очереди {count} треков")
2508
+
2509
+ 🎯 Возможности:
2510
+ - Информирование о размере плейлиста
2511
+ - Проверка, есть ли ещё треки
2512
+ """
2513
+ return len(self.queue)
2514
+
2515
+
2516
+ # =============================================================================
2517
+ # КЛАСС Face – РАСПОЗНАВАНИЕ ЛИЦ И ЭМОЦИЙ
2518
+ # =============================================================================
2519
+ class Face:
2520
+ """
2521
+ 🧠 РАСПОЗНАВАНИЕ ЛИЦ И ЭМОЦИЙ — анализ через веб-камеру.
2522
+
2523
+ 📝 Пример использования:
2524
+ emotion = Face.detect_emotion()
2525
+ if emotion == 'happy':
2526
+ print("Ты счастлив!")
2527
+ is_smiling = Face.detect_smile()
2528
+ Face.music_by_mood() # включит музыку под настроение
2529
+
2530
+ 🎯 Возможности класса:
2531
+ - Определение эмоций (7 базовых эмоций)
2532
+ - Обнаружение улыбки
2533
+ - Автоматический запуск музыки по настроению
2534
+ """
2535
+
2536
+ @staticmethod
2537
+ def detect_emotion():
2538
+ """
2539
+ 🎭 ОПРЕДЕЛИТЬ ЭМОЦИЮ: Анализирует лицо через веб-камеру с помощью DeepFace.
2540
+
2541
+ Возвращает:
2542
+ str или None: одна из эмоций ('happy', 'sad', 'angry', 'surprise', 'fear', 'disgust', 'neutral')
2543
+ или None, если лицо не найдено
2544
+
2545
+ 📝 Пример:
2546
+ emotion = Face.detect_emotion()
2547
+ if emotion == 'happy':
2548
+ print("Ты сегодня в хорошем настроении!")
2549
+ elif emotion == 'sad':
2550
+ print("Может, включить музыку?")
2551
+
2552
+ 🎯 Возможности:
2553
+ - Определение настроения пользователя
2554
+ - Адаптация интерфейса под эмоции
2555
+ - Использование в играх и интерактивных приложениях
2556
+ """
2557
+ import cv2
2558
+ from deepface import DeepFace
2559
+
2560
+ cap = cv2.VideoCapture(0)
2561
+ ret, frame = cap.read()
2562
+ cap.release()
2563
+
2564
+ if not ret:
2565
+ print("❌ Не удалось получить кадр с камеры")
2566
+ return None
2567
+
2568
+ try:
2569
+ result = DeepFace.analyze(
2570
+ img_path=frame,
2571
+ actions=['emotion'],
2572
+ enforce_detection=False,
2573
+ silent=True
2574
+ )
2575
+ emotion = result[0]['dominant_emotion']
2576
+ return emotion
2577
+ except Exception as e:
2578
+ print(f"❌ Ошибка DeepFace: {e}")
2579
+ return None
2580
+
2581
+ @staticmethod
2582
+ def detect_smile():
2583
+ """
2584
+ 😁 ОБНАРУЖЕНИЕ УЛЫБКИ: Проверяет, улыбается ли человек в кадре.
2585
+
2586
+ Возвращает:
2587
+ bool: True если обнаружена улыбка, False если нет или ошибка
2588
+
2589
+ 📝 Пример:
2590
+ if Face.detect_smile():
2591
+ print("Отличная улыбка!")
2592
+ Auto.screenshot("smile_capture.png")
2593
+
2594
+ 🎯 Возможности:
2595
+ - Автоматическая фотосъёмка при улыбке
2596
+ - Игровые механики с отслеживанием улыбки
2597
+ - Интерактивные приложения
2598
+ """
2599
+ import cv2
2600
+
2601
+ face_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_frontalface_default.xml')
2602
+ smile_cascade = cv2.CascadeClassifier(cv2.data.haarcascades + 'haarcascade_smile.xml')
2603
+
2604
+ cap = cv2.VideoCapture(0)
2605
+ ret, frame = cap.read()
2606
+ cap.release()
2607
+
2608
+ if not ret:
2609
+ return False
2610
+
2611
+ gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
2612
+ faces = face_cascade.detectMultiScale(gray, 1.3, 5)
2613
+
2614
+ for (x, y, w, h) in faces:
2615
+ roi = gray[y:y+h, x:x+w]
2616
+ smiles = smile_cascade.detectMultiScale(roi, 1.8, 20)
2617
+ if len(smiles) > 0:
2618
+ return True
2619
+
2620
+ return False
2621
+
2622
+ @staticmethod
2623
+ def music_by_mood():
2624
+ """
2625
+ 🎵 МУЗЫКА ПО НАСТРОЕНИЮ: Определяет эмоцию и включает подходящий плейлист.
2626
+
2627
+ 📝 Пример:
2628
+ Face.music_by_mood()
2629
+ # Определит, что ты грустишь, и включит спокойную музыку
2630
+
2631
+ 🎯 Возможности:
2632
+ - Автоматический подбор музыки
2633
+ - Создание атмосферы под настроение
2634
+
2635
+ Таблица соответствия эмоций и плейлистов:
2636
+ - happy → happy (весёлая музыка)
2637
+ - sad → sad (грустная музыка)
2638
+ - angry → energy (энергичная музыка)
2639
+ - surprise → happy (весёлая музыка)
2640
+ - fear → calm (спокойная музыка)
2641
+ - neutral → calm (спокойная музыка)
2642
+ """
2643
+ emotion = Face.detect_emotion()
2644
+ if not emotion:
2645
+ print("Не удалось определить эмоцию")
2646
+ return
2647
+
2648
+ mood_map = {
2649
+ 'happy': 'happy',
2650
+ 'sad': 'sad',
2651
+ 'angry': 'energy',
2652
+ 'surprise': 'happy',
2653
+ 'fear': 'calm',
2654
+ 'neutral': 'calm'
2655
+ }
2656
+
2657
+ music = Music()
2658
+ music.mood(mood_map.get(emotion, 'calm'))
2659
+
2660
+
2661
+ # =============================================================================
2662
+ # КЛАСС Auto – АВТОМАТИЗАЦИЯ КЛАВИШ И МЫШИ
2663
+ # =============================================================================
2664
+ class Auto:
2665
+ """
2666
+ 🖱️ АВТОМАТИЗАЦИЯ: клики, клавиши, скриншоты, уведомления.
2667
+
2668
+ Все методы статические — вызывай напрямую:
2669
+ Auto.click()
2670
+ Auto.press('enter')
2671
+ Auto.write("Привет!")
2672
+
2673
+ 🎯 Возможности класса:
2674
+ - Эмуляция клавиатуры (нажатие, удержание, печать текста)
2675
+ - Эмуляция мыши (клики, двойные клики, перетаскивание, скроллинг)
2676
+ - Создание скриншотов
2677
+ - Системные уведомления
2678
+ """
2679
+
2680
+ # =========================== КЛАВИАТУРА ===========================
2681
+
2682
+ @staticmethod
2683
+ def key_down(key: str):
2684
+ """
2685
+ ⬇️ ЗАЖАТЬ КЛАВИШУ: Зажимает и удерживает клавишу (не отпускает, пока не вызван key_up).
2686
+
2687
+ Параметры:
2688
+ key (str): название клавиши, например 'shift', 'ctrl', 'a', 'win'
2689
+
2690
+ 📝 Пример:
2691
+ Auto.key_down('ctrl') # зажали Ctrl
2692
+ Auto.click(100, 200) # кликнули мышью с зажатым Ctrl
2693
+ Auto.key_up('ctrl') # отпустили Ctrl
2694
+
2695
+ 🎯 Возможности:
2696
+ - Создание горячих клавиш (Ctrl+C, Alt+Tab)
2697
+ - Комбинации с мышью
2698
+ """
2699
+ import keyboard
2700
+ keyboard.press(key)
2701
+
2702
+ @staticmethod
2703
+ def key_up(key: str):
2704
+ """
2705
+ ⬆️ ОТПУСТИТЬ КЛАВИШУ: Отпускает указанную клавишу, которая была зажата через key_down.
2706
+
2707
+ Параметры:
2708
+ key (str): название клавиши, которую нужно отпустить
2709
+
2710
+ 📝 Пример:
2711
+ Auto.key_down('shift')
2712
+ # ... что-то делаем ...
2713
+ Auto.key_up('shift')
2714
+
2715
+ 🎯 Возможности:
2716
+ - Завершение комбинаций клавиш
2717
+ - Возврат клавиатуры в обычный режим
2718
+ """
2719
+ import keyboard
2720
+ keyboard.release(key)
2721
+
2722
+ @staticmethod
2723
+ def press(key: str, duration: float = 0.05):
2724
+ """
2725
+ ⌨️ НАЖАТЬ КЛАВИШУ: Нажимает и сразу отпускает клавишу. Аналог быстрого тыка по кнопке.
2726
+
2727
+ Параметры:
2728
+ key (str): клавиша (например, 'enter', 'tab', 'esc', 'win+r')
2729
+ duration (float): время в секундах между нажатием и отпусканием
2730
+
2731
+ 📝 Пример:
2732
+ Auto.press('enter') # быстрый Enter
2733
+ Auto.press('win+r', 0.1) # открыть окно «Выполнить»
2734
+ Auto.press('volume_up') # увеличить громкость
2735
+
2736
+ 🎯 Возможности:
2737
+ - Имитация нажатий пользователя
2738
+ - Управление системой через скрипт
2739
+ """
2740
+ import keyboard
2741
+ keyboard.press(key)
2742
+ time.sleep(duration)
2743
+ keyboard.release(key)
2744
+
2745
+ @staticmethod
2746
+ def is_pressed(key: str) -> bool:
2747
+ """
2748
+ ❓ ПРОВЕРИТЬ НАЖАТИЕ: Проверяет, зажата ли клавиша в данный момент.
2749
+
2750
+ Параметры:
2751
+ key (str): название клавиши
2752
+
2753
+ Возвращает:
2754
+ bool: True, если клавиша нажата
2755
+
2756
+ 📝 Пример:
2757
+ if Auto.is_pressed('esc'):
2758
+ print("Клавиша Escape нажата! Выходим...")
2759
+ break
2760
+
2761
+ 🎯 Возможности:
2762
+ - Создание горячих клавиш выхода
2763
+ - Обработка пользовательского ввода в реальном времени
2764
+ """
2765
+ import keyboard
2766
+ return keyboard.is_pressed(key)
2767
+
2768
+ @staticmethod
2769
+ def write(text: str, delay: float = 0.05):
2770
+ """
2771
+ 📝 ПЕЧАТАТЬ ТЕКСТ: Печатает текст так, как будто его вводит человек с клавиатуры.
2772
+
2773
+ Параметры:
2774
+ text (str): строка для ввода
2775
+ delay (float): задержка между нажатиями символов в секундах
2776
+
2777
+ 📝 Пример:
2778
+ Auto.write("Привет, мир!")
2779
+ Auto.click(200, 300) # клик в поле ввода
2780
+ Auto.write("username")
2781
+ Auto.press('tab')
2782
+ Auto.write("password123")
2783
+ Auto.press('enter')
2784
+
2785
+ 🎯 Возможности:
2786
+ - Автозаполнение форм
2787
+ - Ввод логинов и паролей
2788
+ - Имитация живого пользователя (можно менять delay)
2789
+ """
2790
+ import keyboard
2791
+ for ch in text:
2792
+ keyboard.write(ch)
2793
+ time.sleep(delay)
2794
+
2795
+ # =========================== МЫШЬ ===========================
2796
+
2797
+ @staticmethod
2798
+ def mouse_down(button: str = 'left'):
2799
+ """
2800
+ 🖱️⬇️ ЗАЖАТЬ КНОПКУ МЫШИ: Зажимает кнопку мыши (не отпускает, пока не вызван mouse_up).
2801
+
2802
+ Параметры:
2803
+ button (str): 'left', 'right' или 'middle'
2804
+
2805
+ 📝 Пример:
2806
+ Auto.mouse_down('left') # зажали левую кнопку
2807
+ Auto.move(0, 0, 100, 100, 0.5) # перетащили объект
2808
+ Auto.mouse_up('left') # отпустили
2809
+
2810
+ 🎯 Возможности:
2811
+ - Перетаскивание объектов
2812
+ - Выделение текста мышью
2813
+ """
2814
+ import pyautogui
2815
+ pyautogui.mouseDown(button=button)
2816
+
2817
+ @staticmethod
2818
+ def mouse_up(button: str = 'left'):
2819
+ """
2820
+ 🖱️⬆️ ОТПУСТИТЬ КНОПКУ МЫШИ: Отпускает ранее зажатую кнопку мыши.
2821
+
2822
+ Параметры:
2823
+ button (str): 'left', 'right' или 'middle'
2824
+
2825
+ 📝 Пример:
2826
+ Auto.mouse_down('left')
2827
+ # ... перетаскивание ...
2828
+ Auto.mouse_up('left')
2829
+
2830
+ 🎯 Возможности:
2831
+ - Завершение перетаскивания
2832
+ - Отпускание после длительного удержания
2833
+ """
2834
+ import pyautogui
2835
+ pyautogui.mouseUp(button=button)
2836
+
2837
+ @staticmethod
2838
+ def click(x=None, y=None, button: str = 'left', clicks: int = 1):
2839
+ """
2840
+ 🖱️ КЛИК МЫШЬЮ: Кликает в указанных координатах или в текущем положении.
2841
+
2842
+ Параметры:
2843
+ x, y (int, optional): координаты клика. Можно передать кортеж (x, y) в x.
2844
+ button (str): 'left', 'right', 'middle'
2845
+ clicks (int): количество кликов (1 — одиночный, 2 — двойной)
2846
+
2847
+ 📝 Пример:
2848
+ Auto.click() # клик левой в текущей позиции
2849
+ Auto.click(500, 300) # клик в точку (500, 300)
2850
+ Auto.click((500, 300), button='right') # правый клик туда же
2851
+ Auto.click(clicks=2) # двойной клик в текущей позиции
2852
+
2853
+ 🎯 Возможности:
2854
+ - Автоматизация интерфейсов
2855
+ - Клики по кнопкам и ссылкам
2856
+ - Поддержка двойных и правых кликов
2857
+ """
2858
+ import pyautogui
2859
+ if x is not None and y is not None:
2860
+ pyautogui.click(x, y, button=button, clicks=clicks)
2861
+ elif isinstance(x, (tuple, list)) and len(x) >= 2:
2862
+ pyautogui.click(x[0], x[1], button=button, clicks=clicks)
2863
+ elif x is None and y is None:
2864
+ pyautogui.click(button=button, clicks=clicks)
2865
+ else:
2866
+ raise ValueError("Передай и x, и y, или кортеж (x, y), или ничего")
2867
+
2868
+ @staticmethod
2869
+ def double_click(x=None, y=None, button: str = 'left'):
2870
+ """
2871
+ 🖱️🖱️ ДВОЙНОЙ КЛИК: Делает двойной клик мышью.
2872
+
2873
+ Параметры:
2874
+ x, y (int, optional): координаты клика
2875
+ button (str): 'left', 'right', 'middle'
2876
+
2877
+ 📝 Пример:
2878
+ Auto.double_click() # двойной клик в текущей позиции
2879
+ Auto.double_click(400, 250) # двойной клик в точку
2880
+
2881
+ 🎯 Возможности:
2882
+ - Открытие файлов и папок
2883
+ - Выделение слов двойным кликом
2884
+ """
2885
+ Auto.click(x, y, button, clicks=2)
2886
+
2887
+ @staticmethod
2888
+ def move(x, y=None, duration: float = 0.0):
2889
+ """
2890
+ ➡️ ДВИЖЕНИЕ МЫШЬЮ: Перемещает курсор в указанную точку.
2891
+
2892
+ Параметры:
2893
+ x, y (int): координаты (или кортеж (x, y) в параметре x)
2894
+ duration (float): время перемещения в секундах (0 — мгновенно, 0.5 — плавно)
2895
+
2896
+ 📝 Пример:
2897
+ Auto.move(300, 400) # мгновенно в (300, 400)
2898
+ Auto.move((300, 400)) # то же самое кортежем
2899
+ Auto.move(300, 400, 0.5) # плавно за полсекунды
2900
+
2901
+ 🎯 Возможности:
2902
+ - Плавная анимация движения
2903
+ - Точное позиционирование мыши
2904
+ """
2905
+ import pyautogui
2906
+ if isinstance(x, (tuple, list)) and len(x) >= 2:
2907
+ pyautogui.moveTo(x[0], x[1], duration=duration)
2908
+ elif y is not None:
2909
+ pyautogui.moveTo(x, y, duration=duration)
2910
+ else:
2911
+ print("⚠️ Координаты не указаны")
2912
+
2913
+ @staticmethod
2914
+ def scroll(amount: int, x=None, y=None):
2915
+ """
2916
+ 🖱️🔄 ПРОКРУТКА: Прокручивает колёсико мыши.
2917
+
2918
+ Параметры:
2919
+ amount (int): положительное — вверх, отрицательное — вниз
2920
+ x, y (int, optional): координаты курсора перед прокруткой
2921
+
2922
+ 📝 Пример:
2923
+ Auto.scroll(3) # прокрутить вверх на 3 щелчка
2924
+ Auto.scroll(-5) # прокрутить вниз на 5 щелчков
2925
+ Auto.scroll(2, 500, 300) # переместиться в (500, 300) и прокрутить вверх
2926
+
2927
+ 🎯 Возможности:
2928
+ - Скроллинг веб-страниц
2929
+ - Навигация по спискам
2930
+ """
2931
+ import pyautogui
2932
+ pyautogui.scroll(amount, x, y)
2933
+
2934
+ @staticmethod
2935
+ def drag(start_x, start_y, end_x, end_y, button='left', duration=0.5):
2936
+ """
2937
+ 🖱️↔️ ПЕРЕТАСКИВАНИЕ: Перетаскивает объект от начальных координат к конечным.
2938
+
2939
+ Параметры:
2940
+ start_x, start_y (int): начальные координаты
2941
+ end_x, end_y (int): конечные координаты
2942
+ button (str): кнопка мыши для перетаскивания
2943
+ duration (float): время перетаскивания в секундах
2944
+
2945
+ 📝 Пример:
2946
+ Auto.drag(100, 100, 300, 300) # перетащить по диагонали
2947
+ Auto.drag(100, 100, 300, 300, duration=0.2) # быстро
2948
+
2949
+ 🎯 Возможности:
2950
+ - Перемещение окон и файлов
2951
+ - Drag-and-drop интерфейсы
2952
+ """
2953
+ import pyautogui
2954
+ pyautogui.moveTo(start_x, start_y)
2955
+ pyautogui.drag(end_x - start_x, end_y - start_y, duration=duration, button=button)
2956
+
2957
+ # =========================== ПРОЧЕЕ ===========================
2958
+
2959
+ @staticmethod
2960
+ def screenshot(save_path: str = "screen.png"):
2961
+ """
2962
+ 📸 СКРИНШОТ: Делает снимок всего экрана и сохраняет в файл.
2963
+
2964
+ Параметры:
2965
+ save_path (str): путь для сохранения относительно папки скрипта
2966
+
2967
+ 📝 Пример:
2968
+ Auto.screenshot() # screen.png в папке скрипта
2969
+ Auto.screenshot("my_screen.png") # своё имя файла
2970
+ Auto.screenshot("screenshots/game.png") # в подпапке
2971
+
2972
+ 🎯 Возможности:
2973
+ - Сохранение состояний интерфейса
2974
+ - Создание отчётов с картинками
2975
+ - Отладка автоматизации
2976
+ """
2977
+ import pyautogui
2978
+ full = Manager.search_file_path(save_path)
2979
+ img = pyautogui.screenshot()
2980
+ img.save(full)
2981
+
2982
+ @staticmethod
2983
+ def notify(title: str, message: str, timeout: int = 5):
2984
+ """
2985
+ 🔔 УВЕДОМЛЕНИЕ: Показывает системное уведомление в правом нижнем углу экрана.
2986
+
2987
+ Параметры:
2988
+ title (str): заголовок уведомления
2989
+ message (str): текст уведомления
2990
+ timeout (int): сколько секунд показывать
2991
+
2992
+ 📝 Пример:
2993
+ Auto.notify("Напоминание", "Пора сделать зарядку!")
2994
+ Auto.notify("Кеша", "Музыка поставлена на паузу", 3)
2995
+ Auto.notify("Загрузка", "Файл успешно скачан!", 5)
2996
+
2997
+ 🎯 Возможности:
2998
+ - Ненавязчивые оповещения
2999
+ - Уведомления о завершении операций
3000
+ - Напоминания пользователю
3001
+ """
3002
+ try:
3003
+ from plyer import notification
3004
+ notification.notify(title=title, message=message, timeout=timeout)
3005
+ except:
3006
+ print(f"⚠️ {title}: {message}")
3007
+
3008
+
3009
+ # =============================================================================
3010
+ # КЛАСС Web – БРАУЗЕР И ОТКРЫТИЕ ССЫЛОК
3011
+ # =============================================================================
3012
+ class Web:
3013
+ """
3014
+ 🌐 РАБОТА С БРАУЗЕРОМ: открытие ссылок, поиск в Яндексе и на YouTube.
3015
+
3016
+ Все методы статические — вызывай напрямую:
3017
+ Web.open("https://google.com")
3018
+ Web.search("погода Москва")
3019
+ Web.youtube("python уроки")
3020
+
3021
+ 🎯 Возможности класса:
3022
+ - Открытие любых URL в браузере по умолчанию
3023
+ - Быстрый поиск в Яндексе
3024
+ - Открытие результатов поиска на YouTube
3025
+ """
3026
+
3027
+ @staticmethod
3028
+ def open(url):
3029
+ """
3030
+ 🌐 ОТКРЫТЬ ССЫЛКУ: Открывает URL в браузере по умолчанию.
3031
+
3032
+ Параметры:
3033
+ url (str): полный URL для открытия
3034
+
3035
+ 📝 Пример:
3036
+ Web.open("https://google.com")
3037
+ Web.open("https://github.com/Naruto456y")
3038
+
3039
+ 🎯 Возможности:
3040
+ - Быстрый переход на сайты
3041
+ - Интеграция с веб-сервисами
3042
+ """
3043
+ webbrowser.open(url)
3044
+ print(f"🌐 Открыто: {url}")
3045
+
3046
+ @staticmethod
3047
+ def search(query):
3048
+ """
3049
+ 🔍 ПОИСК В ЯНДЕКСЕ: Открывает результаты поиска в Яндексе.
3050
+
3051
+ Параметры:
3052
+ query (str): поисковый запрос
3053
+
3054
+ 📝 Пример:
3055
+ Web.search("как сделать бота на Python")
3056
+ Web.search("погода в Москве")
3057
+
3058
+ 🎯 Возможности:
3059
+ - Быстрый поиск информации
3060
+ - Голосовой ассистент может искать в интернете
3061
+ """
3062
+ url = f"https://yandex.ru/search/?text={quote(query)}"
3063
+ webbrowser.open(url)
3064
+ print(f"🔍 Поиск: {query}")
3065
+
3066
+ @staticmethod
3067
+ def youtube(query):
3068
+ """
3069
+ 🎬 ПОИСК НА YOUTUBE: Открывает результаты поиска видео на YouTube.
3070
+
3071
+ Параметры:
3072
+ query (str): поисковый запрос
3073
+
3074
+ 📝 Пример:
3075
+ Web.youtube("python для начинающих")
3076
+ Web.youtube("Morgenshtern последний клип")
3077
+
3078
+ 🎯 Возможности:
3079
+ - Быстрый поиск видео
3080
+ - Интеграция с голосовым ассистентом
3081
+ """
3082
+ url = f"https://www.youtube.com/results?search_query={quote(query)}"
3083
+ webbrowser.open(url)
3084
+ print(f"🎬 YouTube: {query}")
3085
+
3086
+
3087
+ # =============================================================================
3088
+ # КЛАСС System – СИСТЕМНЫЕ КОМАНДЫ
3089
+ # =============================================================================
3090
+ class System:
3091
+ """
3092
+ 💻 СИСТЕМНЫЕ КОМАНДЫ: выключение, перезагрузка, сон, блокировка.
3093
+
3094
+ Все методы статические. Будь осторожен с shutdown и restart!
3095
+
3096
+ 🎯 Возможности класса:
3097
+ - Выключение компьютера
3098
+ - Перезагрузка системы
3099
+ - Переход в спящий режим
3100
+ - Блокировка экрана
3101
+ """
3102
+
3103
+ @staticmethod
3104
+ def shutdown(delay=0):
3105
+ """
3106
+ 🔌 ВЫКЛЮЧЕНИЕ КОМПЬЮТЕРА: Выключает ПК через указанное количество секунд.
3107
+
3108
+ Параметры:
3109
+ delay (int): задержка в секундах перед выключением
3110
+
3111
+ 📝 Пример:
3112
+ System.shutdown(10) # Выключится через 10 секунд
3113
+ System.shutdown() # Выключится немедленно
3114
+
3115
+ ⚠️ Внимание: Эта команда реально выключает компьютер! Сохраните все данные.
3116
+ """
3117
+ if delay > 0:
3118
+ print(f"⚠️ Выключение через {delay} секунд...")
3119
+ time.sleep(delay)
3120
+ os.system("shutdown /s /t 0")
3121
+ print("🔌 Выключение...")
3122
+
3123
+ @staticmethod
3124
+ def restart(delay=0):
3125
+ """
3126
+ 🔄 ПЕРЕЗАГРУЗКА: Перезагружает компьютер через указанное количество секунд.
3127
+
3128
+ Параметры:
3129
+ delay (int): задержка в секундах перед перезагрузкой
3130
+
3131
+ 📝 Пример:
3132
+ System.restart(30) # Перезагрузится через 30 секунд
3133
+ System.restart() # Перезагрузится немедленно
3134
+
3135
+ ⚠️ Внимание: Эта команда реально перезагружает компьютер! Сохраните все данные.
3136
+ """
3137
+ if delay > 0:
3138
+ print(f"⚠️ Перезагрузка через {delay} секунд...")
3139
+ time.sleep(delay)
3140
+ os.system("shutdown /r /t 0")
3141
+ print("🔄 Перезагрузка...")
3142
+
3143
+ @staticmethod
3144
+ def sleep():
3145
+ """
3146
+ 💤 СПЯЩИЙ РЕЖИМ: Переводит компьютер в спящий режим.
3147
+
3148
+ 📝 Пример:
3149
+ System.sleep()
3150
+ # Компьютер уснёт
3151
+
3152
+ 🎯 Возможности:
3153
+ - Экономия энергии
3154
+ - Быстрое прерывание работы с сохранением состояния
3155
+ """
3156
+ os.system("rundll32.exe powrprof.dll,SetSuspendState 0,1,0")
3157
+ print("💤 Спящий режим")
3158
+
3159
+ @staticmethod
3160
+ def lock():
3161
+ """
3162
+ 🔒 ЗАБЛОКИРОВАТЬ ЭКРАН: Блокирует рабочую станцию (экран входа в систему).
3163
+
3164
+ 📝 Пример:
3165
+ System.lock()
3166
+ # Экран заблокирован, нужно ввести пароль для входа
3167
+
3168
+ 🎯 Возможности:
3169
+ - Быстрая блокировка при отходе от компьютера
3170
+ - Безопасность данных
3171
+ """
3172
+ os.system("rundll32.exe user32.dll,LockWorkStation")
3173
+ print("🔒 Экран заблокирован")
3174
+
3175
+
3176
+ # =============================================================================
3177
+ # КЛАСС Info – ИНФОРМАЦИЯ О СИСТЕМЕ
3178
+ # =============================================================================
3179
+ class Info:
3180
+ """
3181
+ ℹ️ ИНФОРМАЦИЯ О СИСТЕМЕ: ОС, батарея, процессор, память.
3182
+
3183
+ Все методы статические — вызывай напрямую:
3184
+ battery, plugged = Info.battery()
3185
+ os_name = Info.os()
3186
+ cpu_load = Info.cpu()
3187
+ ram_used, ram_total = Info.ram()
3188
+
3189
+ 🎯 Возможности класса:
3190
+ - Мониторинг заряда батареи ноутбука
3191
+ - Определение операционной системы
3192
+ - Загрузка процессора в процентах
3193
+ - Использование оперативной памяти
3194
+ """
3195
+
3196
+ @staticmethod
3197
+ def battery():
3198
+ """
3199
+ 🔋 БАТАРЕЯ: Возвращает уровень заряда и статус подключения к сети.
3200
+
3201
+ Возвращает:
3202
+ tuple: (процент заряда, подключена ли зарядка) или (None, None) при ошибке
3203
+
3204
+ 📝 Пример:
3205
+ percent, plugged = Info.battery()
3206
+ if percent is not None:
3207
+ print(f"Заряд: {percent}%")
3208
+ if plugged:
3209
+ print("Ноутбук на зарядке")
3210
+ elif percent < 20:
3211
+ print("⚠️ Пора заряжать!")
3212
+
3213
+ 🎯 Возможности:
3214
+ - Мониторинг батареи в приложениях
3215
+ - Предупреждения о низком заряде
3216
+ """
3217
+ try:
3218
+ import psutil
3219
+ battery = psutil.sensors_battery()
3220
+ if battery:
3221
+ return battery.percent, battery.power_plugged
3222
+ except:
3223
+ pass
3224
+ return None, None
3225
+
3226
+ @staticmethod
3227
+ def os():
3228
+ """
3229
+ 🖥️ ОПЕРАЦИОННАЯ СИСТЕМА: Возвращает тип операционной системы.
3230
+
3231
+ Возвращает:
3232
+ str: 'win32' для Windows, 'linux' для Linux, 'darwin' для Mac
3233
+
3234
+ 📝 Пример:
3235
+ os_name = Info.os()
3236
+ if os_name == 'win32':
3237
+ print("Работаем на Windows")
3238
+ elif os_name == 'linux':
3239
+ print("Работаем на Linux")
3240
+
3241
+ 🎯 Возможности:
3242
+ - Кроссплатформенная разработка
3243
+ - Выбор системных команд под ОС
3244
+ """
3245
+ return sys.platform
3246
+
3247
+ @staticmethod
3248
+ def cpu():
3249
+ """
3250
+ 🧠 ПРОЦЕССОР: Возвращает загрузку CPU в процентах.
3251
+
3252
+ Возвращает:
3253
+ float или None: процент загрузки процессора (измеряется 1 секунду)
3254
+
3255
+ 📝 Пример:
3256
+ cpu_load = Info.cpu()
3257
+ if cpu_load and cpu_load > 90:
3258
+ print(f"⚠️ Процессор сильно загружен: {cpu_load}%")
3259
+ else:
3260
+ print(f"CPU: {cpu_load}%")
3261
+
3262
+ 🎯 Возможности:
3263
+ - Мониторинг производительности
3264
+ - Оптимизация тяжёлых вычислений
3265
+ """
3266
+ try:
3267
+ import psutil
3268
+ return psutil.cpu_percent(interval=1)
3269
+ except:
3270
+ return None
3271
+
3272
+ @staticmethod
3273
+ def ram():
3274
+ """
3275
+ 💾 ОПЕРАТИВНАЯ ПАМЯТЬ: Возвращает использование RAM в мегабайтах.
3276
+
3277
+ Возвращает:
3278
+ tuple: (использовано МБ, всего МБ) или (None, None) при ошибке
3279
+
3280
+ 📝 Пример:
3281
+ used, total = Info.ram()
3282
+ if used and total:
3283
+ print(f"RAM: {used}/{total} MB")
3284
+ print(f"Свободно: {total - used} MB ({100 - used/total*100:.0f}%)")
3285
+
3286
+ 🎯 Возможности:
3287
+ - Мониторинг потребления памяти
3288
+ - Диагностика утечек памяти
3289
+ """
3290
+ try:
3291
+ import psutil
3292
+ mem = psutil.virtual_memory()
3293
+ return mem.used // (1024*1024), mem.total // (1024*1024)
3294
+ except:
3295
+ return None, None
3296
+
3297
+
3298
+ # =============================================================================
3299
+ # КЛАСС ThreadManager – УПРАВЛЕНИЕ ПОТОКАМИ
3300
+ # =============================================================================
3301
+ class ThreadManager:
3302
+ """
3303
+ 🧵 УПРАВЛЕНИЕ ПОТОКАМИ — запуск, остановка, мониторинг фоновых задач.
3304
+
3305
+ 📝 Пример:
3306
+ tm = ThreadManager()
3307
+ tm.start(my_function, args=(1,2))
3308
+ tm.stop_all()
3309
+
3310
+ 🎯 Возможности:
3311
+ - Запуск фоновых задач без блокировки основного кода
3312
+ - Остановка потоков по ID или всех сразу
3313
+ - Мониторинг активных потоков
3314
+ - Автоматическая обработка событий остановки
3315
+ """
3316
+
3317
+ def __init__(self):
3318
+ """
3319
+ 🏗️ ИНИЦИАЛИЗАЦИЯ: Создаёт менеджер потоков с пустыми словарями.
3320
+ """
3321
+ self.threads = {}
3322
+ self.stop_events = {}
3323
+
3324
+ def start(self, target, args=(), name=None, daemon=True):
3325
+ """
3326
+ ▶️ ЗАПУСК ПОТОКА: Запускает функцию в отдельном фоновом потоке.
3327
+
3328
+ Параметры:
3329
+ target (callable): функция для выполнения
3330
+ args (tuple): кортеж аргументов функции
3331
+ name (str, optional): имя потока (если не указано, генерируется автоматически)
3332
+ daemon (bool): фоновый поток (умрёт при завершении программы)
3333
+
3334
+ Возвращает:
3335
+ str: ID потока
3336
+
3337
+ 📝 Пример:
3338
+ def worker(stop_event):
3339
+ while not stop_event.is_set():
3340
+ print("Работаю...")
3341
+ time.sleep(1)
3342
+
3343
+ tm = ThreadManager()
3344
+ thread_id = tm.start(worker, name="my_worker")
3345
+ time.sleep(5)
3346
+ tm.stop(thread_id)
3347
+
3348
+ 🎯 Возможности:
3349
+ - Фоновая обработка данных
3350
+ - Параллельное выполнение задач
3351
+ """
3352
+ stop_event = threading.Event()
3353
+ thread_id = name or f"Thread_{len(self.threads) + 1}"
3354
+
3355
+ def wrapper():
3356
+ target(*args, stop_event=stop_event)
3357
+
3358
+ thread = threading.Thread(target=wrapper, name=thread_id, daemon=daemon)
3359
+ self.threads[thread_id] = thread
3360
+ self.stop_events[thread_id] = stop_event
3361
+ thread.start()
3362
+ print(f"✅ Поток '{thread_id}' запущен")
3363
+ return thread_id
3364
+
3365
+ def stop(self, thread_id):
3366
+ """
3367
+ ⏹️ ОСТАНОВИТЬ ПОТОК: Останавливает поток по его ID.
3368
+
3369
+ Параметры:
3370
+ thread_id (str): ID потока, который нужно остановить
3371
+
3372
+ Возвращает:
3373
+ bool: True если поток остановлен, False если не найден
3374
+
3375
+ 📝 Пример:
3376
+ tm.stop("my_worker")
3377
+ """
3378
+ if thread_id in self.stop_events:
3379
+ self.stop_events[thread_id].set()
3380
+ print(f"⏹️ Поток '{thread_id}' остановлен")
3381
+ return True
3382
+ print(f"❌ Поток '{thread_id}' не найден")
3383
+ return False
3384
+
3385
+ def stop_all(self):
3386
+ """
3387
+ ⏹️⏹️ ОСТАНОВИТЬ ВСЁ: Останавливает все запущенные потоки.
3388
+
3389
+ 📝 Пример:
3390
+ tm.stop_all()
3391
+ print("Все фоновые задачи остановлены")
3392
+ """
3393
+ for thread_id in self.stop_events:
3394
+ self.stop_events[thread_id].set()
3395
+ print(f"⏹️ Остановлено {len(self.stop_events)} потоков")
3396
+
3397
+ def is_alive(self, thread_id):
3398
+ """
3399
+ ❓ ПОТОК ЖИВ?: Проверяет, активен ли поток.
3400
+
3401
+ Параметры:
3402
+ thread_id (str): ID потока
3403
+
3404
+ Возвращает:
3405
+ bool: True если поток жив, False если завершился или не найден
3406
+
3407
+ 📝 Пример:
3408
+ if tm.is_alive("my_worker"):
3409
+ print("Поток ещё работает")
3410
+ else:
3411
+ print("Поток завершён")
3412
+ """
3413
+ return thread_id in self.threads and self.threads[thread_id].is_alive()
3414
+
3415
+ def list_threads(self):
3416
+ """
3417
+ 📋 СПИСОК ПОТОКОВ: Возвращает список имён всех активных потоков.
3418
+
3419
+ Возвращает:
3420
+ list[str]: имена активных потоков
3421
+
3422
+ 📝 Пример:
3423
+ active = tm.list_threads()
3424
+ print(f"Активных потоков: {len(active)}")
3425
+ for name in active:
3426
+ print(f" - {name}")
3427
+ """
3428
+ return [name for name, t in self.threads.items() if t.is_alive()]
3429
+
3430
+ def active_count(self):
3431
+ """
3432
+ 🔢 КОЛИЧЕСТВО ПОТОКОВ: Возвращает общее количество активных потоков в программе.
3433
+
3434
+ Возвращает:
3435
+ int: количество активных потоков
3436
+
3437
+ 📝 Пример:
3438
+ total = tm.active_count()
3439
+ print(f"Всего потоков в программе: {total}")
3440
+ """
3441
+ return threading.active_count()
3442
+
3443
+
3444
+ # =============================================================================
3445
+ # КЛАСС ScreenReader – РАСПОЗНАВАНИЕ ЭКРАНА
3446
+ # =============================================================================
3447
+ class ScreenReader:
3448
+ """
3449
+ 🖥️ РАСПОЗНАВАНИЕ ЭКРАНА — поиск изображений, цветов, текста на экране.
3450
+
3451
+ Требуется установка: opencv-python, pillow, easyocr, mss, numpy
3452
+
3453
+ 📝 Примеры:
3454
+ # Найти картинку на экране
3455
+ pos = ScreenReader.find_image("button.png")
3456
+ if pos:
3457
+ pyautogui.click(pos)
3458
+
3459
+ # Найти цвет
3460
+ pos = ScreenReader.find_color((255, 0, 0)) # красный
3461
+
3462
+ # Прочитать текст с экрана
3463
+ result = ScreenReader.find_text_coordinates("Принять")
3464
+ if result['found']:
3465
+ pyautogui.click(result['x'], result['y'])
3466
+
3467
+ 🎯 Возможности:
3468
+ - Поиск изображения на экране (для автоматизации кликов)
3469
+ - Поиск пикселя по цвету
3470
+ - Чтение текста с экрана (OCR)
3471
+ - Получение цвета пикселя в координатах
3472
+ - Ожидание появления изображения
3473
+ - Выделение найденных изображений рамкой
3474
+ """
3475
+
3476
+ @staticmethod
3477
+ def find_image(template_path, confidence=0.8, region=None):
3478
+ """
3479
+ 🔍 НАЙТИ ИЗОБРАЖЕНИЕ: Ищет изображение-шаблон на текущем экране.
3480
+
3481
+ Параметры:
3482
+ template_path (str): путь к файлу-шаблону (.png рекомендуется)
3483
+ confidence (float): порог совпадения (0.7–0.95, чем выше — тем точнее)
3484
+ region (tuple): область поиска (x, y, width, height)
3485
+
3486
+ Возвращает:
3487
+ tuple или None: (x, y) — центр найденного изображения, или None если не найдено
3488
+
3489
+ 📝 Пример:
3490
+ pos = ScreenReader.find_image("button.png", confidence=0.9)
3491
+ if pos:
3492
+ print(f"Кнопка найдена в {pos}")
3493
+ Auto.click(pos)
3494
+
3495
+ # Искать только в левом верхнем углу
3496
+ pos = ScreenReader.find_image("icon.png", region=(0, 0, 500, 500))
3497
+
3498
+ 🎯 Возможности:
3499
+ - Автоматизация кликов по кнопкам
3500
+ - Проверка наличия элементов интерфейса
3501
+ - Поиск в ограниченной области для ускорения
3502
+ """
3503
+ import cv2
3504
+ import numpy as np
3505
+ import pyautogui
3506
+
3507
+ if region:
3508
+ screenshot = pyautogui.screenshot(region=region)
3509
+ else:
3510
+ screenshot = pyautogui.screenshot()
3511
+
3512
+ screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
3513
+ template = cv2.imread(template_path)
3514
+
3515
+ if template is None:
3516
+ print(f"❌ Не удалось загрузить {template_path}")
3517
+ return None
3518
+
3519
+ result = cv2.matchTemplate(screenshot, template, cv2.TM_CCOEFF_NORMED)
3520
+ min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(result)
3521
+
3522
+ if max_val >= confidence:
3523
+ h, w = template.shape[:2]
3524
+ center_x = max_loc[0] + w // 2
3525
+ center_y = max_loc[1] + h // 2
3526
+ if region:
3527
+ center_x += region[0]
3528
+ center_y += region[1]
3529
+ print(f"✅ Изображение найдено в ({center_x}, {center_y}) (совпадение: {max_val:.2f})")
3530
+ return (center_x, center_y)
3531
+ else:
3532
+ print(f"❌ Изображение не найдено (совпадение: {max_val:.2f})")
3533
+ return None
3534
+
3535
+ @staticmethod
3536
+ def find_color(target_color, tolerance=30, region=None):
3537
+ """
3538
+ 🎨 НАЙТИ ЦВЕТ: Ищет первый пиксель заданного цвета на экране.
3539
+
3540
+ Параметры:
3541
+ target_color (tuple): кортеж (R, G, B) — цвет для поиска
3542
+ tolerance (int): допустимое отклонение (0 — точное совпадение, 30 — похожий оттенок)
3543
+ region (tuple): область поиска (x, y, width, height)
3544
+
3545
+ Возвращает:
3546
+ tuple или None: (x, y) координаты пикселя, или None если не найден
3547
+
3548
+ 📝 Пример:
3549
+ # Найти красный крестик закрытия окна
3550
+ pos = ScreenReader.find_color((255, 0, 0), tolerance=20)
3551
+ if pos:
3552
+ Auto.click(pos)
3553
+
3554
+ # Искать только в строке состояния
3555
+ pos = ScreenReader.find_color((0, 255, 0), region=(0, 900, 1920, 1080))
3556
+
3557
+ 🎯 Возможности:
3558
+ - Поиск элементов по цвету
3559
+ - Проверка статусов (красный/зелёный индикатор)
3560
+ """
3561
+ import pyautogui
3562
+ import numpy as np
3563
+
3564
+ screenshot = pyautogui.screenshot(region=region) if region else pyautogui.screenshot()
3565
+ offset_x = region[0] if region else 0
3566
+ offset_y = region[1] if region else 0
3567
+
3568
+ img = np.array(screenshot)
3569
+ for y in range(img.shape[0]):
3570
+ for x in range(img.shape[1]):
3571
+ pixel = img[y, x][:3]
3572
+ if all(abs(pixel[i] - target_color[i]) <= tolerance for i in range(3)):
3573
+ result_x = x + offset_x
3574
+ result_y = y + offset_y
3575
+ print(f"✅ Цвет найден в ({result_x}, {result_y})")
3576
+ return (result_x, result_y)
3577
+
3578
+ print(f"❌ Цвет {target_color} не найден")
3579
+ return None
3580
+
3581
+ @staticmethod
3582
+ def get_pixel_color(x, y):
3583
+ """
3584
+ 🎨 ЦВЕТ ПИКСЕЛЯ: Получает цвет пикселя в указанных координатах.
3585
+
3586
+ Параметры:
3587
+ x (int): координата X
3588
+ y (int): координата Y
3589
+
3590
+ Возвращает:
3591
+ tuple: (R, G, B) — цвет пикселя
3592
+
3593
+ 📝 Пример:
3594
+ color = ScreenReader.get_pixel_color(100, 200)
3595
+ print(f"Цвет в (100, 200): {color}")
3596
+ # Цвет в (100, 200): (255, 128, 64)
3597
+ """
3598
+ import pyautogui
3599
+ pixel = pyautogui.pixel(x, y)
3600
+ print(f"🖱️ Цвет в ({x}, {y}): {pixel}")
3601
+ return pixel
3602
+
3603
+ @staticmethod
3604
+ def find_text_coordinates(search_text, languages=['en', 'ru'], monitor_index=1, confidence_threshold=0.5, partial_match=True):
3605
+ """
3606
+ 📖 НАЙТИ ТЕКСТ: Ищет координаты текста на экране с помощью OCR.
3607
+
3608
+ Параметры:
3609
+ search_text (str): текст для поиска на экране
3610
+ languages (list): языки распознавания (по умолчанию ['en', 'ru'])
3611
+ monitor_index (int): индекс монитора (1 — основной)
3612
+ confidence_threshold (float): минимальная уверенность распознавания (0.0–1.0)
3613
+ partial_match (bool): искать частичное совпадение (True) или точное (False)
3614
+
3615
+ Возвращает:
3616
+ dict: {'found': True/False, 'x': ..., 'y': ..., 'confidence': ...}
3617
+
3618
+ 📝 Пример:
3619
+ result = ScreenReader.find_text_coordinates("Принять")
3620
+ if result['found']:
3621
+ Auto.click(result['x'], result['y'])
3622
+ print(f"Нажали на текст (уверенность: {result['confidence']:.2f})")
3623
+
3624
+ 🎯 Возможности:
3625
+ - Поиск текста в интерфейсах
3626
+ - Автоклики по надписям
3627
+ - Многоязычное распознавание
3628
+ """
3629
+ import easyocr
3630
+ import mss
3631
+ import numpy as np
3632
+ from PIL import Image
3633
+
3634
+ reader = easyocr.Reader(languages)
3635
+
3636
+ with mss.mss() as sct:
3637
+ monitor = sct.monitors[monitor_index]
3638
+ screenshot = sct.grab(monitor)
3639
+ img = Image.frombytes("RGB", screenshot.size, screenshot.bgra, "raw", "BGRX")
3640
+ img_array = np.array(img)
3641
+
3642
+ results = reader.readtext(img_array, detail=1, text_threshold=0.7)
3643
+
3644
+ best_match = None
3645
+ for detection in results:
3646
+ bbox = detection[0]
3647
+ text = detection[1].lower()
3648
+ confidence = detection[2]
3649
+
3650
+ match = False
3651
+ if partial_match:
3652
+ if search_text in text:
3653
+ match = True
3654
+ else:
3655
+ if search_text == text:
3656
+ match = True
3657
+
3658
+ if match and confidence >= confidence_threshold:
3659
+ x_coords = [point[0] for point in bbox]
3660
+ y_coords = [point[1] for point in bbox]
3661
+ center_x = int(sum(x_coords) / len(x_coords))
3662
+ center_y = int(sum(y_coords) / len(y_coords))
3663
+
3664
+ if best_match is None or confidence > best_match['confidence']:
3665
+ best_match = {
3666
+ 'found': True,
3667
+ 'x': center_x,
3668
+ 'y': center_y,
3669
+ 'confidence': confidence,
3670
+ 'matched_text': detection[1],
3671
+ 'bbox': bbox
3672
+ }
3673
+
3674
+ if best_match:
3675
+ return best_match
3676
+ return {'found': False, 'error': f'Текст "{search_text}" не найден'}
3677
+
3678
+ @staticmethod
3679
+ def wait_for_image(template_path, timeout=30, confidence=0.8):
3680
+ """
3681
+ ⏳ ЖДАТЬ ИЗОБРАЖЕНИЕ: Ждёт появления изображения на экране в течение заданного времени.
3682
+
3683
+ Параметры:
3684
+ template_path (str): путь к изображению
3685
+ timeout (int): максимальное время ожидания в секундах
3686
+ confidence (float): порог совпадения
3687
+
3688
+ Возвращает:
3689
+ tuple или None: (x, y) центр найденного изображения, или None если таймаут
3690
+
3691
+ 📝 Пример:
3692
+ print("Ждём загрузку...")
3693
+ pos = ScreenReader.wait_for_image("loading_complete.png", timeout=60)
3694
+ if pos:
3695
+ print("Загрузка завершена!")
3696
+ else:
3697
+ print("Таймаут!")
3698
+
3699
+ 🎯 Возможности:
3700
+ - Ожидание загрузки интерфейса
3701
+ - Синхронизация с медленными программами
3702
+ """
3703
+ start = time.time()
3704
+ while time.time() - start < timeout:
3705
+ pos = ScreenReader.find_image(template_path, confidence)
3706
+ if pos:
3707
+ print(f"✅ Изображение появилось через {time.time() - start:.1f} сек")
3708
+ return pos
3709
+ time.sleep(0.5)
3710
+ print(f"❌ Изображение не появилось за {timeout} сек")
3711
+ return None
3712
+
3713
+ @staticmethod
3714
+ def highlight_image(template_path, confidence=0.8, duration=2000, color='lime', thickness=4):
3715
+ """
3716
+ 🟩 ВЫДЕЛИТЬ ИЗОБРАЖЕНИЕ: Находит изображение на экране и обводит его рамкой.
3717
+
3718
+ Параметры:
3719
+ template_path (str): путь к картинке-образцу (png)
3720
+ confidence (float): порог совпадения (0.7–0.95)
3721
+ duration (int): сколько миллисекунд держать рамку
3722
+ color (str): цвет рамки ('lime', 'red', 'blue', ...)
3723
+ thickness (int): толщина линии рамки в пикселях
3724
+
3725
+ Возвращает:
3726
+ tuple или None: (x, y) координаты найденного изображения
3727
+
3728
+ 📝 Пример:
3729
+ ScreenReader.highlight_image("button.png", duration=3000, color='red')
3730
+ # Найдёт кнопку и обведёт её красной рамкой на 3 секунды
3731
+
3732
+ 🎯 Возможности:
3733
+ - Визуальная отладка поиска изображений
3734
+ - Демонстрация найденных элементов
3735
+ """
3736
+ import cv2
3737
+ import numpy as np
3738
+ import pyautogui
3739
+ import tkinter as tk
3740
+
3741
+ screenshot = pyautogui.screenshot()
3742
+ screenshot = cv2.cvtColor(np.array(screenshot), cv2.COLOR_RGB2BGR)
3743
+ template = cv2.imread(template_path)
3744
+
3745
+ if template is None:
3746
+ print(f"❌ Не найден файл: {template_path}")
3747
+ return
3748
+
3749
+ result = cv2.matchTemplate(screenshot, template, cv2.TM_CCOEFF_NORMED)
3750
+ _, max_val, _, max_loc = cv2.minMaxLoc(result)
3751
+
3752
+ if max_val < confidence:
3753
+ print(f"❌ Изображение не найдено (совпадение {max_val:.2f})")
3754
+ return
3755
+
3756
+ h, w = template.shape[:2]
3757
+ x, y = max_loc
3758
+ print(f"✅ Найдено: ({x}, {y}) размер {w}x{h} (совпадение {max_val:.2f})")
3759
+
3760
+ root = tk.Tk()
3761
+ root.overrideredirect(True)
3762
+ root.lift()
3763
+ root.wm_attributes("-topmost", True)
3764
+ root.wm_attributes("-transparentcolor", "white")
3765
+
3766
+ canvas = tk.Canvas(root, width=w, height=h, highlightthickness=0, bg='white')
3767
+ canvas.pack()
3768
+ canvas.create_rectangle(0, 0, w-1, h-1, outline=color, width=thickness)
3769
+
3770
+ root.geometry(f"{w}x{h}+{x}+{y}")
3771
+ root.after(duration, root.destroy)
3772
+ root.mainloop()
3773
+
3774
+ return (x, y)
3775
+
3776
+
3777
+ # =============================================================================
3778
+ # КЛАСС Clipboard – БУФЕР ОБМЕНА
3779
+ # =============================================================================
3780
+ class Clipboard:
3781
+ """
3782
+ 📋 РАБОТА С БУФЕРОМ ОБМЕНА: копирование и вставка текста.
3783
+
3784
+ Требуется установка: pip install pyperclip
3785
+
3786
+ 📝 Пример:
3787
+ Clipboard.copy("Привет, мир!")
3788
+ text = Clipboard.paste()
3789
+ print(text) # "Привет, мир!"
3790
+ """
3791
+
3792
+ @staticmethod
3793
+ def copy(text: str):
3794
+ """
3795
+ 📋📤 КОПИРОВАТЬ: Копирует текст в буфер обмена.
3796
+
3797
+ Параметры:
3798
+ text (str): текст для копирования
3799
+
3800
+ 📝 Пример:
3801
+ Clipboard.copy("Важный текст")
3802
+ # Теперь можно вставить его через Ctrl+V где угодно
3803
+
3804
+ 🎯 Возможности:
3805
+ - Программное копирование текста
3806
+ - Подготовка данных для вставки
3807
+ """
3808
+ import pyperclip
3809
+ pyperclip.copy(text)
3810
+
3811
+ @staticmethod
3812
+ def paste() -> str:
3813
+ """
3814
+ 📋📥 ВСТАВИТЬ: Извлекает текст из буфера обмена.
3815
+
3816
+ Возвращает:
3817
+ str: текст из буфера обмена
3818
+
3819
+ 📝 Пример:
3820
+ text = Clipboard.paste()
3821
+ print(f"В буфере: {text}")
3822
+
3823
+ 🎯 Возможности:
3824
+ - Получение скопированных данных
3825
+ - Автоматическая обработка текста из буфера
3826
+ """
3827
+ import pyperclip
3828
+ return pyperclip.paste()
3829
+
3830
+
3831
+ # =============================================================================
3832
+ # КЛАСС MathHelper – БЫСТРЫЕ ВЫЧИСЛЕНИЯ
3833
+ # =============================================================================
3834
+ class MathHelper:
3835
+ """
3836
+ 🧮 ПРОСТЫЕ МАТЕМАТИЧЕСКИЕ ОПЕРАЦИИ: факториал, среднее, медиана.
3837
+
3838
+ 📝 Примеры:
3839
+ MathHelper.factorial(5) # 120
3840
+ MathHelper.mean([1, 2, 3]) # 2.0
3841
+ MathHelper.median([1, 2, 3, 4]) # 2.5
3842
+ MathHelper.to_binary(42) # '101010'
3843
+ MathHelper.to_hex(42) # '2a'
3844
+ MathHelper.school_round(3.5) # 4
3845
+ """
3846
+
3847
+ @staticmethod
3848
+ def factorial(n: int) -> int:
3849
+ """
3850
+ 🔢 ФАКТОРИАЛ: Вычисляет факториал числа n! = 1 × 2 × ... × n.
3851
+
3852
+ Параметры:
3853
+ n (int): неотрицательное целое число
3854
+
3855
+ Возвращает:
3856
+ int: значение факториала
3857
+
3858
+ 📝 Пример:
3859
+ MathHelper.factorial(5) # 120 (1×2×3×4×5)
3860
+ MathHelper.factorial(0) # 1
3861
+ """
3862
+ return math.factorial(n)
3863
+
3864
+ @staticmethod
3865
+ def mean(numbers: list) -> float:
3866
+ """
3867
+ 📊 СРЕДНЕЕ АРИФМЕТИЧЕСКОЕ: Сумма элементов, делённая на их количество.
3868
+
3869
+ Параметры:
3870
+ numbers (list): список чисел
3871
+
3872
+ Возвращает:
3873
+ float: среднее значение
3874
+
3875
+ 📝 Пример:
3876
+ MathHelper.mean([1, 2, 3, 4, 5]) # 3.0
3877
+ MathHelper.mean([10, 20]) # 15.0
3878
+ """
3879
+ return sum(numbers) / len(numbers)
3880
+
3881
+ @staticmethod
3882
+ def median(numbers: list) -> float:
3883
+ """
3884
+ 📈 МЕДИАНА: Значение, которое находится посередине отсортированного списка.
3885
+
3886
+ Параметры:
3887
+ numbers (list): список чисел
3888
+
3889
+ Возвращает:
3890
+ float: медиана
3891
+
3892
+ 📝 Пример:
3893
+ MathHelper.median([1, 2, 3, 4, 5]) # 3
3894
+ MathHelper.median([1, 2, 3, 4]) # 2.5
3895
+ """
3896
+ return statistics.median(numbers)
3897
+
3898
+ @staticmethod
3899
+ def to_binary(n: int) -> str:
3900
+ """
3901
+ 0️⃣1️⃣ В ДВОИЧНУЮ: Переводит целое число в двоичную систему счисления.
3902
+
3903
+ Параметры:
3904
+ n (int): целое число
3905
+
3906
+ Возвращает:
3907
+ str: строка с двоичным представлением
3908
+
3909
+ 📝 Пример:
3910
+ MathHelper.to_binary(42) # '101010'
3911
+ MathHelper.to_binary(255) # '11111111'
3912
+ """
3913
+ return bin(n)[2:]
3914
+
3915
+ @staticmethod
3916
+ def to_hex(n: int) -> str:
3917
+ """
3918
+ 🔣 В ШЕСТНАДЦАТЕРИЧНУЮ: Переводит целое число в шестнадцатеричную систему.
3919
+
3920
+ Параметры:
3921
+ n (int): целое число
3922
+
3923
+ Возвращает:
3924
+ str: строка с шестнадцатеричным представлением
3925
+
3926
+ 📝 Пример:
3927
+ MathHelper.to_hex(42) # '2a'
3928
+ MathHelper.to_hex(255) # 'ff'
3929
+ """
3930
+ return hex(n)[2:]
3931
+
3932
+ @staticmethod
3933
+ def school_round(number, digits=0):
3934
+ """
3935
+ 🎓 ШКОЛЬНОЕ ОКРУГЛЕНИЕ: Работает как на уроках математики.
3936
+ 3.5 → 4, 3.15 (до десятых) → 3.2
3937
+
3938
+ Отличается от встроенного round(), который использует банковское округление.
3939
+
3940
+ Параметры:
3941
+ number (float): число для округления
3942
+ digits (int): сколько знаков после запятой оставить (по умолчанию 0)
3943
+
3944
+ Возвращает:
3945
+ float: округлённое число
3946
+
3947
+ 📝 Пример:
3948
+ MathHelper.school_round(3.5) # 4.0
3949
+ MathHelper.school_round(4.5) # 5.0
3950
+ MathHelper.school_round(3.15, 1) # 3.2
3951
+ MathHelper.school_round(3.14, 1) # 3.1
3952
+
3953
+ 🎯 Возможности:
3954
+ - Правильное округление для школьных задач
3955
+ - Финансовые расчёты
3956
+ """
3957
+ multiplier = 10 ** digits
3958
+ return (number * multiplier + 0.5 * (1 if number >= 0 else -1)) // 1 * (1 / multiplier)
3959
+
3960
+
3961
+ # =============================================================================
3962
+ # КЛАСС FileUtils – ПРОСТЫЕ ОПЕРАЦИИ С ФАЙЛАМИ
3963
+ # =============================================================================
3964
+ class FileUtils:
3965
+ """
3966
+ 📄 ЧТЕНИЕ, ЗАПИСЬ И ПОДСЧЁТ СТРОК.
3967
+
3968
+ 📝 Примеры:
3969
+ content = FileUtils.read_text("notes.txt")
3970
+ FileUtils.write_text("hello.txt", "Привет!")
3971
+ lines = FileUtils.count_lines("log.txt")
3972
+ """
3973
+
3974
+ @staticmethod
3975
+ def read_text(path: str) -> str:
3976
+ """
3977
+ 📖 ЧИТАТЬ ФАЙЛ: Читает весь текстовый файл и возвращает содержимое.
3978
+
3979
+ Параметры:
3980
+ path (str): путь к текстовому файлу
3981
+
3982
+ Возвращает:
3983
+ str: содержимое файла
3984
+
3985
+ 📝 Пример:
3986
+ content = FileUtils.read_text("config.txt")
3987
+ print(content)
3988
+
3989
+ 🎯 Возможности:
3990
+ - Чтение конфигурационных файлов
3991
+ - Загрузка текстовых данных
3992
+ """
3993
+ with open(path, 'r', encoding='utf-8') as f:
3994
+ return f.read()
3995
+
3996
+ @staticmethod
3997
+ def write_text(path: str, content: str):
3998
+ """
3999
+ ✍️ ЗАПИСАТЬ ФАЙЛ: Записывает текст в файл (перезаписывает, если существует).
4000
+
4001
+ Параметры:
4002
+ path (str): путь к файлу
4003
+ content (str): текст для записи
4004
+
4005
+ 📝 Пример:
4006
+ FileUtils.write_text("output.txt", "Результаты:\\n...")
4007
+ print("Файл сохранён!")
4008
+
4009
+ 🎯 Возможности:
4010
+ - Сохранение отчётов и логов
4011
+ - Создание новых текстовых файлов
4012
+ """
4013
+ with open(path, 'w', encoding='utf-8') as f:
4014
+ f.write(content)
4015
+
4016
+ @staticmethod
4017
+ def count_lines(path: str) -> int:
4018
+ """
4019
+ 🔢 ПОДСЧЁТ СТРОК: Считает количество строк в текстовом файле.
4020
+
4021
+ Параметры:
4022
+ path (str): путь к файлу
4023
+
4024
+ Возвращает:
4025
+ int: количество строк
4026
+
4027
+ 📝 Пример:
4028
+ lines = FileUtils.count_lines("log.txt")
4029
+ print(f"В лог-файле {lines} строк")
4030
+
4031
+ 🎯 Возможности:
4032
+ - Анализ размера логов
4033
+ - Проверка количества записей
4034
+ """
4035
+ with open(path, 'r', encoding='utf-8') as f:
4036
+ return sum(1 for _ in f)
4037
+
4038
+
4039
+ # =============================================================================
4040
+ # КЛАСС ImageUtils – ПРОСТЫЕ ОПЕРАЦИИ С ИЗОБРАЖЕНИЯМИ (Pillow)
4041
+ # =============================================================================
4042
+ class ImageUtils:
4043
+ """
4044
+ 🖼️ ИЗМЕНЕНИЕ РАЗМЕРА, ОБРЕЗКА, ПОВОРОТ ИЗОБРАЖЕНИЙ.
4045
+
4046
+ Требуется установка: pip install pillow
4047
+
4048
+ 📝 Примеры:
4049
+ ImageUtils.resize("input.jpg", "small.jpg", 100, 100)
4050
+ ImageUtils.crop("input.jpg", "cropped.jpg", 10, 10, 50, 50)
4051
+ ImageUtils.rotate("input.jpg", "rotated.jpg", 90)
4052
+ """
4053
+
4054
+ @staticmethod
4055
+ def resize(input_path, output_path, width, height):
4056
+ """
4057
+ 📏 ИЗМЕНИТЬ РАЗМЕР: Изменяет размер изображения.
4058
+
4059
+ Параметры:
4060
+ input_path (str): путь к исходному изображению
4061
+ output_path (str): путь для сохранения
4062
+ width (int): новая ширина в пикселях
4063
+ height (int): новая высота в пикселях
4064
+
4065
+ 📝 Пример:
4066
+ ImageUtils.resize("photo.jpg", "photo_small.jpg", 200, 150)
4067
+ print("Миниатюра создана!")
4068
+
4069
+ 🎯 Возможности:
4070
+ - Создание миниатюр
4071
+ - Оптимизация размера для веба
4072
+ """
4073
+ from PIL import Image
4074
+ img = Image.open(input_path)
4075
+ img = img.resize((width, height))
4076
+ img.save(output_path)
4077
+
4078
+ @staticmethod
4079
+ def crop(input_path, output_path, left, top, right, bottom):
4080
+ """
4081
+ ✂️ ОБРЕЗАТЬ: Обрезает изображение по указанным границам.
4082
+
4083
+ Параметры:
4084
+ input_path (str): путь к исходному изображению
4085
+ output_path (str): путь для сохранения
4086
+ left, top, right, bottom (int): границы обрезки в пикселях
4087
+
4088
+ 📝 Пример:
4089
+ ImageUtils.crop("photo.jpg", "face.jpg", 100, 50, 300, 300)
4090
+ print("Обрезано!")
4091
+
4092
+ 🎯 Возможности:
4093
+ - Вырезание фрагментов изображений
4094
+ - Удаление лишних краёв
4095
+ """
4096
+ from PIL import Image
4097
+ img = Image.open(input_path)
4098
+ cropped = img.crop((left, top, right, bottom))
4099
+ cropped.save(output_path)
4100
+
4101
+ @staticmethod
4102
+ def rotate(input_path, output_path, degrees):
4103
+ """
4104
+ 🔄 ПОВЕРНУТЬ: Поворачивает изображение на указанный угол.
4105
+
4106
+ Параметры:
4107
+ input_path (str): путь к исходному изображению
4108
+ output_path (str): путь для сохранения
4109
+ degrees (int): угол поворота в градусах
4110
+
4111
+ 📝 Пример:
4112
+ ImageUtils.rotate("photo.jpg", "photo_90.jpg", 90)
4113
+ ImageUtils.rotate("photo.jpg", "photo_flip.jpg", 180)
4114
+
4115
+ 🎯 Возможности:
4116
+ - Исправление ориентации фото
4117
+ - Создание повёрнутых копий
4118
+ """
4119
+ from PIL import Image
4120
+ img = Image.open(input_path)
4121
+ rotated = img.rotate(degrees, expand=True)
4122
+ rotated.save(output_path)
4123
+
4124
+
4125
+ # =============================================================================
4126
+ # КЛАСС PDFUtils – ИЗВЛЕЧЕНИЕ ТЕКСТА ИЗ PDF (PyPDF2)
4127
+ # =============================================================================
4128
+ class PDFUtils:
4129
+ """
4130
+ 📑 ЧТЕНИЕ ТЕКСТА ИЗ PDF-ФАЙЛОВ.
4131
+
4132
+ Требуется установка: pip install PyPDF2
4133
+
4134
+ 📝 Пример:
4135
+ text = PDFUtils.extract_text("document.pdf")
4136
+ print(text)
4137
+ """
4138
+
4139
+ @staticmethod
4140
+ def extract_text(pdf_path: str) -> str:
4141
+ """
4142
+ 📄 ИЗВЛЕЧЬ ТЕКСТ: Извлекает весь текст из PDF-файла.
4143
+
4144
+ Параметры:
4145
+ pdf_path (str): путь к PDF-файлу
4146
+
4147
+ Возвращает:
4148
+ str: извлечённый текст (страницы разделены переносом строки)
4149
+
4150
+ 📝 Пример:
4151
+ text = PDFUtils.extract_text("report.pdf")
4152
+ print(f"В документе {len(text)} символов")
4153
+ print(text[:200]) # первые 200 символов
4154
+
4155
+ 🎯 Возможности:
4156
+ - Обработка документов
4157
+ - Извлечение данных из PDF-отчётов
4158
+ """
4159
+ import PyPDF2
4160
+ text = []
4161
+ with open(pdf_path, 'rb') as f:
4162
+ reader = PyPDF2.PdfReader(f)
4163
+ for page in reader.pages:
4164
+ text.append(page.extract_text() or '')
4165
+ return '\n'.join(text)
4166
+
4167
+
4168
+ # =============================================================================
4169
+ # КЛАСС PasswordGen – ГЕНЕРАТОР ПАРОЛЕЙ
4170
+ # =============================================================================
4171
+ class PasswordGen:
4172
+ """
4173
+ 🔐 ГЕНЕРАЦИЯ СЛУЧАЙНЫХ ПАРОЛЕЙ.
4174
+
4175
+ 📝 Примеры:
4176
+ PasswordGen.generate() # 12 символов, с цифрами и спецсимволами
4177
+ PasswordGen.generate(8, False) # только буквы, 8 символов
4178
+ PasswordGen.generate(16) # длинный надёжный пароль
4179
+ """
4180
+
4181
+ @staticmethod
4182
+ def generate(length=12, use_digits=True, use_special=True) -> str:
4183
+ """
4184
+ 🔑 СОЗДАТЬ ПАРОЛЬ: Генерирует случайный пароль из букв, цифр и спецсимволов.
4185
+
4186
+ Параметры:
4187
+ length (int): длина пароля (по умолчанию 12)
4188
+ use_digits (bool): включать ли цифры (0-9)
4189
+ use_special (bool): включать ли спецсимволы (!@#$%^&*)
4190
+
4191
+ Возвращает:
4192
+ str: сгенерированный пароль
4193
+
4194
+ 📝 Пример:
4195
+ pwd1 = PasswordGen.generate() # "aB3$xK9@mP2q"
4196
+ pwd2 = PasswordGen.generate(8, False) # "aBcDeFgH" (только буквы)
4197
+ pwd3 = PasswordGen.generate(16) # 16 символов, надёжный
4198
+
4199
+ 🎯 Возможности:
4200
+ - Создание паролей для новых аккаунтов
4201
+ - Генерация токенов и ключей
4202
+ - Настраиваемая сложность
4203
+ """
4204
+ chars = string.ascii_letters
4205
+ if use_digits:
4206
+ chars += string.digits
4207
+ if use_special:
4208
+ chars += "!@#$%^&*"
4209
+ return ''.join(random.choice(chars) for _ in range(length))
4210
+
4211
+
4212
+ # =============================================================================
4213
+ # КЛАСС UnitConverter – КОНВЕРТАЦИЯ ВЕЛИЧИН
4214
+ # =============================================================================
4215
+ class UnitConverter:
4216
+ """
4217
+ 📏 КОНВЕРТАЦИЯ МЕЖДУ ЕДИНИЦАМИ ИЗМЕРЕНИЯ.
4218
+
4219
+ 📝 Примеры:
4220
+ UnitConverter.celsius_to_fahrenheit(0) # 32.0
4221
+ UnitConverter.km_to_miles(10) # 6.21371
4222
+ UnitConverter.kg_to_lbs(5) # 11.0231
4223
+ """
4224
+
4225
+ @staticmethod
4226
+ def celsius_to_fahrenheit(c: float) -> float:
4227
+ """
4228
+ 🌡️ °C → °F: Переводит температуру из Цельсия в Фаренгейт.
4229
+
4230
+ Формула: F = C × 9/5 + 32
4231
+
4232
+ Параметры:
4233
+ c (float): температура в градусах Цельсия
4234
+
4235
+ Возвращает:
4236
+ float: температура в градусах Фаренгейта
4237
+
4238
+ 📝 Пример:
4239
+ UnitConverter.celsius_to_fahrenheit(0) # 32.0 (замерзание воды)
4240
+ UnitConverter.celsius_to_fahrenheit(100) # 212.0 (кипение воды)
4241
+ UnitConverter.celsius_to_fahrenheit(37) # 98.6 (температура тела)
4242
+ """
4243
+ return c * 9/5 + 32
4244
+
4245
+ @staticmethod
4246
+ def fahrenheit_to_celsius(f: float) -> float:
4247
+ """
4248
+ 🌡️ °F → °C: Переводит температуру из Фаренгейта в Цельсий.
4249
+
4250
+ Формула: C = (F - 32) × 5/9
4251
+
4252
+ Параметры:
4253
+ f (float): температура в градусах Фаренгейта
4254
+
4255
+ Возвращает:
4256
+ float: температура в градусах Цельсия
4257
+
4258
+ 📝 Пример:
4259
+ UnitConverter.fahrenheit_to_celsius(32) # 0.0
4260
+ UnitConverter.fahrenheit_to_celsius(212) # 100.0
4261
+ """
4262
+ return (f - 32) * 5/9
4263
+
4264
+ @staticmethod
4265
+ def km_to_miles(km: float) -> float:
4266
+ """
4267
+ 🛣️ КМ → МИЛИ: Переводит километры в мили.
4268
+
4269
+ Параметры:
4270
+ km (float): расстояние в километрах
4271
+
4272
+ Возвращает:
4273
+ float: расстояние в милях
4274
+
4275
+ 📝 Пример:
4276
+ UnitConverter.km_to_miles(10) # 6.21371
4277
+ UnitConverter.km_to_miles(100) # 62.1371
4278
+ """
4279
+ return km * 0.621371
4280
+
4281
+ @staticmethod
4282
+ def miles_to_km(miles: float) -> float:
4283
+ """
4284
+ 🛣️ МИЛИ → КМ: Переводит мили в километры.
4285
+
4286
+ Параметры:
4287
+ miles (float): расстояние в милях
4288
+
4289
+ Возвращает:
4290
+ float: расстояние в километрах
4291
+
4292
+ 📝 Пример:
4293
+ UnitConverter.miles_to_km(10) # 16.0934
4294
+ """
4295
+ return miles / 0.621371
4296
+
4297
+ @staticmethod
4298
+ def kg_to_lbs(kg: float) -> float:
4299
+ """
4300
+ ⚖️ КГ → ФУНТЫ: Переводит килограммы в фунты.
4301
+
4302
+ Параметры:
4303
+ kg (float): вес в килограммах
4304
+
4305
+ Возвращает:
4306
+ float: вес в фунтах
4307
+
4308
+ 📝 Пример:
4309
+ UnitConverter.kg_to_lbs(5) # 11.0231
4310
+ UnitConverter.kg_to_lbs(70) # 154.3234
4311
+ """
4312
+ return kg * 2.20462
4313
+
4314
+ @staticmethod
4315
+ def lbs_to_kg(lbs: float) -> float:
4316
+ """
4317
+ ⚖️ ФУНТЫ → КГ: Переводит фунты в килограммы.
4318
+
4319
+ Параметры:
4320
+ lbs (float): вес в фунтах
4321
+
4322
+ Возвращает:
4323
+ float: вес в килограммах
4324
+
4325
+ 📝 Пример:
4326
+ UnitConverter.lbs_to_kg(10) # 4.5359
4327
+ """
4328
+ return lbs / 2.20462
4329
+
4330
+
4331
+ # =============================================================================
4332
+ # ПРИМЕРЫ ИСПОЛЬЗОВАНИЯ
4333
+ # =============================================================================
4334
+ if __name__ == "__main__":
4335
+ Manager.open_file("test_help_manager.py")