import shlex import subprocess from PyQt6.QtCore import QThread, pyqtSignal from core.controllers.ApplicationVersionController import ApplicationVersionController from core.controllers.ClientController import ClientController from core.controllers.ConfigurationController import ConfigurationController from core.controllers.InvoiceController import InvoiceController from core.controllers.LocationController import LocationController from core.controllers.ProfileController import ProfileController from core.controllers.SubscriptionController import SubscriptionController from core.controllers.SubscriptionPlanController import SubscriptionPlanController from core.models.session.SessionConnection import SessionConnection from core.models.session.SessionProfile import SessionProfile from core.models.system.SystemConnection import SystemConnection from core.models.system.SystemProfile import SystemProfile from core.observers.ClientObserver import ClientObserver from core.observers.ConnectionObserver import ConnectionObserver from gui.v2.infrastructure.setup_observers import ( client_observer, connection_observer, invoice_observer, profile_observer, ) class WorkerThread(QThread): text_output = pyqtSignal(str) sync_output = pyqtSignal(list, list, bool, bool, list, list) invoice_output = pyqtSignal(object, str) invoice_finished = pyqtSignal(bool) profiles_output = pyqtSignal(dict) special_output = pyqtSignal(str) finished = pyqtSignal(bool) update_finished = pyqtSignal(dict) def __init__(self, action=None, profile_data=None, profile_type=None, package_name=None, package_command=None): super().__init__() self.action = action self.profile_data = profile_data self.profile_type = profile_type self.package_name = package_name self.package_command = package_command self.is_running = True self.is_disabling = False def run(self): if self.action == 'LIST_PROFILES': self.list_profiles() elif self.action == 'CREATE_SESSION_PROFILE': self.create_profile(self.profile_type) elif self.action == 'CREATE_SYSTEM_PROFILE': self.create_profile(self.profile_type) elif self.action == 'GET_SUBSCRIPTION': self.get_subscription() elif self.action == 'DISABLE_PROFILE': self.disable_profile() elif self.action == 'SYNC' or self.action == 'SYNC_TOR': self.sync() elif self.action == 'DESTROY_PROFILE': self.destroy_profile() elif self.action == 'DISABLE_ALL_PROFILES': self.disable_all_profiles() elif self.action == 'INSTALL_PACKAGE': self.install_package() elif self.action == 'CHECK_FOR_UPDATE': self.check_for_update() elif self.action == 'DOWNLOAD_UPDATE': self.download_update() elif self.action == 'CHECK_INVOICE_STATUS': self.check_invoice_status() def check_invoice_status(self): try: invoice = InvoiceController.get(self.profile_data['billing_code']) if invoice: status = invoice.status if status == "expired": self.invoice_finished.emit(False) else: self.invoice_finished.emit(True) else: self.invoice_finished.emit(False) except Exception as e: print(f"Error retrieving invoice: {str(e)}") self.invoice_finished.emit(False) def download_update(self): self.text_output.emit("Starting update process...") ClientController.update(client_observer=client_observer) client_observer.subscribe('update_progressing', lambda event: self.text_output.emit( f"Downloading: {event.meta.get('progress'):.1f}%")) client_observer.subscribe( 'updated', lambda event: self.text_output.emit("Update process completed")) def check_for_update(self): self.text_output.emit("Checking for updates...") ClientController.sync(client_observer=client_observer, connection_observer=connection_observer) update_available = ClientController.can_be_updated() if update_available: self.text_output.emit("An update is available. Downloading...") self.finished.emit(True) else: self.text_output.emit("No updates available.") self.finished.emit(False) def install_package(self): try: self.text_output.emit(f"Installing {self.package_name}...") subprocess.run(shlex.split(self.package_command), check=True) self.text_output.emit(f"{self.package_name} installed!") self.finished.emit(True) except subprocess.CalledProcessError: self.text_output.emit("Installation failed") self.finished.emit(False) except Exception as e: self.text_output.emit( f"An error occurred when installing {self.package_name}: {e}") self.finished.emit(False) def disable_all_profiles(self): try: for profile_id in self.profile_data: profile = ProfileController.get(int(profile_id)) if isinstance(profile, SessionProfile): ProfileController.disable( profile, ignore=True, profile_observer=profile_observer) for profile_id in self.profile_data: profile = ProfileController.get(int(profile_id)) if isinstance(profile, SystemProfile): ProfileController.disable( profile, ignore=True, profile_observer=profile_observer) self.text_output.emit("All profiles were successfully disabled") except Exception: self.text_output.emit("An error occurred when disabling profile") finally: self.finished.emit(True) def destroy_profile(self): profile_id = int(self.profile_data['id']) profile = ProfileController.get(profile_id) if profile is not None: try: ProfileController.destroy(profile) except Exception as e: error_name = type(e).__name__ error_text = str(e) or 'Unknown deletion error' self.text_output.emit( f'Could not delete profile {profile_id}: {error_name}: {error_text}') self.finished.emit(False) return self.text_output.emit(f'Profile {profile_id} deleted') self.finished.emit(True) else: self.text_output.emit(f'Profile {profile_id} does not exist') self.finished.emit(False) def list_profiles(self): profiles = ProfileController.get_all() self.profiles_output.emit(profiles) def create_profile(self, profile_type): location = LocationController.get( self.profile_data['country_code'], self.profile_data['code']) if location is None: self.text_output.emit( f"Invalid location code: {self.profile_data['location_code']}") return profile_id = int( self.profile_data['id']) if self.profile_data['id'] else None name = self.profile_data['name'] connection_type = self.profile_data['connection_type'] if profile_type == "session": application_details = self.profile_data['application'].split( ':', 1) application_version = ApplicationVersionController.get( application_details[0], application_details[1] if len(application_details) > 1 else None) if application_version is None: self.text_output.emit( f"Invalid application: {self.profile_data['application']}") return mask_connection = True if connection_type == 'tor' or connection_type == 'system' else False resolution = self.profile_data['resolution'] connection = SessionConnection(connection_type, mask_connection) profile = SessionProfile( profile_id, name, None, location, resolution, application_version, connection) elif profile_type == "system": connection = SystemConnection(connection_type) profile = SystemProfile( profile_id, name, None, location, connection) else: self.text_output.emit(f"Invalid profile type: {profile_type}") return try: ProfileController.create( profile, profile_observer=profile_observer) except Exception as e: self.text_output.emit( f"An error occurred when creating profile {profile.id}") self.text_output.emit( f"{profile_type.capitalize()} Profile created with ID: {profile.id}") def disable_profile(self): try: profile = ProfileController.get(int(self.profile_data['id'])) if profile: ProfileController.disable( profile, profile_observer=profile_observer) else: self.text_output.emit( f"No profile found with ID: {self.profile_data['id']}") except Exception as e: self.text_output.emit("An error occurred when disabling profile") finally: self.finished.emit(True) def _emit_observer_message(self, topic, event): subject = getattr(event, 'subject', None) meta = getattr(event, 'meta', None) if subject is not None: self.text_output.emit(str(subject)) elif meta: self.text_output.emit(str(meta)) else: self.text_output.emit(str(topic)) def _subscribe_observer_messages(self, observer, skip_topics=None): skip_topics = skip_topics or set() for attr in dir(observer): if not attr.startswith('on_'): continue callbacks = getattr(observer, attr, None) if not isinstance(callbacks, list): continue topic = attr[3:] if topic in skip_topics: continue observer.subscribe( topic, lambda event, topic=topic: self._emit_observer_message(topic, event)) def sync(self): try: if self.action == 'SYNC_TOR': ConfigurationController.set_connection('tor') else: ConfigurationController.set_connection('system') sync_succeeded = False def sync_complete(event): nonlocal sync_succeeded sync_succeeded = True self._emit_observer_message('synchronized', event) sync_client_observer = ClientObserver() sync_connection_observer = ConnectionObserver() self._subscribe_observer_messages( sync_client_observer, {'synchronized'}) self._subscribe_observer_messages(sync_connection_observer) sync_client_observer.subscribe('synchronized', sync_complete) ClientController.sync( client_observer=sync_client_observer, connection_observer=sync_connection_observer) if not sync_succeeded: self.sync_output.emit([], [], False, False, [], []) self.finished.emit(False) return locations = LocationController.get_all() browser = ApplicationVersionController.get_all() all_browser_versions = [ f"{browser.application_code}:{browser.version_number}" for browser in browser if browser.supported] all_location_codes = [ f"{location.country_code}_{location.code}" for location in locations] if not all_location_codes or not all_browser_versions or not locations or not browser: self.sync_output.emit([], [], False, False, [], []) self.finished.emit(False) return self.sync_output.emit( all_location_codes, all_browser_versions, True, False, locations, browser) self.finished.emit(True) except Exception as e: print(f'the error is: {e}') self.sync_output.emit([], [], False, False, [], []) self.finished.emit(False) def get_connection(self): connection = ConfigurationController.get_connection() self.text_output.emit(f"Current connection: {connection}") def set_connection(self): ConfigurationController.set_connection( self.profile_data['connection_type']) self.text_output.emit( f"Connection set to '{self.profile_data['connection_type']}'") def get_subscription(self): try: invoice_observer.subscribe('retrieved', lambda event: self.handle_event( event, self.profile_data['currency'])) invoice_observer.subscribe('processing', lambda event: self.text_output.emit( 'A payment has been detected and is being verified...')) invoice_observer.subscribe('settled', lambda event: self.text_output.emit( 'The payment has been successfully verified.')) profile = ProfileController.get(int(self.profile_data['id'])) subscription_plan = SubscriptionPlanController.get( profile.connection, self.profile_data['duration']) if subscription_plan is None: self.text_output.emit( 'No compatible subscription plan was found.') return potential_subscription = SubscriptionController.create( subscription_plan, profile, connection_observer=connection_observer) if potential_subscription is not None: ProfileController.attach_subscription( profile, potential_subscription) else: self.text_output.emit( 'The subscription could not be created. Try again later.') return subscription = InvoiceController.handle_payment( potential_subscription.billing_code, invoice_observer=invoice_observer, connection_observer=connection_observer) if subscription is not None: ProfileController.attach_subscription(profile, subscription) self.text_output.emit( 'Successfully activated the subscription') self.invoice_finished.emit(True) else: self.text_output.emit( 'The subscription could not be activated. Try again later.') self.invoice_finished.emit(False) except Exception as e: self.text_output.emit('An unknown error occurred') self.invoice_finished.emit(False) def handle_connection_events(self, event): self.text_output.emit(f'Profile disabled') def handle_event(self, event, currency=None): invoice = event.subject if isinstance(invoice, object): self.invoice_output.emit(invoice, '') self.text_output.emit("Invoice generated. Awaiting payment...") else: self.text_output.emit("Invalid invoice data received.")