receive confirmation

This commit is contained in:
N0\A
2025-10-24 19:14:13 +02:00
parent a0417d7267
commit 9b20fa745e
2 changed files with 98 additions and 16 deletions

View File

@@ -40,6 +40,7 @@ class DuktoProtocol:
# Callbacks # Callbacks
self.on_peer_added: Optional[Callable[[Peer], None]] = None self.on_peer_added: Optional[Callable[[Peer], None]] = None
self.on_peer_removed: Optional[Callable[[Peer], None]] = None self.on_peer_removed: Optional[Callable[[Peer], None]] = None
self.on_receive_request: Optional[Callable[[str, int, int], bool]] = None
self.on_receive_start: Optional[Callable[[str], None]] = None self.on_receive_start: Optional[Callable[[str], None]] = None
self.on_receive_complete: Optional[Callable[[List[str], int], None]] = None self.on_receive_complete: Optional[Callable[[List[str], int], None]] = None
self.on_receive_text: Optional[Callable[[str, int], None]] = None self.on_receive_text: Optional[Callable[[str, int], None]] = None
@@ -192,29 +193,45 @@ class DuktoProtocol:
conn.close() conn.close()
continue continue
threading.Thread(target=self._receive_files, threading.Thread(target=self._handle_connection_request,
args=(conn, addr[0]), daemon=True).start() args=(conn, addr[0]), daemon=True).start()
except Exception as e: except Exception as e:
if self.running: if self.running:
print(f"TCP listener error: {e}") print(f"TCP listener error: {e}")
def _receive_files(self, conn: socket.socket, sender_ip: str): def _handle_connection_request(self, conn: socket.socket, sender_ip: str):
try:
conn.settimeout(10)
header = conn.recv(16)
if len(header) < 16:
conn.close()
return
elements_count = struct.unpack('<Q', header[0:8])[0]
total_size = struct.unpack('<Q', header[8:16])[0]
accept_transfer = True
if self.on_receive_request:
accept_transfer = self.on_receive_request(sender_ip, elements_count, total_size)
if accept_transfer:
self._receive_files(conn, sender_ip, elements_count, total_size)
else:
conn.close()
except Exception as e:
if self.on_error:
self.on_error(f"Connection request error: {e}")
conn.close()
def _receive_files(self, conn: socket.socket, sender_ip: str, elements_count: int, total_size: int):
self.is_receiving = True self.is_receiving = True
if self.on_receive_start: if self.on_receive_start:
self.on_receive_start(sender_ip) self.on_receive_start(sender_ip)
try: try:
conn.settimeout(10)
# Read header
header = conn.recv(16)
if len(header) < 16:
return
elements_count = struct.unpack('<Q', header[0:8])[0]
total_size = struct.unpack('<Q', header[8:16])[0]
conn.settimeout(None) conn.settimeout(None)
received_files = [] received_files = []

71
main.py
View File

@@ -1,5 +1,5 @@
#!/usr/bin/python3 #!/usr/bin/python3
import sys, os, subprocess import sys, os, subprocess, threading
from pathlib import Path from pathlib import Path
from PySide6 import QtCore, QtGui, QtWidgets from PySide6 import QtCore, QtGui, QtWidgets
from pynput import keyboard from pynput import keyboard
@@ -9,6 +9,7 @@ from core.web_search import MullvadLetaWrapper
from core.discord_presence import presence from core.discord_presence import presence
from core.app_launcher import list_apps, launch from core.app_launcher import list_apps, launch
from core.updater import update_repository, is_update_available from core.updater import update_repository, is_update_available
from core.dukto import DuktoProtocol
ASSET = Path(__file__).parent / "assets" / "2ktan.png" ASSET = Path(__file__).parent / "assets" / "2ktan.png"
@@ -316,10 +317,39 @@ class WebSearchResults(QtWidgets.QDialog):
QtWidgets.QApplication.restoreOverrideCursor() QtWidgets.QApplication.restoreOverrideCursor()
class ReceiveConfirmationDialog(QtWidgets.QDialog):
def __init__(self, sender_ip, file_count, total_size, parent=None):
super().__init__(parent)
self.setWindowTitle("Incoming Transfer")
size_mb = total_size / (1024 * 1024)
layout = QtWidgets.QVBoxLayout()
message = (
f"Incoming transfer request from {sender_ip}\n\n"
f"Files: {file_count}\n"
f"Total size: {size_mb:.2f} MB\n\n"
"Do you want to accept?"
)
label = QtWidgets.QLabel(message)
layout.addWidget(label)
button_box = QtWidgets.QDialogButtonBox(
QtWidgets.QDialogButtonBox.StandardButton.Yes | QtWidgets.QDialogButtonBox.StandardButton.No
)
button_box.accepted.connect(self.accept)
button_box.rejected.connect(self.reject)
layout.addWidget(button_box)
self.setLayout(layout)
class MainWindow(QtWidgets.QMainWindow): class MainWindow(QtWidgets.QMainWindow):
show_menu_signal = QtCore.Signal() show_menu_signal = QtCore.Signal()
def __init__(self, restart=False, no_quit=False, super_menu=True): def __init__(self, dukto_handler, restart=False, no_quit=False, super_menu=True):
super().__init__() super().__init__()
flags = ( flags = (
@@ -344,6 +374,11 @@ class MainWindow(QtWidgets.QMainWindow):
self.setMask(mask) self.setMask(mask)
self.super_menu = super_menu self.super_menu = super_menu
self.dukto_handler = dukto_handler
self.dukto_handler.on_receive_request = self.handle_receive_request
self.receive_confirmation_event = threading.Event()
self.receive_confirmation_result = [False]
self.tray = QtWidgets.QSystemTrayIcon(self) self.tray = QtWidgets.QSystemTrayIcon(self)
self.tray.setIcon(QtGui.QIcon(str(ASSET))) self.tray.setIcon(QtGui.QIcon(str(ASSET)))
@@ -517,8 +552,30 @@ class MainWindow(QtWidgets.QMainWindow):
elif status == "FAILED": elif status == "FAILED":
QtWidgets.QMessageBox.critical(self, "Update Failed", message) QtWidgets.QMessageBox.critical(self, "Update Failed", message)
@QtCore.Slot(str, int, int)
def show_receive_dialog(self, sender_ip, file_count, total_size):
dialog = ReceiveConfirmationDialog(sender_ip, file_count, total_size, self)
result = dialog.exec()
self.receive_confirmation_result[0] = (result == QtWidgets.QDialog.Accepted)
self.receive_confirmation_event.set()
def handle_receive_request(self, sender_ip, file_count, total_size) -> bool:
self.receive_confirmation_event.clear()
QtCore.QMetaObject.invokeMethod(
self, "show_receive_dialog", QtCore.Qt.QueuedConnection, #type: ignore
QtCore.Q_ARG(str, sender_ip),
QtCore.Q_ARG(int, file_count),
QtCore.Q_ARG(int, total_size),
)
self.receive_confirmation_event.wait()
return self.receive_confirmation_result[0]
def restart_application(self): def restart_application(self):
presence.end() presence.end()
self.dukto_handler.shutdown()
args = [sys.executable] + sys.argv args = [sys.executable] + sys.argv
@@ -534,9 +591,16 @@ def main():
restart = "--restart" in sys.argv restart = "--restart" in sys.argv
no_quit = "--no-quit" in sys.argv no_quit = "--no-quit" in sys.argv
super_menu = not "--no-super" in sys.argv super_menu = not "--no-super" in sys.argv
pet = MainWindow(restart=restart, no_quit=no_quit, super_menu=super_menu)
dukto_handler = DuktoProtocol()
pet = MainWindow(dukto_handler=dukto_handler, restart=restart, no_quit=no_quit, super_menu=super_menu)
presence.start() presence.start()
dukto_handler.initialize()
dukto_handler.say_hello()
# bottom right corner # bottom right corner
screen_geometry = app.primaryScreen().availableGeometry() screen_geometry = app.primaryScreen().availableGeometry()
pet_geometry = pet.frameGeometry() pet_geometry = pet.frameGeometry()
@@ -547,6 +611,7 @@ def main():
pet.show() pet.show()
app.aboutToQuit.connect(presence.end) app.aboutToQuit.connect(presence.end)
app.aboutToQuit.connect(dukto_handler.shutdown)
sys.exit(app.exec()) sys.exit(app.exec())