import sys
import platform
import asyncio
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from bleak import BleakScanner, BleakClient
from PyQt5.QtWidgets import (QApplication, QWidget, QLabel, QLineEdit,
                             QPushButton, QVBoxLayout, QTextEdit, QComboBox,
                             QMenuBar, QAction, QMessageBox, QListWidget, QHBoxLayout, QSplitter, QStyledItemDelegate, QListWidgetItem)
from PyQt5.QtCore import QThread, pyqtSignal, Qt, QPropertyAnimation, QEasingCurve, QTimer
from PyQt5.QtGui import QColor, QFont, QPixmap
import sqlite3
import os

# Генерация ключа для шифрования AES
key = AESGCM.generate_key(bit_length=128)
aesgcm = AESGCM(key)
nonce = b"unique_nonce12"

# Функция для шифрования сообщения
def encrypt_message(message):
    return aesgcm.encrypt(nonce, message.encode(), None)

# Функция для дешифрования сообщения
def decrypt_message(encrypted_message):
    return aesgcm.decrypt(nonce, encrypted_message, None).decode()

# Функция для сканирования устройств
async def scan_devices():
    while True:
        devices = await BleakScanner.discover()
        device_list = [(d.address, d.name) for d in devices]
        yield device_list
        await asyncio.sleep(10)

# Функция для отправки сообщения
async def send_message(address, message):
    try:
        async with BleakClient(address) as client:
            encrypted_message = encrypt_message(message)
            await client.write_gatt_char("<Ваш-UUID-характера>", encrypted_message)
            print(f"Сообщение отправлено на {address}")
            save_message(address, message, "sent")
            show_notification(f"Сообщение отправлено на {address}")
    except Exception as e:
        print(f"Не удалось отправить сообщение на {address}: {e}")

# Функция для получения сообщения
async def receive_message(address):
    try:
        async with BleakClient(address) as client:
            encrypted_message = await client.read_gatt_char("<Ваш-UUID-характера>")
            decrypted_message = decrypt_message(encrypted_message)
            save_message(address, decrypted_message, "received")
            show_notification(f"Получено сообщение от {address}")
            return decrypted_message
    except Exception as e:
        print(f"Не удалось получить сообщение от {address}: {e}")

# Функция для сохранения сообщения в базу данных
def save_message(address, message, direction):
    conn = sqlite3.connect('messages.db')
    c = conn.cursor()
    c.execute('''CREATE TABLE IF NOT EXISTS messages
                 (address TEXT, message TEXT, direction TEXT, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP)''')
    c.execute("INSERT INTO messages (address, message, direction) VALUES (?, ?, ?)", (address, message, direction))
    conn.commit()
    conn.close()

# Функция для отображения уведомлений
def show_notification(message):
    if platform.system() == "Windows":
        from win10toast import ToastNotifier
        toaster = ToastNotifier()
        toaster.show_toast("Bluetooth Messenger", message, duration=5)
    elif platform.system() == "Linux":
        import subprocess
        subprocess.run(["notify-send", "Bluetooth Messenger", message])
    elif platform.system() == "Android":
        import subprocess
        subprocess.run(["termux-toast", message])

# Класс для потока сканирования
class ScanThread(QThread):
    update_signal = pyqtSignal(list)

    def run(self):
        loop = asyncio.new_event_loop()
        asyncio.set_event_loop(loop)
        loop.run_until_complete(self.scan_devices())

    async def scan_devices(self):
        async for device_list in scan_devices():
            self.update_signal.emit(device_list)

# Класс для основного окна приложения
class MainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Bluetooth Messenger')
        self.setGeometry(100, 100, 800, 600)

        self.layout = QVBoxLayout()

        self.device_label = QLabel('Выберите устройство:')
        self.device_combo = QComboBox(self)
        self.message_label = QLabel('Сообщение:')
        self.message_input = QLineEdit(self)
        self.send_button = QPushButton('Отправить', self)
        self.send_button.clicked.connect(self.send_message)
        self.output_text = AnimatedTextEdit(self)
        self.output_text.setReadOnly(True)

        self.chat_list = AnimatedListWidget(self)
        self.chat_list.itemClicked.connect(self.load_chat)

        self.splitter = QSplitter(Qt.Horizontal)
        self.splitter.addWidget(self.chat_list)
        self.splitter.addWidget(self.output_text)

        self.layout.addWidget(self.device_label)
        self.layout.addWidget(self.device_combo)
        self.layout.addWidget(self.message_label)
        self.layout.addWidget(self.message_input)
        self.layout.addWidget(self.send_button)
        self.layout.addWidget(self.splitter)

        self.setLayout(self.layout)

        self.scan_thread = ScanThread()
        self.scan_thread.update_signal.connect(self.update_device_list)
        self.scan_thread.start()

        self.create_menu()

    def create_menu(self):
        menubar = QMenuBar(self)
        file_menu = menubar.addMenu('Файл')

        save_action = QAction('Сохранить переписку', self)
        save_action.triggered.connect(self.save_conversation)
        file_menu.addAction(save_action)

        exit_action = QAction('Выход', self)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        self.layout.setMenuBar(menubar)

    def save_conversation(self):
        # Логика для сохранения переписки
        dialog = QMessageBox.information(
            self, "Сохранение", "Переписка сохранена!")

    def send_message(self):
        address = self.device_combo.currentData()
        message = self.message_input.text()
        if address and message:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            loop.run_until_complete(send_message(address, message))
            self.output_text.append(f"Сообщение отправлено на {address}")
            # Уведомление о отправке сообщения
            QMessageBox.information(self, "Уведомление", f"Сообщение отправлено на {address}")
        else:
            QMessageBox.warning(self, 'Ошибка', 'Пожалуйста, выберите устройство и введите сообщение.')

    def update_device_list(self, device_list):
        self.device_combo.clear()
        for address, name in device_list:
            self.device_combo.addItem(f"{name} ({address})", address)

    def load_chat(self, item):
        address = item.data(Qt.UserRole)
        conn = sqlite3.connect('messages.db')
        c = conn.cursor()
        c.execute("SELECT message, direction FROM messages WHERE address = ? ORDER BY timestamp", (address,))
        messages = c.fetchall()
        conn.close()

        self.output_text.clear()
        for message, direction in messages:
            if direction == "sent":
                self.output_text.append(f"Вы: {message}")
            else:
                self.output_text.append(f"{address}: {message}")

# Класс для анимации
class AnimatedWidget(QWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.animation = QPropertyAnimation(self, b"windowOpacity")
        self.animation.setDuration(1000)
        self.animation.setStartValue(0)
        self.animation.setEndValue(1)
        self.animation.setEasingCurve(QEasingCurve.InOutQuad)
        self.animation.start()

# Класс для делегата элементов списка
class ChatListDelegate(QStyledItemDelegate):
    def paint(self, painter, option, index):
        painter.save()
        painter.setPen(QColor(0, 0, 0))
        painter.drawText(option.rect, Qt.AlignLeft, index.data())
        painter.restore()

# Класс для анимации появления элементов списка
class AnimatedListWidget(QListWidget):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.setItemDelegate(ChatListDelegate())
        self.animation_timer = QTimer(self)
        self.animation_timer.timeout.connect(self.animate_items)
        self.animation_timer.start(50)

    def animate_items(self):
        for i in range(self.count()):
            item = self.item(i)
            if not hasattr(item, 'opacity'):
                item.opacity = 0
            if item.opacity < 1:
                item.opacity += 0.1
                item.setBackground(QColor(255, 255, 255, int(item.opacity * 255)))
            else:
                self.animation_timer.stop()

# Класс для анимации появления текста
class AnimatedTextEdit(QTextEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.animation = QPropertyAnimation(self, b"windowOpacity")
        self.animation.setDuration(1000)
        self.animation.setStartValue(0)
        self.animation.setEndValue(1)
        self.animation.setEasingCurve(QEasingCurve.InOutQuad)
        self.animation.start()

# Класс для анимации появления кнопок
class AnimatedButton(QPushButton):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)
        self.animation = QPropertyAnimation(self, b"windowOpacity")
        self.animation.setDuration(1000)
        self.animation.setStartValue(0)
        self.animation.setEndValue(1)
        self.animation.setEasingCurve(QEasingCurve.InOutQuad)
        self.animation.start()

# Класс для анимации появления метки
class AnimatedLabel(QLabel):
    def __init__(self, text, parent=None):
        super().__init__(text, parent)
        self.animation = QPropertyAnimation(self, b"windowOpacity")
        self.animation.setDuration(1000)
        self.animation.setStartValue(0)
        self.animation.setEndValue(1)
        self.animation.setEasingCurve(QEasingCurve.InOutQuad)
        self.animation.start()

# Класс для анимации появления поля ввода
class AnimatedLineEdit(QLineEdit):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.animation = QPropertyAnimation(self, b"windowOpacity")
        self.animation.setDuration(1000)
        self.animation.setStartValue(0)
        self.animation.setEndValue(1)
        self.animation.setEasingCurve(QEasingCurve.InOutQuad)
        self.animation.start()

# Класс для анимации появления комбобокса
class AnimatedComboBox(QComboBox):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.animation = QPropertyAnimation(self, b"windowOpacity")
        self.animation.setDuration(1000)
        self.animation.setStartValue(0)
        self.animation.setEndValue(1)
        self.animation.setEasingCurve(QEasingCurve.InOutQuad)
        self.animation.start()

# Класс для анимации появления меню
class AnimatedMenuBar(QMenuBar):
    def __init__(self, parent=None):
        super().__init__(parent)
        self.animation = QPropertyAnimation(self, b"windowOpacity")
        self.animation.setDuration(1000)
        self.animation.setStartValue(0)
        self.animation.setEndValue(1)
        self.animation.setEasingCurve(QEasingCurve.InOutQuad)
        self.animation.start()

# Класс для анимации появления окна
class AnimatedMainWindow(QWidget):
    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setWindowTitle('Bluetooth Messenger')
        self.setGeometry(100, 100, 800, 600)

        self.layout = QVBoxLayout()

        self.device_label = AnimatedLabel('Выберите устройство:', self)
        self.device_combo = AnimatedComboBox(self)
        self.message_label = AnimatedLabel('Сообщение:', self)
        self.message_input = AnimatedLineEdit(self)
        self.send_button = AnimatedButton('Отправить', self)
        self.send_button.clicked.connect(self.send_message)
        self.output_text = AnimatedTextEdit(self)
        self.output_text.setReadOnly(True)

        self.chat_list = AnimatedListWidget(self)
        self.chat_list.itemClicked.connect(self.load_chat)

        self.splitter = QSplitter(Qt.Horizontal)
        self.splitter.addWidget(self.chat_list)
        self.splitter.addWidget(self.output_text)

        self.layout.addWidget(self.device_label)
        self.layout.addWidget(self.device_combo)
        self.layout.addWidget(self.message_label)
        self.layout.addWidget(self.message_input)
        self.layout.addWidget(self.send_button)
        self.layout.addWidget(self.splitter)

        self.setLayout(self.layout)

        self.scan_thread = ScanThread()
        self.scan_thread.update_signal.connect(self.update_device_list)
        self.scan_thread.start()

        self.create_menu()

    def create_menu(self):
        menubar = AnimatedMenuBar(self)
        file_menu = menubar.addMenu('Файл')

        save_action = QAction('Сохранить переписку', self)
        save_action.triggered.connect(self.save_conversation)
        file_menu.addAction(save_action)

        exit_action = QAction('Выход', self)
        exit_action.triggered.connect(self.close)
        file_menu.addAction(exit_action)

        self.layout.setMenuBar(menubar)

    def save_conversation(self):
        # Логика для сохранения переписки
        dialog = QMessageBox.information(
            self, "Сохранение", "Переписка сохранена!")

    def send_message(self):
        address = self.device_combo.currentData()
        message = self.message_input.text()
        if address and message:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            loop.run_until_complete(send_message(address, message))
            self.output_text.append(f"Сообщение отправлено на {address}")
            # Уведомление о отправке сообщения
            QMessageBox.information(self, "Уведомление", f"Сообщение отправлено на {address}")
        else:
            QMessageBox.warning(self, 'Ошибка', 'Пожалуйста, выберите устройство и введите сообщение.')

    def update_device_list(self, device_list):
        self.device_combo.clear()
        for address, name in device_list:
            self.device_combo.addItem(f"{name} ({address})", address)

    def load_chat(self, item):
        address = item.data(Qt.UserRole)
        conn = sqlite3.connect('messages.db')
        c = conn.cursor()
        c.execute("SELECT message, direction FROM messages WHERE address = ? ORDER BY timestamp", (address,))
        messages = c.fetchall()
        conn.close()

        self.output_text.clear()
        for message, direction in messages:
            if direction == "sent":
                self.output_text.append(f"Вы: {message}")
            else:
                self.output_text.append(f"{address}: {message}")

if __name__ == '__main__':
    app = QApplication(sys.argv)
    main_window = AnimatedMainWindow()
    main_window.show()
    sys.exit(app.exec_())



ДОБАВЬ и ИЗМЕНИ!!!
дата сообщения, все функции тг
улучшить безопасность
доступ к уведомлениям телефона
кнопка для полного удаления приложения и всех переписок



некоторые меню сломались, а так же мне не нравится дизайн

как мне это перенести на ANDOROID, если некоторые библиотеки не работают на нем?? ВЫБЕРИ БИБЛИОТЕКИ КОТОРЫЕ 100%  СОВМЕСТИМЫ С ANDOROID


некоторые меню сломались, а так же мне не нравится дизайн

как мне это перенести на ANDOROID, если некоторые библиотеки не работают на нем?? ВЫБЕРИ БИБЛИОТЕКИ КОТОРЫЕ 100%  СОВМЕСТИМЫ С ANDOROID

СДЕЛАЙ ТАК ЧТОБЫ СКРИПТ ПРОВЕРЯЛ, ЕСТЬ ЛИ У ПОЛЬЗОВАТЕЛЯ ТАКОЙ ЖЕ КЛИНЕТ ДЛЯ ОБЩЕНИЯ, ЕСЛИ НЕТУ, ТО ОБЩЕНИЯ НЕ БУДЕТ. ИСПРАВЬ ВСЕ ОШИБКИ, УЛУЧШИ САМ КОД.
ПОСТАВЬ БИБЛИОТЕТКУ ДЛЯ ОБЕСПЕЧЕНИЯ БЕЗОПАСНОСТИ ПРИ РАБОТЕ С ПРОГРАММОЙ. УЛУЧШИ ИНТРЕФЕЙС (АНИМАЦИИ) 
ДОБАВЬ ВОЗМОЖНОСТЬ С ОДНОГО УТСТРОЙСТВА ПРОВЕРИТЬ РАБОТУ СКРИПТА!!!!!!!
НЕ ОГРАНИЧИВАЙ СЕБЯ В КОЛИЧЕСТВЕ СИМВОЛОВ И СТРОК, СДЕЛАЙ СКРИПТ НА БОЛЕЕ ЧЕМ 50 ТЫСЯЧ ТОКЕНОВ!!!!!!!!!

Интерфейс как у настоящих мессенджеров (с чатами и ТД)











СДЕЛАЙ СОХРОНЕНИЕ ПЕРЕПИСОК, ШИФРОВАНИЕ ЧЕРЕЗ ДРУГУЮ БИБЛИОТЕКУ, РАБОТУ ТОЛЬКО НА Windows, Linux и Android(Termux). ДОБАВЬ УВЕДОМЛЕНИЯ ДЛЯ КАЖДОЙ ПЛАТФОРМЫ







перепиши код выше он должен: "Bluetooth messenger", который будет искать людей с таким же мессенджером в радиусе 100 метров, пересылать сообщения и уведомления, ДЛЯ WINDOWS и ANDROID. ИСПОЛЬЗУЙ ТАК ЖЕ ШИФРОВАНИЕ ДЛЯ ОБЩЕНИЕ МЕЖДУ ЛЮДЬМИ. МЕССЕНДЖЕР ПО ТИПУ BRAIR. СДЕЛАЙ ДЕСТВИТЕЛЬНО ДОСТОЙНЫЙ ПРОДУКТ НА 500 СТРОК ИЛИ 50к ТОКЕНОВ!!!, ты МОЖЕШЬ ЭТО СДЕЛАТЬ!!!!!

не используй библиотеки: pybluez и bluetooth

СДЕЛАЙ В ВИДЕ ОДНОГО ФАЙЛА И С АВТООПРЕДЕЛЕНИЕ ТИПА OS






УЛУЧШИ ИНТЕРФЕЙС, СДЕЛАЙ УДОБНЕЕ МЕНЮ, ВЫБОР УСТРОЙСТВА


ТЫ ВООБЩЕ НЕ ЧЕГО НЕ ИЗМЕНИЛ. ИЗМЕНИ ПОЛНОСТЬЮ ИНТЕРФЕЙС. МНЕ НУЖЕН ПО НАСТОЯЩЕНМУ КРАСИВЫЙ ИНТЕРФЕЙС

ДОБАВЬ НАСТРОЙКИ С ВЫБОРОМ ЦВЕТА И ТД. (ПРИДУМАЙ САМ, ЧТО ДОБАВИТЬ В МЕНЮ)