Compare commits
No commits in common. "8a0a1fc74d8f67e498500bd05d4ce89bfb42dc03" and "2258eda245d93a80f82efc534107d7612711a4d5" have entirely different histories.
8a0a1fc74d
...
2258eda245
|
|
@ -61,7 +61,6 @@ from gui.v2.actions.profile_data import (
|
||||||
clear_profile_data,
|
clear_profile_data,
|
||||||
)
|
)
|
||||||
from gui.v2.actions.database_health import GuiStorageDatabaseError
|
from gui.v2.actions.database_health import GuiStorageDatabaseError
|
||||||
from gui.v2.actions.profile_status import is_profile_enabled_for_gui
|
|
||||||
from gui.v2.actions.ticket_failure import (
|
from gui.v2.actions.ticket_failure import (
|
||||||
save_ticket_verification_failure,
|
save_ticket_verification_failure,
|
||||||
get_ticket_verification_failure,
|
get_ticket_verification_failure,
|
||||||
|
|
@ -675,7 +674,7 @@ class CustomWindow(QMainWindow):
|
||||||
self.connection_manager._connected_profiles.clear()
|
self.connection_manager._connected_profiles.clear()
|
||||||
for profile_id, profile in profiles.items():
|
for profile_id, profile in profiles.items():
|
||||||
try:
|
try:
|
||||||
if is_profile_enabled_for_gui(profile):
|
if ProfileController.is_enabled(profile):
|
||||||
connected_profiles.append(profile_id)
|
connected_profiles.append(profile_id)
|
||||||
self.connection_manager.add_connected_profile(profile_id)
|
self.connection_manager.add_connected_profile(profile_id)
|
||||||
except Exception:
|
except Exception:
|
||||||
|
|
|
||||||
BIN
gui/resources/images/hysteri_mini_icon.png
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
gui/resources/images/hysteria2_nl_li.png
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
gui/resources/images/hysteria2_us_ny.png
Normal file
|
After Width: | Height: | Size: 258 KiB |
BIN
gui/resources/images/hysteria2_us_wa.png
Normal file
|
After Width: | Height: | Size: 259 KiB |
BIN
gui/resources/images/vless_icon.png
Normal file
|
After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
BIN
gui/resources/images/vless_mini_transparent.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
gui/resources/images/vless_mini_transparent_white.png
Normal file
|
After Width: | Height: | Size: 58 KiB |
BIN
gui/resources/images/vless_nl_li.png
Normal file
|
After Width: | Height: | Size: 220 KiB |
BIN
gui/resources/images/vless_us_ny.png
Normal file
|
After Width: | Height: | Size: 219 KiB |
BIN
gui/resources/images/vless_us_wa.png
Normal file
|
After Width: | Height: | Size: 220 KiB |
|
|
@ -1,27 +0,0 @@
|
||||||
from core.controllers.ProfileController import ProfileController
|
|
||||||
from core.controllers.SystemStateController import SystemStateController
|
|
||||||
from core.models.system.SystemProfile import SystemProfile
|
|
||||||
|
|
||||||
|
|
||||||
SINGBOX_PROTOCOLS = ("hysteria2", "vless")
|
|
||||||
|
|
||||||
|
|
||||||
def _connection_code(profile):
|
|
||||||
connection = getattr(profile, "connection", None)
|
|
||||||
return getattr(connection, "code", None)
|
|
||||||
|
|
||||||
|
|
||||||
def _system_state_profile_id():
|
|
||||||
state = SystemStateController.get()
|
|
||||||
if state is None:
|
|
||||||
return None
|
|
||||||
try:
|
|
||||||
return int(state.profile_id)
|
|
||||||
except (TypeError, ValueError):
|
|
||||||
return None
|
|
||||||
|
|
||||||
|
|
||||||
def is_profile_enabled_for_gui(profile):
|
|
||||||
if isinstance(profile, SystemProfile) and _connection_code(profile) in SINGBOX_PROTOCOLS:
|
|
||||||
return _system_state_profile_id() == int(profile.id)
|
|
||||||
return ProfileController.is_enabled(profile)
|
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
from core.services.helpers.install_dependencies import SUDO_SINGBOX_LOCATION
|
|
||||||
from core.services.helpers.setup_sudo_scripts import (
|
|
||||||
is_singbox_wrapper_ready,
|
|
||||||
test_if_in_sudo_folder,
|
|
||||||
)
|
|
||||||
from core.utils.basic_operations.does_file_exist import does_file_exist
|
|
||||||
|
|
||||||
|
|
||||||
def singbox_prereqs_installed():
|
|
||||||
try:
|
|
||||||
if not is_singbox_wrapper_ready():
|
|
||||||
return False
|
|
||||||
|
|
||||||
sudo_scripts = test_if_in_sudo_folder()
|
|
||||||
if not getattr(sudo_scripts, "valid", False):
|
|
||||||
return False
|
|
||||||
|
|
||||||
return does_file_exist(SUDO_SINGBOX_LOCATION)
|
|
||||||
except Exception:
|
|
||||||
return False
|
|
||||||
0
gui/v2/infrastructure/screen_size.py
Executable file → Normal file
|
|
@ -15,18 +15,6 @@ profile_observer = ProfileObserver()
|
||||||
ticket_observer = TicketObserver()
|
ticket_observer = TicketObserver()
|
||||||
|
|
||||||
|
|
||||||
def _format_connecting_status(event):
|
|
||||||
subject = getattr(event, 'subject', None)
|
|
||||||
if isinstance(subject, dict):
|
|
||||||
attempt_count = subject.get("attempt_count")
|
|
||||||
maximum_attempts = subject.get("maximum_number_of_attempts")
|
|
||||||
if attempt_count is not None and maximum_attempts is not None:
|
|
||||||
return f'[{attempt_count}/{maximum_attempts}] Performing connection attempt...'
|
|
||||||
if subject:
|
|
||||||
return str(subject)
|
|
||||||
return 'Connecting..'
|
|
||||||
|
|
||||||
|
|
||||||
def setup_observers(update_status):
|
def setup_observers(update_status):
|
||||||
profile_observer.subscribe(
|
profile_observer.subscribe(
|
||||||
'created', lambda event: update_status('Profile Created'))
|
'created', lambda event: update_status('Profile Created'))
|
||||||
|
|
@ -45,7 +33,7 @@ def setup_observers(update_status):
|
||||||
client_observer.subscribe(
|
client_observer.subscribe(
|
||||||
'updating', lambda event: update_status('Updating client...'))
|
'updating', lambda event: update_status('Updating client...'))
|
||||||
client_observer.subscribe('update_progressing', lambda event: update_status(
|
client_observer.subscribe('update_progressing', lambda event: update_status(
|
||||||
f"Current progress: {event.meta.get('progress'):.2f}%"))
|
f'Current progress: {event.meta.get('progress'):.2f}%'))
|
||||||
client_observer.subscribe('updated', lambda event: update_status(
|
client_observer.subscribe('updated', lambda event: update_status(
|
||||||
'Restart client to apply update.'))
|
'Restart client to apply update.'))
|
||||||
|
|
||||||
|
|
@ -70,7 +58,7 @@ def setup_observers(update_status):
|
||||||
# f'Downloaded {ApplicationController.get(event.subject.application_code).name}'))
|
# f'Downloaded {ApplicationController.get(event.subject.application_code).name}'))
|
||||||
|
|
||||||
connection_observer.subscribe('connecting', lambda event: update_status(
|
connection_observer.subscribe('connecting', lambda event: update_status(
|
||||||
_format_connecting_status(event)))
|
f'[{event.subject.get("attempt_count")}/{event.subject.get("maximum_number_of_attempts")}] Performing connection attempt...'))
|
||||||
|
|
||||||
connection_observer.subscribe('tor_bootstrapping', lambda event: update_status(
|
connection_observer.subscribe('tor_bootstrapping', lambda event: update_status(
|
||||||
'Establishing Tor connection...'))
|
'Establishing Tor connection...'))
|
||||||
|
|
|
||||||
|
|
@ -94,10 +94,3 @@ class Page(QWidget):
|
||||||
boton.setChecked(False)
|
boton.setChecked(False)
|
||||||
|
|
||||||
self.button_next.setVisible(False)
|
self.button_next.setVisible(False)
|
||||||
|
|
||||||
def replace_click_handler(self, button, handler):
|
|
||||||
try:
|
|
||||||
button.clicked.disconnect()
|
|
||||||
except TypeError:
|
|
||||||
pass
|
|
||||||
button.clicked.connect(handler)
|
|
||||||
|
|
|
||||||
|
|
@ -22,11 +22,6 @@ from gui.v2.workers.page_data_worker import PageDataWorker
|
||||||
|
|
||||||
|
|
||||||
class EditorPage(Page):
|
class EditorPage(Page):
|
||||||
SINGBOX_PROTOCOLS = ("hysteria2", "vless")
|
|
||||||
PROTOCOL_BUTTON_ASSETS = {
|
|
||||||
"hysteria2": "hystria2",
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, page_stack, main_window, prepared=None):
|
def __init__(self, page_stack, main_window, prepared=None):
|
||||||
super().__init__("Editor", page_stack, main_window)
|
super().__init__("Editor", page_stack, main_window)
|
||||||
self.page_stack = page_stack
|
self.page_stack = page_stack
|
||||||
|
|
@ -213,13 +208,6 @@ class EditorPage(Page):
|
||||||
"dimentions": self.connection_manager.get_available_resolutions(data_profile.get('id', ''))
|
"dimentions": self.connection_manager.get_available_resolutions(data_profile.get('id', ''))
|
||||||
}, selected_profile_str)
|
}, selected_profile_str)
|
||||||
|
|
||||||
elif protocol in self.SINGBOX_PROTOCOLS:
|
|
||||||
self.process_and_show_labels(data_profile, {
|
|
||||||
"protocol": [protocol],
|
|
||||||
"connection": ['system-wide'],
|
|
||||||
"location": self.connection_manager.get_location_list(),
|
|
||||||
}, selected_profile_str)
|
|
||||||
|
|
||||||
elif protocol == "residential" or protocol == "hidetor":
|
elif protocol == "residential" or protocol == "hidetor":
|
||||||
self.process_and_show_labels(data_profile, {
|
self.process_and_show_labels(data_profile, {
|
||||||
"protocol": ['residential', 'wireguard', 'hidetor'],
|
"protocol": ['residential', 'wireguard', 'hidetor'],
|
||||||
|
|
@ -275,20 +263,6 @@ class EditorPage(Page):
|
||||||
else:
|
else:
|
||||||
self.brow_disp.hide()
|
self.brow_disp.hide()
|
||||||
|
|
||||||
if protocol in self.SINGBOX_PROTOCOLS:
|
|
||||||
if protocol == "vless":
|
|
||||||
self.display.setGeometry(0, 90, 540, 405)
|
|
||||||
else:
|
|
||||||
self.display.setGeometry(0, 60, 540, 405)
|
|
||||||
self.display.show()
|
|
||||||
self.display.setPixmap(self._encrypted_proxy_display_pixmap(
|
|
||||||
protocol, location).scaled(
|
|
||||||
self.display.size(),
|
|
||||||
Qt.AspectRatioMode.KeepAspectRatio,
|
|
||||||
Qt.TransformationMode.SmoothTransformation))
|
|
||||||
self.garaje.hide()
|
|
||||||
self.brow_disp.hide()
|
|
||||||
|
|
||||||
if protocol == "residential":
|
if protocol == "residential":
|
||||||
self.display.setGeometry(0, 60, 540, 405)
|
self.display.setGeometry(0, 60, 540, 405)
|
||||||
self.brow_disp.show()
|
self.brow_disp.show()
|
||||||
|
|
@ -327,7 +301,7 @@ class EditorPage(Page):
|
||||||
l_name = f"{location_info.country_name}, {location_info.name}" if hasattr(
|
l_name = f"{location_info.country_name}, {location_info.name}" if hasattr(
|
||||||
location_info, 'country_name') else ""
|
location_info, 'country_name') else ""
|
||||||
|
|
||||||
if operator_name != 'Simplified Privacy' and operator_name != "" and protocol not in ('hidetor', *self.SINGBOX_PROTOCOLS):
|
if operator_name != 'Simplified Privacy' and operator_name != "" and protocol != 'hidetor':
|
||||||
text_color = "white"
|
text_color = "white"
|
||||||
if profile_obj and profile_obj.is_session_profile():
|
if profile_obj and profile_obj.is_session_profile():
|
||||||
text_color = "black"
|
text_color = "black"
|
||||||
|
|
@ -439,12 +413,9 @@ class EditorPage(Page):
|
||||||
base_image = ScreenPage.create_resolution_button_image(
|
base_image = ScreenPage.create_resolution_button_image(
|
||||||
self, current_value)
|
self, current_value)
|
||||||
else:
|
else:
|
||||||
|
image_path = os.path.join(
|
||||||
|
self.btn_path, f"{data_profile.get(key, '')}_button.png")
|
||||||
current_value = data_profile.get(key, '')
|
current_value = data_profile.get(key, '')
|
||||||
if key == 'protocol':
|
|
||||||
image_path = self._protocol_button_asset(current_value)
|
|
||||||
else:
|
|
||||||
image_path = os.path.join(
|
|
||||||
self.btn_path, f"{current_value}_button.png")
|
|
||||||
base_image = QPixmap(image_path)
|
base_image = QPixmap(image_path)
|
||||||
|
|
||||||
if key == 'dimentions':
|
if key == 'dimentions':
|
||||||
|
|
@ -608,7 +579,7 @@ class EditorPage(Page):
|
||||||
|
|
||||||
prev_button.setVisible(True)
|
prev_button.setVisible(True)
|
||||||
next_button.setVisible(True)
|
next_button.setVisible(True)
|
||||||
if key == 'protocol' or (connection == 'system-wide' and key == 'connection'):
|
if key == 'protocol' or (protocol == 'wireguard' and key == 'connection'):
|
||||||
prev_button.setDisabled(True)
|
prev_button.setDisabled(True)
|
||||||
next_button.setDisabled(True)
|
next_button.setDisabled(True)
|
||||||
|
|
||||||
|
|
@ -616,44 +587,6 @@ class EditorPage(Page):
|
||||||
prev_button.setVisible(False)
|
prev_button.setVisible(False)
|
||||||
next_button.setVisible(False)
|
next_button.setVisible(False)
|
||||||
|
|
||||||
def _protocol_button_asset(self, protocol):
|
|
||||||
asset_name = self.PROTOCOL_BUTTON_ASSETS.get(protocol, protocol)
|
|
||||||
return os.path.join(self.btn_path, f"{asset_name}_button.png")
|
|
||||||
|
|
||||||
def _encrypted_proxy_display_pixmap(self, protocol, location):
|
|
||||||
pixmap = QPixmap(os.path.join(self.btn_path, f"{protocol}.png"))
|
|
||||||
if pixmap.isNull():
|
|
||||||
pixmap = QPixmap(os.path.join(self.btn_path, "system_wide_global.png"))
|
|
||||||
if pixmap.isNull():
|
|
||||||
return pixmap
|
|
||||||
|
|
||||||
location_pixmap = QPixmap(os.path.join(
|
|
||||||
self.btn_path, f"icon_mini_{location}.png"))
|
|
||||||
if location_pixmap.isNull():
|
|
||||||
location_pixmap = QPixmap(os.path.join(
|
|
||||||
self.btn_path, "default_location_mini.png"))
|
|
||||||
if location_pixmap.isNull():
|
|
||||||
return pixmap
|
|
||||||
|
|
||||||
icon_size = max(42, min(72, int(min(pixmap.width(), pixmap.height()) * 0.22)))
|
|
||||||
location_pixmap = location_pixmap.scaled(
|
|
||||||
icon_size,
|
|
||||||
icon_size,
|
|
||||||
Qt.AspectRatioMode.KeepAspectRatio,
|
|
||||||
Qt.TransformationMode.SmoothTransformation)
|
|
||||||
x = int(pixmap.width() * 0.58)
|
|
||||||
if protocol == 'hysteria2':
|
|
||||||
y = int(pixmap.height() * 0.35)
|
|
||||||
else:
|
|
||||||
y = int(pixmap.height() * 0.30)
|
|
||||||
x = min(max(0, x), max(0, pixmap.width() - location_pixmap.width()))
|
|
||||||
y = min(max(0, y), max(0, pixmap.height() - location_pixmap.height()))
|
|
||||||
|
|
||||||
painter = QPainter(pixmap)
|
|
||||||
painter.drawPixmap(x, y, location_pixmap)
|
|
||||||
painter.end()
|
|
||||||
return pixmap
|
|
||||||
|
|
||||||
def on_sync_complete_for_edit_profile(self, available_locations, available_browsers, status, is_tor, locations, all_browsers):
|
def on_sync_complete_for_edit_profile(self, available_locations, available_browsers, status, is_tor, locations, all_browsers):
|
||||||
if status:
|
if status:
|
||||||
self.update_status.update_status('Sync complete.')
|
self.update_status.update_status('Sync complete.')
|
||||||
|
|
|
||||||
|
|
@ -15,9 +15,9 @@ class HidetorPage(Page):
|
||||||
self.selected_location_icon = None
|
self.selected_location_icon = None
|
||||||
self.connection_manager = main_window.connection_manager
|
self.connection_manager = main_window.connection_manager
|
||||||
self.update_status = main_window
|
self.update_status = main_window
|
||||||
self.replace_click_handler(self.button_next, self.go_selected)
|
self.button_next.clicked.connect(self.go_selected)
|
||||||
self.button_reverse.setVisible(True)
|
self.button_reverse.setVisible(True)
|
||||||
self.replace_click_handler(self.button_reverse, self.reverse)
|
self.button_reverse.clicked.connect(self.reverse)
|
||||||
self.display.setGeometry(QtCore.QRect(5, 10, 390, 520))
|
self.display.setGeometry(QtCore.QRect(5, 10, 390, 520))
|
||||||
self.title.setGeometry(395, 40, 380, 40)
|
self.title.setGeometry(395, 40, 380, 40)
|
||||||
self.title.setText("Pick a location")
|
self.title.setText("Pick a location")
|
||||||
|
|
@ -91,12 +91,7 @@ class HidetorPage(Page):
|
||||||
self.update_swarp_json()
|
self.update_swarp_json()
|
||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
profile_data = self.update_status.read_data()
|
self.custom_window.navigator.navigate("protocol")
|
||||||
self.limpiar()
|
|
||||||
if profile_data.get("protocol") == "residential":
|
|
||||||
self.custom_window.navigator.navigate("residential")
|
|
||||||
else:
|
|
||||||
self.custom_window.navigator.navigate("protocol")
|
|
||||||
|
|
||||||
def go_selected(self):
|
def go_selected(self):
|
||||||
self.custom_window.navigator.navigate("browser")
|
self.custom_window.navigator.navigate("browser")
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,7 @@ class LocationPage(Page):
|
||||||
self.update_status = main_window
|
self.update_status = main_window
|
||||||
self.button_reverse.setVisible(True)
|
self.button_reverse.setVisible(True)
|
||||||
self.connection_manager = main_window.connection_manager
|
self.connection_manager = main_window.connection_manager
|
||||||
self.replace_click_handler(self.button_reverse, self.reverse)
|
self.button_reverse.clicked.connect(self.reverse)
|
||||||
self.replace_click_handler(self.button_next, self.go_selected)
|
|
||||||
self.display.setGeometry(QtCore.QRect(5, 10, 390, 520))
|
self.display.setGeometry(QtCore.QRect(5, 10, 390, 520))
|
||||||
self.title.setGeometry(395, 40, 380, 40)
|
self.title.setGeometry(395, 40, 380, 40)
|
||||||
self.title.setText("Pick a location")
|
self.title.setText("Pick a location")
|
||||||
|
|
@ -67,7 +66,6 @@ class LocationPage(Page):
|
||||||
button.setChecked(False)
|
button.setChecked(False)
|
||||||
if hasattr(self, 'verification_button'):
|
if hasattr(self, 'verification_button'):
|
||||||
self.verification_button.setEnabled(False)
|
self.verification_button.setEnabled(False)
|
||||||
self.refresh_protocol_location_visibility()
|
|
||||||
|
|
||||||
def create_interface_elements(self, available_locations):
|
def create_interface_elements(self, available_locations):
|
||||||
|
|
||||||
|
|
@ -81,7 +79,7 @@ class LocationPage(Page):
|
||||||
boton.setCheckable(True)
|
boton.setCheckable(True)
|
||||||
|
|
||||||
locations = self.connection_manager.get_location_info(icon_name)
|
locations = self.connection_manager.get_location_info(icon_name)
|
||||||
if locations and not self._location_supports_selected_protocol(locations):
|
if locations and not (hasattr(locations, 'is_wireguard_capable') and locations.is_wireguard_capable):
|
||||||
boton.setVisible(False)
|
boton.setVisible(False)
|
||||||
icon_path = os.path.join(self.btn_path, f"button_{icon_name}.png")
|
icon_path = os.path.join(self.btn_path, f"button_{icon_name}.png")
|
||||||
boton.setIcon(QIcon(icon_path))
|
boton.setIcon(QIcon(icon_path))
|
||||||
|
|
@ -123,30 +121,6 @@ class LocationPage(Page):
|
||||||
boton.clicked.connect(
|
boton.clicked.connect(
|
||||||
lambda checked, loc=icon_name: self.show_location(loc))
|
lambda checked, loc=icon_name: self.show_location(loc))
|
||||||
|
|
||||||
def refresh_protocol_location_visibility(self):
|
|
||||||
for button in self.buttons:
|
|
||||||
location_key = getattr(button, 'location_icon_name', None)
|
|
||||||
locations = self.connection_manager.get_location_info(location_key)
|
|
||||||
button.setVisible(not locations or self._location_supports_selected_protocol(locations))
|
|
||||||
|
|
||||||
def _current_protocol(self):
|
|
||||||
profile_data = self.update_status.read_data()
|
|
||||||
return profile_data.get("protocol", "wireguard")
|
|
||||||
|
|
||||||
def _is_enabled_capability(self, value):
|
|
||||||
return value in (True, 1, "1", "true", "True")
|
|
||||||
|
|
||||||
def _location_supports_selected_protocol(self, locations):
|
|
||||||
capability_by_protocol = {
|
|
||||||
"wireguard": "is_wireguard_capable",
|
|
||||||
"hysteria2": "is_hysteria2_capable",
|
|
||||||
"vless": "is_vless_capable",
|
|
||||||
}
|
|
||||||
capability_name = capability_by_protocol.get(self._current_protocol())
|
|
||||||
if capability_name is None:
|
|
||||||
return True
|
|
||||||
return self._is_enabled_capability(getattr(locations, capability_name, False))
|
|
||||||
|
|
||||||
def update_swarp_json(self, get_connection=False):
|
def update_swarp_json(self, get_connection=False):
|
||||||
profile_data = self.update_status.read_data()
|
profile_data = self.update_status.read_data()
|
||||||
self.connection_type = profile_data.get("connection", "")
|
self.connection_type = profile_data.get("connection", "")
|
||||||
|
|
@ -167,6 +141,7 @@ class LocationPage(Page):
|
||||||
target_size, Qt.AspectRatioMode.KeepAspectRatio))
|
target_size, Qt.AspectRatioMode.KeepAspectRatio))
|
||||||
self.selected_location_icon = location
|
self.selected_location_icon = location
|
||||||
self.button_next.setVisible(True)
|
self.button_next.setVisible(True)
|
||||||
|
self.button_next.clicked.connect(self.go_selected)
|
||||||
self.update_swarp_json()
|
self.update_swarp_json()
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|
@ -179,19 +154,10 @@ class LocationPage(Page):
|
||||||
self.verification_button.setEnabled(True)
|
self.verification_button.setEnabled(True)
|
||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
profile_data = self.update_status.read_data()
|
self.custom_window.navigator.navigate("protocol")
|
||||||
self.limpiar()
|
|
||||||
if hasattr(self, 'initial_display'):
|
|
||||||
self.initial_display.show()
|
|
||||||
if hasattr(self, 'verification_button'):
|
|
||||||
self.verification_button.setEnabled(False)
|
|
||||||
if profile_data.get("protocol") == "wireguard":
|
|
||||||
self.custom_window.navigator.navigate("wireguard")
|
|
||||||
else:
|
|
||||||
self.custom_window.navigator.navigate("protocol")
|
|
||||||
|
|
||||||
def go_selected(self):
|
def go_selected(self):
|
||||||
if self.update_swarp_json(get_connection=True) == "system-wide":
|
if self.connection_type == "system-wide":
|
||||||
self.custom_window.navigator.navigate("resume")
|
self.custom_window.navigator.navigate("resume")
|
||||||
else:
|
else:
|
||||||
self.custom_window.navigator.navigate("browser")
|
self.custom_window.navigator.navigate("browser")
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,6 @@ from core.models.system.SystemProfile import SystemProfile
|
||||||
from gui.v2.infrastructure.setup_observers import ticket_observer
|
from gui.v2.infrastructure.setup_observers import ticket_observer
|
||||||
from gui.v2.actions.database_health import GuiStorageDatabaseError
|
from gui.v2.actions.database_health import GuiStorageDatabaseError
|
||||||
from gui.v2.actions.profile_order import normalize_profile_order
|
from gui.v2.actions.profile_order import normalize_profile_order
|
||||||
from gui.v2.actions.profile_status import is_profile_enabled_for_gui
|
|
||||||
from gui.v2.ui.pages.Page import Page
|
from gui.v2.ui.pages.Page import Page
|
||||||
from gui.v2.ui.pages.location_verification_page import LocationVerificationPage
|
from gui.v2.ui.pages.location_verification_page import LocationVerificationPage
|
||||||
from gui.v2.ui.styles.styles import SCROLLBAR_CYAN_QSS
|
from gui.v2.ui.styles.styles import SCROLLBAR_CYAN_QSS
|
||||||
|
|
@ -308,15 +307,15 @@ class MenuPage(Page):
|
||||||
|
|
||||||
new_profile['location'] = location
|
new_profile['location'] = location
|
||||||
|
|
||||||
if protocol in ('wireguard', 'hysteria2', 'vless'):
|
if protocol == 'wireguard':
|
||||||
new_profile['protocol'] = protocol
|
new_profile['protocol'] = 'wireguard'
|
||||||
else:
|
else:
|
||||||
new_profile['protocol'] = 'hidetor'
|
new_profile['protocol'] = 'hidetor'
|
||||||
|
|
||||||
connection_type = 'browser-only' if isinstance(
|
connection_type = 'browser-only' if isinstance(
|
||||||
profile, SessionProfile) else 'system-wide'
|
profile, SessionProfile) else 'system-wide'
|
||||||
|
|
||||||
if protocol in ('wireguard', 'hysteria2', 'vless'):
|
if protocol == 'wireguard':
|
||||||
new_profile['connection'] = connection_type
|
new_profile['connection'] = connection_type
|
||||||
elif protocol == 'tor':
|
elif protocol == 'tor':
|
||||||
new_profile['connection'] = protocol
|
new_profile['connection'] = protocol
|
||||||
|
|
@ -376,7 +375,7 @@ class MenuPage(Page):
|
||||||
self.IsSystem = 0
|
self.IsSystem = 0
|
||||||
for profile_id, profile in profiles.items():
|
for profile_id, profile in profiles.items():
|
||||||
try:
|
try:
|
||||||
is_enabled = is_profile_enabled_for_gui(profile)
|
is_enabled = ProfileController.is_enabled(profile)
|
||||||
except Exception:
|
except Exception:
|
||||||
is_enabled = False
|
is_enabled = False
|
||||||
if is_enabled:
|
if is_enabled:
|
||||||
|
|
@ -576,10 +575,7 @@ class MenuPage(Page):
|
||||||
child_label.show()
|
child_label.show()
|
||||||
|
|
||||||
def get_icon_path(self, label_name, value, connection_type):
|
def get_icon_path(self, label_name, value, connection_type):
|
||||||
protocol = value.get('protocol', '')
|
|
||||||
if label_name == 'protocol':
|
if label_name == 'protocol':
|
||||||
if protocol in ('hysteria2', 'vless'):
|
|
||||||
return os.path.join(self.btn_path, f"{protocol}_mini.png")
|
|
||||||
if connection_type == 'tor':
|
if connection_type == 'tor':
|
||||||
return os.path.join(self.btn_path, "toricon_mini.png")
|
return os.path.join(self.btn_path, "toricon_mini.png")
|
||||||
elif connection_type == 'just proxy':
|
elif connection_type == 'just proxy':
|
||||||
|
|
@ -588,8 +584,6 @@ class MenuPage(Page):
|
||||||
return os.path.join(self.btn_path, "wireguard_mini.png")
|
return os.path.join(self.btn_path, "wireguard_mini.png")
|
||||||
elif label_name == 'browser':
|
elif label_name == 'browser':
|
||||||
if connection_type == 'system-wide':
|
if connection_type == 'system-wide':
|
||||||
if protocol in ('hysteria2', 'vless'):
|
|
||||||
return os.path.join(self.btn_path, "wireguard_system_wide.png")
|
|
||||||
return os.path.join(self.btn_path, "wireguard_system_wide.png")
|
return os.path.join(self.btn_path, "wireguard_system_wide.png")
|
||||||
else:
|
else:
|
||||||
return os.path.join(self.btn_path, f"{value[label_name]} latest_mini.png")
|
return os.path.join(self.btn_path, f"{value[label_name]} latest_mini.png")
|
||||||
|
|
@ -847,21 +841,17 @@ class MenuPage(Page):
|
||||||
nostr_txt.show()
|
nostr_txt.show()
|
||||||
self.additional_labels.append(nostr_txt)
|
self.additional_labels.append(nostr_txt)
|
||||||
|
|
||||||
elif protocol.lower() in ["wireguard", "open", "residential", "hidetor", "hysteria2", "vless"]:
|
elif protocol.lower() in ["wireguard", "open", "residential", "hidetor"]:
|
||||||
label_principal = QLabel(self)
|
label_principal = QLabel(self)
|
||||||
label_principal.setGeometry(0, 90, 400, 300)
|
label_principal.setGeometry(0, 90, 400, 300)
|
||||||
if protocol.lower() in ["hysteria2", "vless"]:
|
pixmap = QPixmap(os.path.join(
|
||||||
pixmap = self.build_encrypted_proxy_detail_pixmap(
|
self.btn_path, f"{protocol}_{location}.png"))
|
||||||
protocol.lower(), location)
|
|
||||||
else:
|
|
||||||
pixmap = QPixmap(os.path.join(
|
|
||||||
self.btn_path, f"{protocol}_{location}.png"))
|
|
||||||
label_principal.setPixmap(pixmap)
|
label_principal.setPixmap(pixmap)
|
||||||
label_principal.setScaledContents(True)
|
label_principal.setScaledContents(True)
|
||||||
label_principal.show()
|
label_principal.show()
|
||||||
self.additional_labels.append(label_principal)
|
self.additional_labels.append(label_principal)
|
||||||
|
|
||||||
if protocol.lower() in ["wireguard", "open", "residential", "hidetor", "hysteria2", "vless"]:
|
if protocol.lower() in ["wireguard", "open", "residential", "hidetor"]:
|
||||||
|
|
||||||
if protocol.lower() == "wireguard" and ConfigurationController.get_endpoint_verification_enabled():
|
if protocol.lower() == "wireguard" and ConfigurationController.get_endpoint_verification_enabled():
|
||||||
if is_profile_enabled and profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard':
|
if is_profile_enabled and profile_obj and profile_obj.connection and profile_obj.connection.code == 'wireguard':
|
||||||
|
|
@ -964,38 +954,6 @@ class MenuPage(Page):
|
||||||
|
|
||||||
self.current_profile_location = location
|
self.current_profile_location = location
|
||||||
|
|
||||||
def build_encrypted_proxy_detail_pixmap(self, protocol, location):
|
|
||||||
pixmap = QPixmap(os.path.join(self.btn_path, f"{protocol}.png"))
|
|
||||||
if pixmap.isNull():
|
|
||||||
return pixmap
|
|
||||||
|
|
||||||
location_pixmap = QPixmap(os.path.join(
|
|
||||||
self.btn_path, f"icon_mini_{location}.png"))
|
|
||||||
if location_pixmap.isNull():
|
|
||||||
location_pixmap = QPixmap(os.path.join(
|
|
||||||
self.btn_path, "default_location_mini.png"))
|
|
||||||
if location_pixmap.isNull():
|
|
||||||
return pixmap
|
|
||||||
|
|
||||||
icon_size = max(42, min(72, int(min(pixmap.width(), pixmap.height()) * 0.22)))
|
|
||||||
location_pixmap = location_pixmap.scaled(
|
|
||||||
icon_size,
|
|
||||||
icon_size,
|
|
||||||
Qt.AspectRatioMode.KeepAspectRatio,
|
|
||||||
Qt.TransformationMode.SmoothTransformation)
|
|
||||||
x = int(pixmap.width() * 0.58)
|
|
||||||
if protocol == 'hysteria2':
|
|
||||||
y = int(pixmap.height() * 0.35)
|
|
||||||
else:
|
|
||||||
y = int(pixmap.height() * 0.30)
|
|
||||||
x = min(max(0, x), max(0, pixmap.width() - location_pixmap.width()))
|
|
||||||
y = min(max(0, y), max(0, pixmap.height() - location_pixmap.height()))
|
|
||||||
|
|
||||||
painter = QPainter(pixmap)
|
|
||||||
painter.drawPixmap(x, y, location_pixmap)
|
|
||||||
painter.end()
|
|
||||||
return pixmap
|
|
||||||
|
|
||||||
def show_profile_verification(self, profile_id):
|
def show_profile_verification(self, profile_id):
|
||||||
profile = self._safe_profile(profile_id)
|
profile = self._safe_profile(profile_id)
|
||||||
profile_location = getattr(profile, 'location', None)
|
profile_location = getattr(profile, 'location', None)
|
||||||
|
|
@ -1126,10 +1084,7 @@ class MenuPage(Page):
|
||||||
def on_disconnect_done(self):
|
def on_disconnect_done(self):
|
||||||
self.disconnect_button.setEnabled(True)
|
self.disconnect_button.setEnabled(True)
|
||||||
self.disconnect_system_wide_button.setEnabled(True)
|
self.disconnect_system_wide_button.setEnabled(True)
|
||||||
selected_profile_id = self.reverse_id
|
pass
|
||||||
self.refresh_menu_buttons()
|
|
||||||
if selected_profile_id is not None and self._safe_profile(selected_profile_id):
|
|
||||||
self.print_profile_details(f"Profile_{selected_profile_id}")
|
|
||||||
|
|
||||||
def _config_flag(self, section, key, default=False):
|
def _config_flag(self, section, key, default=False):
|
||||||
try:
|
try:
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,13 @@ from PyQt6.QtWidgets import QLabel, QPushButton, QWidget
|
||||||
from PyQt6.QtGui import QPixmap
|
from PyQt6.QtGui import QPixmap
|
||||||
from PyQt6.QtCore import Qt, QTimer
|
from PyQt6.QtCore import Qt, QTimer
|
||||||
|
|
||||||
from core.services.helpers.setup_sudo_scripts import auto_install_sudo_script, test_if_in_sudo_folder, is_singbox_wrapper_ready
|
from core.services.helpers.setup_sudo_scripts import auto_install_sudo_script, test_if_in_sudo_folder
|
||||||
from core.services.helpers.manage_assets import sudo_assets_folder_setup
|
from core.services.helpers.manage_assets import sudo_assets_folder_setup
|
||||||
from core.models.Result import Result, ResultError
|
from core.models.Result import Result, ResultError
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
from gui.v2.actions.singbox_prereqs import singbox_prereqs_installed
|
|
||||||
from gui.v2.ui.pages.Page import Page
|
from gui.v2.ui.pages.Page import Page
|
||||||
from gui.v2.workers.worker_thread import WorkerThread
|
|
||||||
|
|
||||||
|
|
||||||
class NetworkingSetupPage(Page):
|
class NetworkingSetupPage(Page):
|
||||||
|
|
@ -21,10 +19,6 @@ class NetworkingSetupPage(Page):
|
||||||
self.btn_path = main_window.btn_path
|
self.btn_path = main_window.btn_path
|
||||||
self.update_status = main_window
|
self.update_status = main_window
|
||||||
self.manual_scripts_ready = False
|
self.manual_scripts_ready = False
|
||||||
self.singbox_completion = None
|
|
||||||
self.singbox_setup_started = False
|
|
||||||
self.singbox_worker = None
|
|
||||||
self.latest_singbox_message = ""
|
|
||||||
self.setStyleSheet("font-family: Arial;")
|
self.setStyleSheet("font-family: Arial;")
|
||||||
self.button_next.clicked.disconnect(self.gestionar_next)
|
self.button_next.clicked.disconnect(self.gestionar_next)
|
||||||
self.button_next.setVisible(True)
|
self.button_next.setVisible(True)
|
||||||
|
|
@ -35,32 +29,29 @@ class NetworkingSetupPage(Page):
|
||||||
super().showEvent(event)
|
super().showEvent(event)
|
||||||
self.button_next.setVisible(True)
|
self.button_next.setVisible(True)
|
||||||
self.button_next.raise_()
|
self.button_next.raise_()
|
||||||
if self._is_singbox_mode():
|
|
||||||
self.button_next.setVisible(False)
|
|
||||||
QTimer.singleShot(0, self.prepare_singbox_prereqs)
|
|
||||||
|
|
||||||
def _setup_ui(self):
|
def _setup_ui(self):
|
||||||
header_icon = self._icon_label("networking_shield.png", 48, self)
|
header_icon = self._icon_label("networking_shield.png", 48, self)
|
||||||
header_icon.setGeometry(74, 66, 48, 48)
|
header_icon.setGeometry(74, 66, 48, 48)
|
||||||
|
|
||||||
self.title_label = QLabel("Firewall & Managed-DNS Setup", self)
|
title = QLabel("Firewall & Managed-DNS Setup", self)
|
||||||
self.title_label.setGeometry(138, 68, 590, 44)
|
title.setGeometry(138, 68, 590, 44)
|
||||||
self.title_label.setStyleSheet("font-family: Arial; font-size: 26px; font-weight: bold; color: cyan;")
|
title.setStyleSheet("font-family: Arial; font-size: 26px; font-weight: bold; color: cyan;")
|
||||||
|
|
||||||
self.subtitle_label = QLabel(
|
subtitle = QLabel(
|
||||||
"Optional privileged networking helpers",
|
"Optional privileged networking helpers",
|
||||||
self)
|
self)
|
||||||
self.subtitle_label.setGeometry(140, 108, 560, 24)
|
subtitle.setGeometry(140, 108, 560, 24)
|
||||||
self.subtitle_label.setStyleSheet("font-family: Arial; font-size: 14px; color: #d8ffff;")
|
subtitle.setStyleSheet("font-family: Arial; font-size: 14px; color: #d8ffff;")
|
||||||
|
|
||||||
self.description_label = QLabel(
|
description = QLabel(
|
||||||
"These firewall and managed-DNS helpers are separate bash scripts. "
|
"These firewall and managed-DNS helpers are separate bash scripts. "
|
||||||
"They stay outside the AppImage Python runtime, and if installed they are placed "
|
"They stay outside the AppImage Python runtime, and if installed they are placed "
|
||||||
"in sudo-protected folders owned by root.",
|
"in sudo-protected folders owned by root.",
|
||||||
self)
|
self)
|
||||||
self.description_label.setGeometry(80, 152, 640, 72)
|
description.setGeometry(80, 152, 640, 72)
|
||||||
self.description_label.setWordWrap(True)
|
description.setWordWrap(True)
|
||||||
self.description_label.setStyleSheet("font-family: Arial; font-size: 15px; color: cyan;")
|
description.setStyleSheet("font-family: Arial; font-size: 15px; color: cyan;")
|
||||||
|
|
||||||
manual_panel = self._option_panel(74, 248, "Option 1", "Manual review")
|
manual_panel = self._option_panel(74, 248, "Option 1", "Manual review")
|
||||||
manual_text = QLabel(
|
manual_text = QLabel(
|
||||||
|
|
@ -161,94 +152,6 @@ class NetworkingSetupPage(Page):
|
||||||
def _result_message(self, result: Result, fallback):
|
def _result_message(self, result: Result, fallback):
|
||||||
return getattr(result, "message", fallback)
|
return getattr(result, "message", fallback)
|
||||||
|
|
||||||
def _is_singbox_mode(self):
|
|
||||||
return self.singbox_completion is not None
|
|
||||||
|
|
||||||
def configure_for_singbox_profile(self, completion_callback):
|
|
||||||
self.singbox_completion = completion_callback
|
|
||||||
self.singbox_setup_started = False
|
|
||||||
self.singbox_worker = None
|
|
||||||
self.latest_singbox_message = ""
|
|
||||||
self.manual_scripts_ready = False
|
|
||||||
self.title_label.setText("Singbox Prerequisites")
|
|
||||||
self.subtitle_label.setText("Required for Hysteria2 and VLESS systemwide profiles")
|
|
||||||
self.description_label.setText(
|
|
||||||
"Hysteria2 and VLESS need the firewall, managed-DNS, Singbox wrapper, "
|
|
||||||
"and Singbox binary before the profile can be created.")
|
|
||||||
self.manual_button.setText("Get scripts")
|
|
||||||
self.manual_button.setDisabled(False)
|
|
||||||
self.auto_button.setDisabled(False)
|
|
||||||
self.button_next.setVisible(False)
|
|
||||||
self._set_status("Checking Singbox prerequisites...")
|
|
||||||
return self
|
|
||||||
|
|
||||||
def _sudo_scripts_ready_for_singbox(self):
|
|
||||||
if not is_singbox_wrapper_ready():
|
|
||||||
return False
|
|
||||||
confirmed = test_if_in_sudo_folder()
|
|
||||||
return confirmed.valid
|
|
||||||
|
|
||||||
def prepare_singbox_prereqs(self):
|
|
||||||
if not self._is_singbox_mode():
|
|
||||||
return
|
|
||||||
if singbox_prereqs_installed():
|
|
||||||
self._set_status("Singbox prerequisites are already installed. Creating profile...", "#2ecc71")
|
|
||||||
QTimer.singleShot(200, self.finish_singbox_prereq_flow)
|
|
||||||
return
|
|
||||||
if not self._sudo_scripts_ready_for_singbox():
|
|
||||||
self.manual_button.setDisabled(False)
|
|
||||||
self.auto_button.setDisabled(False)
|
|
||||||
self.button_next.setVisible(False)
|
|
||||||
self._set_status("Firewall, managed-DNS, and the Singbox wrapper are required. Choose manual or automated setup.", "#d8ffff")
|
|
||||||
return
|
|
||||||
self.start_singbox_binary_setup()
|
|
||||||
|
|
||||||
def start_singbox_binary_setup(self):
|
|
||||||
if self.singbox_setup_started:
|
|
||||||
return
|
|
||||||
self.singbox_setup_started = True
|
|
||||||
self.latest_singbox_message = ""
|
|
||||||
self.manual_button.setDisabled(True)
|
|
||||||
self.auto_button.setDisabled(True)
|
|
||||||
self.button_next.setVisible(False)
|
|
||||||
self._set_status("Sudo wrapper scripts are ready. Setting up Singbox binary...")
|
|
||||||
self.singbox_worker = WorkerThread('SETUP_SINGBOX_BINARY')
|
|
||||||
self.singbox_worker.text_output.connect(self._set_singbox_status)
|
|
||||||
self.singbox_worker.finished.connect(self.on_singbox_setup_finished)
|
|
||||||
self.singbox_worker.start()
|
|
||||||
|
|
||||||
def _set_singbox_status(self, message):
|
|
||||||
self.latest_singbox_message = message
|
|
||||||
self._set_status(message)
|
|
||||||
|
|
||||||
def on_singbox_setup_finished(self, success):
|
|
||||||
if success:
|
|
||||||
self._set_status("Singbox setup complete. Creating profile...", "#2ecc71")
|
|
||||||
QTimer.singleShot(600, self.finish_singbox_prereq_flow)
|
|
||||||
return
|
|
||||||
self.singbox_setup_started = False
|
|
||||||
self.manual_button.setDisabled(False)
|
|
||||||
self.auto_button.setDisabled(False)
|
|
||||||
self.button_next.setVisible(True)
|
|
||||||
self._set_status(self.latest_singbox_message or "Singbox setup failed.", "#ff6b6b")
|
|
||||||
|
|
||||||
def finish_singbox_prereq_flow(self):
|
|
||||||
completion_callback = self.singbox_completion
|
|
||||||
self.singbox_completion = None
|
|
||||||
self.singbox_setup_started = False
|
|
||||||
self.latest_singbox_message = ""
|
|
||||||
self.title_label.setText("Firewall & Managed-DNS Setup")
|
|
||||||
self.subtitle_label.setText("Optional privileged networking helpers")
|
|
||||||
self.description_label.setText(
|
|
||||||
"These firewall and managed-DNS helpers are separate bash scripts. "
|
|
||||||
"They stay outside the AppImage Python runtime, and if installed they are placed "
|
|
||||||
"in sudo-protected folders owned by root.")
|
|
||||||
self.manual_button.setText("Get scripts")
|
|
||||||
self.manual_button.setDisabled(False)
|
|
||||||
self.auto_button.setDisabled(False)
|
|
||||||
if completion_callback is not None:
|
|
||||||
completion_callback()
|
|
||||||
|
|
||||||
def handle_manual_setup(self):
|
def handle_manual_setup(self):
|
||||||
if self.manual_scripts_ready:
|
if self.manual_scripts_ready:
|
||||||
self.confirm_sudo_scripts()
|
self.confirm_sudo_scripts()
|
||||||
|
|
@ -266,10 +169,6 @@ class NetworkingSetupPage(Page):
|
||||||
def confirm_sudo_scripts(self):
|
def confirm_sudo_scripts(self):
|
||||||
confirmed = test_if_in_sudo_folder()
|
confirmed = test_if_in_sudo_folder()
|
||||||
if confirmed.valid:
|
if confirmed.valid:
|
||||||
if self._is_singbox_mode():
|
|
||||||
self._set_status("Confirmed it worked. Continuing to Singbox setup.", "#2ecc71")
|
|
||||||
QTimer.singleShot(600, self.start_singbox_binary_setup)
|
|
||||||
return
|
|
||||||
self._set_status("Confirmed it worked. Continuing to prerequisite installation.", "#2ecc71")
|
self._set_status("Confirmed it worked. Continuing to prerequisite installation.", "#2ecc71")
|
||||||
QTimer.singleShot(600, self.go_to_install)
|
QTimer.singleShot(600, self.go_to_install)
|
||||||
else:
|
else:
|
||||||
|
|
@ -281,10 +180,6 @@ class NetworkingSetupPage(Page):
|
||||||
if results.valid:
|
if results.valid:
|
||||||
confirmed = test_if_in_sudo_folder()
|
confirmed = test_if_in_sudo_folder()
|
||||||
if confirmed.valid:
|
if confirmed.valid:
|
||||||
if self._is_singbox_mode():
|
|
||||||
self._set_status("The setup script ran successfully. Continuing to Singbox setup.", "#2ecc71")
|
|
||||||
QTimer.singleShot(600, self.start_singbox_binary_setup)
|
|
||||||
return
|
|
||||||
self._set_status("The setup script ran successfully. Continuing to prerequisite installation.", "#2ecc71")
|
self._set_status("The setup script ran successfully. Continuing to prerequisite installation.", "#2ecc71")
|
||||||
QTimer.singleShot(600, self.go_to_install)
|
QTimer.singleShot(600, self.go_to_install)
|
||||||
else:
|
else:
|
||||||
|
|
@ -295,9 +190,6 @@ class NetworkingSetupPage(Page):
|
||||||
f"Setup script did NOT work because {results.message}", "#ff6b6b")
|
f"Setup script did NOT work because {results.message}", "#ff6b6b")
|
||||||
|
|
||||||
def go_to_install(self):
|
def go_to_install(self):
|
||||||
if self._is_singbox_mode():
|
|
||||||
self.prepare_singbox_prereqs()
|
|
||||||
return
|
|
||||||
self.custom_window.navigator.navigate("install_system_package")
|
self.custom_window.navigator.navigate("install_system_package")
|
||||||
install_page = self.custom_window.navigator.get_cached("install_system_package")
|
install_page = self.custom_window.navigator.get_cached("install_system_package")
|
||||||
if install_page is not None:
|
if install_page is not None:
|
||||||
|
|
|
||||||
|
|
@ -9,11 +9,6 @@ from gui.v2.ui.pages.Page import Page
|
||||||
|
|
||||||
|
|
||||||
class ProtocolPage(Page):
|
class ProtocolPage(Page):
|
||||||
SINGBOX_PROTOCOLS = ("hysteria2", "vless")
|
|
||||||
PROTOCOL_BUTTON_ASSETS = {
|
|
||||||
"hysteria2": "hystria2",
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, page_stack, main_window=None, parent=None):
|
def __init__(self, page_stack, main_window=None, parent=None):
|
||||||
super().__init__("Protocol", page_stack, main_window, parent)
|
super().__init__("Protocol", page_stack, main_window, parent)
|
||||||
self.main_window = main_window
|
self.main_window = main_window
|
||||||
|
|
@ -23,8 +18,7 @@ class ProtocolPage(Page):
|
||||||
self.connection_manager = main_window.connection_manager
|
self.connection_manager = main_window.connection_manager
|
||||||
self.button_back.setVisible(True)
|
self.button_back.setVisible(True)
|
||||||
self.update_status = main_window
|
self.update_status = main_window
|
||||||
self.replace_click_handler(self.button_back, self.reverse)
|
self.button_go.clicked.connect(self.go_selected)
|
||||||
self.replace_click_handler(self.button_go, self.go_selected)
|
|
||||||
self.coming_soon_label = QLabel("Coming soon", self)
|
self.coming_soon_label = QLabel("Coming soon", self)
|
||||||
self.coming_soon_label.setGeometry(210, 50, 200, 40)
|
self.coming_soon_label.setGeometry(210, 50, 200, 40)
|
||||||
self.coming_soon_label.setStyleSheet("font-size: 22px;")
|
self.coming_soon_label.setStyleSheet("font-size: 22px;")
|
||||||
|
|
@ -42,11 +36,9 @@ class ProtocolPage(Page):
|
||||||
self.buttons = []
|
self.buttons = []
|
||||||
self.selected_page_name = None
|
self.selected_page_name = None
|
||||||
for j, (object_type, icon_name, page_name, geometry) in enumerate([
|
for j, (object_type, icon_name, page_name, geometry) in enumerate([
|
||||||
(QPushButton, "wireguard", "wireguard", (585, 80, 185, 75)),
|
(QPushButton, "wireguard", "wireguard", (585, 90, 185, 75)),
|
||||||
(QPushButton, "hysteria2", "location", (585, 160, 185, 75)),
|
(QPushButton, "residential", "residential", (585, 90+30+75, 185, 75)),
|
||||||
(QPushButton, "vless", "location", (585, 240, 185, 75)),
|
(QPushButton, "hidetor", "hidetor", (585, 90+30+75+30+75, 185, 75))
|
||||||
(QPushButton, "residential", "residential", (585, 320, 185, 75)),
|
|
||||||
(QPushButton, "hidetor", "hidetor", (585, 400, 185, 75))
|
|
||||||
]):
|
]):
|
||||||
boton = object_type(self)
|
boton = object_type(self)
|
||||||
boton.setGeometry(*geometry)
|
boton.setGeometry(*geometry)
|
||||||
|
|
@ -54,40 +46,28 @@ class ProtocolPage(Page):
|
||||||
boton.setCheckable(True)
|
boton.setCheckable(True)
|
||||||
boton.setDisabled(True)
|
boton.setDisabled(True)
|
||||||
boton.setIcon(
|
boton.setIcon(
|
||||||
QIcon(self._button_asset(icon_name)))
|
QIcon(os.path.join(self.btn_path, f"{icon_name}_button.png")))
|
||||||
self.buttons.append(boton)
|
self.buttons.append(boton)
|
||||||
self.buttonGroup.addButton(boton, j)
|
self.buttonGroup.addButton(boton, j)
|
||||||
boton.clicked.connect(
|
boton.clicked.connect(
|
||||||
lambda _, name=page_name, protocol=icon_name: self.show_protocol(name, protocol))
|
lambda _, name=page_name, protocol=icon_name: self.show_protocol(name, protocol))
|
||||||
|
|
||||||
def _button_asset(self, protocol):
|
|
||||||
asset_name = self.PROTOCOL_BUTTON_ASSETS.get(protocol, protocol)
|
|
||||||
return os.path.join(self.btn_path, f"{asset_name}_button.png")
|
|
||||||
|
|
||||||
def _display_asset(self, protocol):
|
|
||||||
full_size_path = os.path.join(self.btn_path, f"{protocol}.png")
|
|
||||||
if os.path.exists(full_size_path):
|
|
||||||
return full_size_path
|
|
||||||
return self._button_asset(protocol)
|
|
||||||
|
|
||||||
def enable_protocol_buttons(self):
|
def enable_protocol_buttons(self):
|
||||||
for button in self.buttons:
|
for button in self.buttons:
|
||||||
button.setDisabled(False)
|
button.setDisabled(False)
|
||||||
|
|
||||||
def update_swarp_json(self):
|
def update_swarp_json(self):
|
||||||
data = {"protocol": self.selected_protocol_icon}
|
self.update_status.write_data(
|
||||||
if self.selected_protocol_icon in self.SINGBOX_PROTOCOLS:
|
{"protocol": self.selected_protocol_icon})
|
||||||
data["connection"] = "system-wide"
|
|
||||||
self.update_status.write_data(data)
|
|
||||||
|
|
||||||
def show_protocol(self, page_name, protocol):
|
def show_protocol(self, page_name, protocol):
|
||||||
self.update_status.clear_data()
|
self.update_status.clear_data()
|
||||||
self.display.setPixmap(QPixmap(self._display_asset(protocol)).scaled(
|
self.display.setPixmap(QPixmap(os.path.join(self.btn_path, f"{protocol}.png")).scaled(
|
||||||
self.display.size(), Qt.AspectRatioMode.KeepAspectRatio))
|
self.display.size(), Qt.AspectRatioMode.KeepAspectRatio))
|
||||||
self.selected_protocol_icon = protocol
|
self.selected_protocol_icon = protocol
|
||||||
self.selected_page_name = page_name
|
self.selected_page_name = page_name
|
||||||
|
|
||||||
if protocol in ["wireguard", "hidetor", *self.SINGBOX_PROTOCOLS]:
|
if protocol in ["wireguard", "hidetor"]:
|
||||||
self.button_go.setVisible(True)
|
self.button_go.setVisible(True)
|
||||||
self.coming_soon_label.setVisible(False)
|
self.coming_soon_label.setVisible(False)
|
||||||
else:
|
else:
|
||||||
|
|
@ -105,10 +85,3 @@ class ProtocolPage(Page):
|
||||||
|
|
||||||
def find_menu_page(self):
|
def find_menu_page(self):
|
||||||
return self.custom_window.navigator.get_cached("menu")
|
return self.custom_window.navigator.get_cached("menu")
|
||||||
|
|
||||||
def reverse(self):
|
|
||||||
self.display.clear()
|
|
||||||
for boton in self.buttons:
|
|
||||||
boton.setChecked(False)
|
|
||||||
self.button_go.setVisible(False)
|
|
||||||
self.custom_window.navigator.navigate("menu")
|
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,8 @@ class ResidentialPage(Page):
|
||||||
self.update_status = main_window
|
self.update_status = main_window
|
||||||
self.connection_choice = None
|
self.connection_choice = None
|
||||||
self.button_reverse.setVisible(True)
|
self.button_reverse.setVisible(True)
|
||||||
self.replace_click_handler(self.button_reverse, self.reverse)
|
self.button_reverse.clicked.connect(self.reverse)
|
||||||
self.replace_click_handler(self.button_go, self.go_selected)
|
self.button_go.clicked.connect(self.go_selected)
|
||||||
|
|
||||||
self.display_1 = QLabel(self)
|
self.display_1 = QLabel(self)
|
||||||
self.display_1.setGeometry(QtCore.QRect(
|
self.display_1.setGeometry(QtCore.QRect(
|
||||||
|
|
@ -98,5 +98,4 @@ class ResidentialPage(Page):
|
||||||
self.update_status.write_data(inserted_data)
|
self.update_status.write_data(inserted_data)
|
||||||
|
|
||||||
def reverse(self):
|
def reverse(self):
|
||||||
self.button_go.setVisible(False)
|
|
||||||
self.custom_window.navigator.navigate("protocol")
|
self.custom_window.navigator.navigate("protocol")
|
||||||
|
|
|
||||||
|
|
@ -10,18 +10,12 @@ from PyQt6 import QtCore, QtGui
|
||||||
from core.controllers.ProfileController import ProfileController
|
from core.controllers.ProfileController import ProfileController
|
||||||
|
|
||||||
from gui.v2.actions.profile_order import append_profile_to_visual_order
|
from gui.v2.actions.profile_order import append_profile_to_visual_order
|
||||||
from gui.v2.actions.singbox_prereqs import singbox_prereqs_installed
|
|
||||||
from gui.v2.ui.pages.Page import Page
|
from gui.v2.ui.pages.Page import Page
|
||||||
from gui.v2.ui.pages.location_page import LocationPage
|
from gui.v2.ui.pages.location_page import LocationPage
|
||||||
from gui.v2.ui.pages.screen_page import ScreenPage
|
from gui.v2.ui.pages.screen_page import ScreenPage
|
||||||
|
|
||||||
|
|
||||||
class ResumePage(Page):
|
class ResumePage(Page):
|
||||||
SINGBOX_PROTOCOLS = ("hysteria2", "vless")
|
|
||||||
PROTOCOL_BUTTON_ASSETS = {
|
|
||||||
"hysteria2": "hystria2",
|
|
||||||
}
|
|
||||||
|
|
||||||
def __init__(self, page_stack, main_window=None, parent=None):
|
def __init__(self, page_stack, main_window=None, parent=None):
|
||||||
super().__init__("Resume", page_stack, main_window, parent)
|
super().__init__("Resume", page_stack, main_window, parent)
|
||||||
self.update_status = main_window
|
self.update_status = main_window
|
||||||
|
|
@ -29,13 +23,13 @@ class ResumePage(Page):
|
||||||
self.btn_path = main_window.btn_path
|
self.btn_path = main_window.btn_path
|
||||||
self.labels_creados = []
|
self.labels_creados = []
|
||||||
self.additional_labels = []
|
self.additional_labels = []
|
||||||
self.replace_click_handler(self.button_go, self.copy_profile)
|
self.button_go.clicked.connect(self.copy_profile)
|
||||||
self.button_back.setVisible(True)
|
self.button_back.setVisible(True)
|
||||||
self.title.setGeometry(585, 40, 185, 40)
|
self.title.setGeometry(585, 40, 185, 40)
|
||||||
self.title.setText("Profile Summary")
|
self.title.setText("Profile Summary")
|
||||||
self.display.setGeometry(QtCore.QRect(5, 50, 580, 435))
|
self.display.setGeometry(QtCore.QRect(5, 50, 580, 435))
|
||||||
self.buttonGroup = QButtonGroup(self)
|
self.buttonGroup = QButtonGroup(self)
|
||||||
self.replace_click_handler(self.button_back, self.reverse)
|
self.button_back.clicked.connect(self.reverse)
|
||||||
self.create_arrow()
|
self.create_arrow()
|
||||||
self.create_interface_elements()
|
self.create_interface_elements()
|
||||||
|
|
||||||
|
|
@ -206,11 +200,8 @@ class ResumePage(Page):
|
||||||
parent_label.show()
|
parent_label.show()
|
||||||
self.labels_creados.append(parent_label)
|
self.labels_creados.append(parent_label)
|
||||||
else:
|
else:
|
||||||
if item == 'protocol':
|
icon_path = os.path.join(
|
||||||
icon_path = self._protocol_button_asset(text)
|
self.btn_path, f"{text}_button.png")
|
||||||
else:
|
|
||||||
icon_path = os.path.join(
|
|
||||||
self.btn_path, f"{text}_button.png")
|
|
||||||
geometry = (585, initial_y + i * label_height, 185, 75)
|
geometry = (585, initial_y + i * label_height, 185, 75)
|
||||||
parent_label = QLabel(self)
|
parent_label = QLabel(self)
|
||||||
parent_label.setGeometry(*geometry)
|
parent_label.setGeometry(*geometry)
|
||||||
|
|
@ -287,8 +278,8 @@ class ResumePage(Page):
|
||||||
|
|
||||||
elif connection_exists:
|
elif connection_exists:
|
||||||
if profile_1.get("connection", "") == "system-wide":
|
if profile_1.get("connection", "") == "system-wide":
|
||||||
image_path = self._system_profile_image(
|
image_path = os.path.join(
|
||||||
profile_1.get('protocol', 'wireguard'), profile_1.get('location', ''))
|
self.btn_path, f"wireguard_{profile_1.get('location', '')}.png")
|
||||||
main_label = QLabel(self)
|
main_label = QLabel(self)
|
||||||
main_label.setGeometry(10, 130, 500, 375)
|
main_label.setGeometry(10, 130, 500, 375)
|
||||||
main_label.setPixmap(QPixmap(image_path))
|
main_label.setPixmap(QPixmap(image_path))
|
||||||
|
|
@ -345,21 +336,6 @@ class ResumePage(Page):
|
||||||
if hasattr(self, 'arrow_label'):
|
if hasattr(self, 'arrow_label'):
|
||||||
self.arrow_label.raise_()
|
self.arrow_label.raise_()
|
||||||
|
|
||||||
def _protocol_button_asset(self, protocol):
|
|
||||||
asset_name = self.PROTOCOL_BUTTON_ASSETS.get(protocol, protocol)
|
|
||||||
return os.path.join(self.btn_path, f"{asset_name}_button.png")
|
|
||||||
|
|
||||||
def _system_profile_image(self, protocol, location):
|
|
||||||
candidates = [
|
|
||||||
os.path.join(self.btn_path, f"{protocol}_{location}.png"),
|
|
||||||
os.path.join(self.btn_path, f"icon_{location}.png"),
|
|
||||||
os.path.join(self.btn_path, "system_wide_global.png"),
|
|
||||||
]
|
|
||||||
for candidate in candidates:
|
|
||||||
if os.path.exists(candidate):
|
|
||||||
return candidate
|
|
||||||
return candidates[-1]
|
|
||||||
|
|
||||||
def toggle_button_visibility(self):
|
def toggle_button_visibility(self):
|
||||||
self.button_go.setVisible(bool(self.line_edit.text()))
|
self.button_go.setVisible(bool(self.line_edit.text()))
|
||||||
|
|
||||||
|
|
@ -368,6 +344,10 @@ class ResumePage(Page):
|
||||||
|
|
||||||
def copy_profile(self):
|
def copy_profile(self):
|
||||||
profile_name = self.line_edit.text()
|
profile_name = self.line_edit.text()
|
||||||
|
menu_page = self.find_menu_page()
|
||||||
|
if menu_page:
|
||||||
|
number_of_profiles = menu_page.number_of_profiles
|
||||||
|
|
||||||
profile_data = self.update_status.read_data()
|
profile_data = self.update_status.read_data()
|
||||||
|
|
||||||
required_fields = [profile_data.get("protocol"), profile_name]
|
required_fields = [profile_data.get("protocol"), profile_name]
|
||||||
|
|
@ -381,29 +361,13 @@ class ResumePage(Page):
|
||||||
profiles = ProfileController.get_all()
|
profiles = ProfileController.get_all()
|
||||||
profile_id = self.get_next_available_id(profiles)
|
profile_id = self.get_next_available_id(profiles)
|
||||||
new_profile = profile_data
|
new_profile = profile_data
|
||||||
existing_profile_ids = tuple(profiles.keys())
|
|
||||||
|
|
||||||
if new_profile.get('protocol') in self.SINGBOX_PROTOCOLS and not singbox_prereqs_installed():
|
|
||||||
self.show_singbox_prereq_setup(new_profile, profile_id, existing_profile_ids)
|
|
||||||
return
|
|
||||||
|
|
||||||
self.finish_profile_creation(new_profile, profile_id, existing_profile_ids)
|
|
||||||
|
|
||||||
def show_singbox_prereq_setup(self, profile, profile_id, existing_profile_ids):
|
|
||||||
setup_page = self.custom_window.navigator.navigate("networking_setup")
|
|
||||||
if setup_page is None:
|
|
||||||
self.update_status.update_status("Singbox prerequisite page is unavailable.")
|
|
||||||
return
|
|
||||||
setup_page.configure_for_singbox_profile(
|
|
||||||
lambda: self.finish_profile_creation(profile, profile_id, existing_profile_ids))
|
|
||||||
|
|
||||||
def finish_profile_creation(self, new_profile, profile_id, existing_profile_ids):
|
|
||||||
self.create_core_profiles(new_profile, profile_id)
|
self.create_core_profiles(new_profile, profile_id)
|
||||||
if ProfileController.get(profile_id) is not None:
|
if ProfileController.get(profile_id) is not None:
|
||||||
append_profile_to_visual_order(
|
append_profile_to_visual_order(
|
||||||
getattr(self.update_status, 'gui_config_file', None),
|
getattr(self.update_status, 'gui_config_file', None),
|
||||||
profile_id,
|
profile_id,
|
||||||
existing_profile_ids)
|
profiles.keys())
|
||||||
|
|
||||||
main = self.update_status
|
main = self.update_status
|
||||||
if hasattr(main, 'navigate_after_profile_created'):
|
if hasattr(main, 'navigate_after_profile_created'):
|
||||||
|
|
@ -412,6 +376,7 @@ class ResumePage(Page):
|
||||||
self.custom_window.navigator.navigate("menu")
|
self.custom_window.navigator.navigate("menu")
|
||||||
|
|
||||||
self.update_status.clear_data()
|
self.update_status.clear_data()
|
||||||
|
|
||||||
self.line_edit.clear()
|
self.line_edit.clear()
|
||||||
self.display.clear()
|
self.display.clear()
|
||||||
self.button_go.setVisible(False)
|
self.button_go.setVisible(False)
|
||||||
|
|
@ -443,8 +408,8 @@ class ResumePage(Page):
|
||||||
parts = profile.get('location').split('_')
|
parts = profile.get('location').split('_')
|
||||||
country_code = parts[0]
|
country_code = parts[0]
|
||||||
location_code = parts[1]
|
location_code = parts[1]
|
||||||
if profile.get('protocol') in ('wireguard', 'hysteria2', 'vless'):
|
if profile.get('protocol') == 'wireguard':
|
||||||
connection_type = profile.get('protocol')
|
connection_type = 'wireguard'
|
||||||
elif profile.get('protocol') == 'hidetor' or profile.get('protocol') == 'residential':
|
elif profile.get('protocol') == 'hidetor' or profile.get('protocol') == 'residential':
|
||||||
if profile.get('connection') == 'tor':
|
if profile.get('connection') == 'tor':
|
||||||
connection_type = 'tor'
|
connection_type = 'tor'
|
||||||
|
|
|
||||||
|
|
@ -345,6 +345,3 @@ class ScreenPage(Page):
|
||||||
|
|
||||||
def gestionar_next(self):
|
def gestionar_next(self):
|
||||||
self.custom_window.navigator.navigate("resume")
|
self.custom_window.navigator.navigate("resume")
|
||||||
|
|
||||||
def gestionar_back(self):
|
|
||||||
self.custom_window.navigator.navigate("browser")
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,15 @@
|
||||||
import os
|
import os
|
||||||
import shutil
|
|
||||||
|
|
||||||
from PyQt6.QtWidgets import QButtonGroup, QMessageBox, QPushButton
|
from PyQt6.QtWidgets import QApplication, QButtonGroup, QMessageBox, QPushButton
|
||||||
from PyQt6.QtGui import QIcon
|
from PyQt6.QtGui import QIcon
|
||||||
from PyQt6.QtCore import QSize
|
from PyQt6.QtCore import QSize
|
||||||
from PyQt6 import QtCore
|
from PyQt6 import QtCore
|
||||||
|
|
||||||
from core.Constants import Constants
|
from core.services.prepare_tickets.ticket_tracker import delete_ticket_data
|
||||||
|
from core.controllers.tickets.TicketPayController import check_if_paid
|
||||||
|
from core.models.Result import Result, ResultError
|
||||||
|
|
||||||
|
from gui.v2.infrastructure.setup_observers import connection_observer, ticket_observer
|
||||||
from gui.v2.ui.pages.Page import Page
|
from gui.v2.ui.pages.Page import Page
|
||||||
from gui.v2.ui.popups.message_box import style_message_box, mark_confirm_button
|
from gui.v2.ui.popups.message_box import style_message_box, mark_confirm_button
|
||||||
from gui.v2.workers.ticketing_worker_thread import TicketingWorkerThread
|
from gui.v2.workers.ticketing_worker_thread import TicketingWorkerThread
|
||||||
|
|
@ -64,6 +66,28 @@ class TicketCryptoPickerPage(Page):
|
||||||
currency = selected_button.property('currency')
|
currency = selected_button.property('currency')
|
||||||
self.start_initiate_payment(currency)
|
self.start_initiate_payment(currency)
|
||||||
|
|
||||||
|
def check_if_paid_for_existing(self, temp_billing_code):
|
||||||
|
self.update_status.update_status("Checking if paid...")
|
||||||
|
result = check_if_paid(temp_billing_code, ticket_observer, connection_observer)
|
||||||
|
if isinstance(result, dict) and result.get('valid') and result.get('payment_status') == 'paid':
|
||||||
|
self.update_status.update_status("Already Paid!")
|
||||||
|
self.custom_window.navigator.navigate("ticket_prep")
|
||||||
|
prep_page = self.custom_window.navigator.get_cached("ticket_prep")
|
||||||
|
if prep_page:
|
||||||
|
prep_page.start_prep()
|
||||||
|
else:
|
||||||
|
self.update_status.update_status("Not yet paid.")
|
||||||
|
clipboard = QApplication.clipboard()
|
||||||
|
clipboard.setText(temp_billing_code)
|
||||||
|
not_paid_msg = f"The billing code is not yet showing paid for {temp_billing_code}. Right now, your clipboard has the billing code, to paste it in any text editor. If you did pay, either wait longer for blockchain confirmation, or contact customer support with the code in your clipboard now."
|
||||||
|
info = QMessageBox(self)
|
||||||
|
info.setWindowTitle("Not Paid")
|
||||||
|
info.setText(not_paid_msg)
|
||||||
|
info.setStandardButtons(QMessageBox.StandardButton.Ok)
|
||||||
|
style_message_box(info)
|
||||||
|
mark_confirm_button(info.button(QMessageBox.StandardButton.Ok))
|
||||||
|
info.exec()
|
||||||
|
|
||||||
def start_initiate_payment(self, currency):
|
def start_initiate_payment(self, currency):
|
||||||
self.update_status.update_status("Initiating payment...")
|
self.update_status.update_status("Initiating payment...")
|
||||||
self.worker = TicketingWorkerThread('INITIATE_PAYMENT', params={
|
self.worker = TicketingWorkerThread('INITIATE_PAYMENT', params={
|
||||||
|
|
@ -81,82 +105,69 @@ class TicketCryptoPickerPage(Page):
|
||||||
self.update_status.update_status("Could not initiate payment.")
|
self.update_status.update_status("Could not initiate payment.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if not getattr(invoice, 'is_valid', False):
|
if not invoice.valid:
|
||||||
return self._handle_api_errors(invoice)
|
return self._handle_api_errors(invoice)
|
||||||
|
|
||||||
|
# error_code = getattr(invoice, 'error_code', None)
|
||||||
self.custom_window.navigator.navigate("payment_details")
|
self.custom_window.navigator.navigate("payment_details")
|
||||||
payment_page = self.custom_window.navigator.get_cached("payment_details")
|
payment_page = self.custom_window.navigator.get_cached("payment_details")
|
||||||
if payment_page is not None:
|
if payment_page is not None:
|
||||||
payment_page.set_ticket_invoice(invoice, self.selected_plan)
|
payment_page.set_ticket_invoice(invoice.data, self.selected_plan)
|
||||||
|
|
||||||
def _handle_api_errors(self, invoice):
|
def _handle_api_errors(self, invoice: Result):
|
||||||
error_code = getattr(invoice, 'error_code', None)
|
error_code = invoice.error_type
|
||||||
if error_code == "already_exists" and not self.bypass_existing:
|
if error_code == ResultError.ALREADY_EXISTS and not self.bypass_existing:
|
||||||
self._prompt_wipe_existing()
|
self._prompt_wipe_existing(invoice)
|
||||||
return
|
return
|
||||||
elif error_code == "billing_code_exists" and not self.bypass_existing:
|
elif error_code == ResultError.BILLING_CODE_EXISTS and not self.bypass_existing:
|
||||||
temp_billing_code = getattr(invoice, 'temp_billing_code', None)
|
temp_billing_code = getattr(invoice, 'temp_billing_code', None)
|
||||||
self._prompt_wipe_existing(temp_billing_code)
|
print(f"temp_billing_code is {temp_billing_code}")
|
||||||
return
|
if temp_billing_code:
|
||||||
|
self._prompt_wipe_billingcode(temp_billing_code)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
self._prompt_wipe_billingcode("NONE")
|
||||||
|
return
|
||||||
else:
|
else:
|
||||||
error_msg = getattr(invoice, 'final_error_msg', None) or error_code or "Could not initiate payment."
|
error_msg = invoice.message
|
||||||
|
# msg = getattr(invoice, 'final_error_msg', None) or error_code
|
||||||
self.update_status.update_status(error_msg)
|
self.update_status.update_status(error_msg)
|
||||||
return
|
return
|
||||||
|
|
||||||
def _prompt_wipe_existing(self, temp_billing_code=None):
|
def _prompt_wipe_existing(self, invoice):
|
||||||
msg = QMessageBox(self)
|
msg = QMessageBox(self)
|
||||||
if temp_billing_code:
|
msg.setWindowTitle("Existing tickets found")
|
||||||
msg.setWindowTitle("Existing billing code found")
|
msg.setText("You already have ticket data. Wipe it and start over?")
|
||||||
msg.setText("You already have a ticket billing code. Use it, or wipe it and start over?")
|
msg.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
||||||
use_button = msg.addButton("Use existing", QMessageBox.ButtonRole.AcceptRole)
|
|
||||||
wipe_button = msg.addButton("Wipe and start over", QMessageBox.ButtonRole.DestructiveRole)
|
|
||||||
cancel_button = msg.addButton("Cancel", QMessageBox.ButtonRole.RejectRole)
|
|
||||||
mark_confirm_button(use_button)
|
|
||||||
else:
|
|
||||||
msg.setWindowTitle("Existing tickets found")
|
|
||||||
msg.setText("You already have ticket data. Wipe it and start over?")
|
|
||||||
use_button = None
|
|
||||||
wipe_button = msg.addButton("Wipe and start over", QMessageBox.ButtonRole.DestructiveRole)
|
|
||||||
cancel_button = msg.addButton("Cancel", QMessageBox.ButtonRole.RejectRole)
|
|
||||||
mark_confirm_button(wipe_button)
|
|
||||||
style_message_box(msg)
|
style_message_box(msg)
|
||||||
msg.exec()
|
mark_confirm_button(msg.button(QMessageBox.StandardButton.Yes))
|
||||||
clicked = msg.clickedButton()
|
result = msg.exec()
|
||||||
if temp_billing_code and clicked == use_button:
|
if result == QMessageBox.StandardButton.Yes:
|
||||||
self.update_status.update_status("Reusing existing billing code")
|
self.bypass_existing = True
|
||||||
self.custom_window.navigator.navigate("payment_details")
|
delete_ticket_data()
|
||||||
payment_page = self.custom_window.navigator.get_cached("payment_details")
|
currency_btn = self.buttonGroup.checkedButton()
|
||||||
if payment_page is not None:
|
if currency_btn:
|
||||||
payment_page.resume_ticket_billing(temp_billing_code)
|
self.start_initiate_payment(currency_btn.property('currency'))
|
||||||
return
|
|
||||||
if clicked == wipe_button:
|
|
||||||
self._restart_payment_after_wipe()
|
|
||||||
else:
|
else:
|
||||||
self.update_status.update_status("Cancelled.")
|
self.update_status.update_status("Cancelled.")
|
||||||
|
|
||||||
def _restart_payment_after_wipe(self):
|
def _prompt_wipe_billingcode(self, temp_billing_code):
|
||||||
if not self._delete_ticket_data():
|
msg = QMessageBox(self)
|
||||||
return
|
msg.setWindowTitle("Existing billing code found")
|
||||||
self.bypass_existing = False
|
msg.setText("You already have a ticket billing code. Do you want to use it? Only hit YES if you ALREADY paid.")
|
||||||
currency_btn = self.buttonGroup.checkedButton()
|
msg.setStandardButtons(QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
||||||
if currency_btn:
|
style_message_box(msg)
|
||||||
self.start_initiate_payment(currency_btn.property('currency'))
|
mark_confirm_button(msg.button(QMessageBox.StandardButton.Yes))
|
||||||
|
result = msg.exec()
|
||||||
def _delete_ticket_data(self):
|
if result == QMessageBox.StandardButton.Yes:
|
||||||
paths = [
|
self.update_status.update_status("Reusing same code")
|
||||||
Constants.TICKET_TRACKER_PATH,
|
self.check_if_paid_for_existing(temp_billing_code)
|
||||||
os.path.join(Constants.HV_TICKETING_CONFIG_HOME, "billing_choices.json"),
|
else:
|
||||||
]
|
self.bypass_existing = True
|
||||||
try:
|
delete_ticket_data()
|
||||||
for path in paths:
|
currency_btn = self.buttonGroup.checkedButton()
|
||||||
if os.path.exists(path):
|
if currency_btn:
|
||||||
os.remove(path)
|
self.start_initiate_payment(currency_btn.property('currency'))
|
||||||
if os.path.isdir(Constants.HV_TICKETING_DATA_HOME):
|
|
||||||
shutil.rmtree(Constants.HV_TICKETING_DATA_HOME)
|
|
||||||
return True
|
|
||||||
except OSError as error:
|
|
||||||
self.update_status.update_status(f"Could not wipe ticket data: {error}")
|
|
||||||
return False
|
|
||||||
|
|
||||||
def on_error(self, msg):
|
def on_error(self, msg):
|
||||||
self.update_status.update_status(f"Payment error: {msg}")
|
self.update_status.update_status(f"Payment error: {msg}")
|
||||||
|
|
|
||||||
|
|
@ -24,11 +24,10 @@ class TorPage(Page):
|
||||||
QPixmap(os.path.join(self.btn_path, "browser only.png")))
|
QPixmap(os.path.join(self.btn_path, "browser only.png")))
|
||||||
self.display0.lower()
|
self.display0.lower()
|
||||||
|
|
||||||
self.replace_click_handler(self.button_go, self.go_selected)
|
self.button_go.clicked.connect(self.go_selected)
|
||||||
|
|
||||||
self.button_reverse.setVisible(True)
|
self.button_reverse.setVisible(True)
|
||||||
self.replace_click_handler(self.button_reverse, self.reverse_selected)
|
self.button_reverse.clicked.connect(self.reverse_selected)
|
||||||
self.replace_click_handler(self.button_back, self.reverse_selected)
|
|
||||||
|
|
||||||
self.label = QLabel(self)
|
self.label = QLabel(self)
|
||||||
self.label.setGeometry(440, 370, 86, 130)
|
self.label.setGeometry(440, 370, 86, 130)
|
||||||
|
|
@ -73,8 +72,6 @@ class TorPage(Page):
|
||||||
self.update_status.write_data(inserted_data)
|
self.update_status.write_data(inserted_data)
|
||||||
|
|
||||||
def reverse_selected(self):
|
def reverse_selected(self):
|
||||||
self.limpiar()
|
|
||||||
self.button_go.setVisible(False)
|
|
||||||
self.custom_window.navigator.navigate("residential")
|
self.custom_window.navigator.navigate("residential")
|
||||||
|
|
||||||
def go_selected(self):
|
def go_selected(self):
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,7 @@ class WireGuardPage(Page):
|
||||||
self.selected_protocol = None
|
self.selected_protocol = None
|
||||||
self.selected_protocol_icon = None
|
self.selected_protocol_icon = None
|
||||||
self.button_back.setVisible(True)
|
self.button_back.setVisible(True)
|
||||||
self.replace_click_handler(self.button_back, self.reverse)
|
self.button_go.clicked.connect(self.go_selected)
|
||||||
self.replace_click_handler(self.button_go, self.go_selected)
|
|
||||||
self.additional_labels = []
|
self.additional_labels = []
|
||||||
self.title.setGeometry(585, 40, 185, 40)
|
self.title.setGeometry(585, 40, 185, 40)
|
||||||
self.title.setText("Pick a Protocol")
|
self.title.setText("Pick a Protocol")
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import shlex
|
import shlex
|
||||||
import subprocess
|
import subprocess
|
||||||
import inspect
|
|
||||||
|
|
||||||
from PyQt6.QtCore import QThread, pyqtSignal
|
from PyQt6.QtCore import QThread, pyqtSignal
|
||||||
|
|
||||||
|
|
@ -21,10 +20,8 @@ from core.models.BaseProfile import ProfileType
|
||||||
from core.models.system.SystemProfile import SystemProfile
|
from core.models.system.SystemProfile import SystemProfile
|
||||||
from core.errors.exceptions import SudoScript, MissingPreReqs, FirewallError
|
from core.errors.exceptions import SudoScript, MissingPreReqs, FirewallError
|
||||||
from core.models.Result import Result, ResultError
|
from core.models.Result import Result, ResultError
|
||||||
from core.services.helpers.install_dependencies import setup_singbox_binary as install_singbox_binary
|
|
||||||
|
|
||||||
from gui.v2.infrastructure.setup_observers import (
|
from gui.v2.infrastructure.setup_observers import (
|
||||||
application_version_observer,
|
|
||||||
client_observer,
|
client_observer,
|
||||||
connection_observer,
|
connection_observer,
|
||||||
invoice_observer,
|
invoice_observer,
|
||||||
|
|
@ -53,18 +50,6 @@ class WorkerThread(QThread):
|
||||||
self.is_running = True
|
self.is_running = True
|
||||||
self.is_disabling = False
|
self.is_disabling = False
|
||||||
|
|
||||||
def _disable_profile(self, profile):
|
|
||||||
kwargs = {
|
|
||||||
'profile_observer': profile_observer,
|
|
||||||
'ticket_observer': ticket_observer,
|
|
||||||
'connection_observer': connection_observer,
|
|
||||||
}
|
|
||||||
supported = inspect.signature(ProfileController.disable).parameters
|
|
||||||
ProfileController.disable(
|
|
||||||
profile,
|
|
||||||
**{key: value for key, value in kwargs.items() if key in supported}
|
|
||||||
)
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if self.action == 'LIST_PROFILES':
|
if self.action == 'LIST_PROFILES':
|
||||||
self.list_profiles()
|
self.list_profiles()
|
||||||
|
|
@ -84,8 +69,6 @@ class WorkerThread(QThread):
|
||||||
self.disable_all_profiles()
|
self.disable_all_profiles()
|
||||||
elif self.action == 'INSTALL_PACKAGE':
|
elif self.action == 'INSTALL_PACKAGE':
|
||||||
self.install_package()
|
self.install_package()
|
||||||
elif self.action == 'SETUP_SINGBOX_BINARY':
|
|
||||||
self.setup_singbox_binary()
|
|
||||||
elif self.action == 'CHECK_FOR_UPDATE':
|
elif self.action == 'CHECK_FOR_UPDATE':
|
||||||
self.check_for_update()
|
self.check_for_update()
|
||||||
elif self.action == 'DOWNLOAD_UPDATE':
|
elif self.action == 'DOWNLOAD_UPDATE':
|
||||||
|
|
@ -141,50 +124,18 @@ class WorkerThread(QThread):
|
||||||
f"An error occurred when installing {self.package_name}: {e}")
|
f"An error occurred when installing {self.package_name}: {e}")
|
||||||
self.finished.emit(False)
|
self.finished.emit(False)
|
||||||
|
|
||||||
def setup_singbox_binary(self):
|
|
||||||
connection_error = "Connection problems downloading Singbox or related data. Please disable Tor or try again with a better connection."
|
|
||||||
try:
|
|
||||||
setup_result = install_singbox_binary(application_version_observer, connection_observer)
|
|
||||||
except ConnectionError as e:
|
|
||||||
self.text_output.emit(f"{connection_error}: {str(e)}")
|
|
||||||
self.finished.emit(False)
|
|
||||||
return
|
|
||||||
except ValueError as e:
|
|
||||||
self.text_output.emit(f"Your configuration files may be corrupted, or a server-side error gave bad data: {str(e)}")
|
|
||||||
self.finished.emit(False)
|
|
||||||
return
|
|
||||||
except Exception as e:
|
|
||||||
self.text_output.emit(f"Unknown error: {str(e)}")
|
|
||||||
self.finished.emit(False)
|
|
||||||
return
|
|
||||||
|
|
||||||
if setup_result.valid:
|
|
||||||
self.text_output.emit("Setup done. You're all set to proceed with Singbox.")
|
|
||||||
self.finished.emit(True)
|
|
||||||
return
|
|
||||||
|
|
||||||
messages = {
|
|
||||||
ResultError.NEED_SYNC: "You must sync to find out which Singbox version is supported.",
|
|
||||||
ResultError.FILE_SYSTEM: "Please check the configuration file, disk space, permissions, and filesystem health.",
|
|
||||||
ResultError.CONNECTION: connection_error,
|
|
||||||
ResultError.INVALID_INPUT: "This is a rare bug. Check the error logs, then run the program again from the terminal with DEBUG=true.",
|
|
||||||
ResultError.PERMISSION: "Singbox requires sudo for setup. After that, the wrapper allows it to run without sudo on an ongoing basis.",
|
|
||||||
ResultError.MISSING_FILE: "Singbox download or file setup did not complete. Please try again.",
|
|
||||||
ResultError.UNKNOWN: f"Unknown error: {setup_result.message}",
|
|
||||||
}
|
|
||||||
self.text_output.emit(messages.get(setup_result.error_type, setup_result.message or "Unknown Singbox setup error"))
|
|
||||||
self.finished.emit(False)
|
|
||||||
|
|
||||||
def disable_all_profiles(self):
|
def disable_all_profiles(self):
|
||||||
try:
|
try:
|
||||||
for profile_id in self.profile_data:
|
for profile_id in self.profile_data:
|
||||||
profile = ProfileController.get(int(profile_id))
|
profile = ProfileController.get(int(profile_id))
|
||||||
if isinstance(profile, SessionProfile):
|
if isinstance(profile, SessionProfile):
|
||||||
self._disable_profile(profile)
|
ProfileController.disable(
|
||||||
|
profile, ignore=True, profile_observer=profile_observer, ticket_observer=ticket_observer, connection_observer=connection_observer)
|
||||||
for profile_id in self.profile_data:
|
for profile_id in self.profile_data:
|
||||||
profile = ProfileController.get(int(profile_id))
|
profile = ProfileController.get(int(profile_id))
|
||||||
if isinstance(profile, SystemProfile):
|
if isinstance(profile, SystemProfile):
|
||||||
self._disable_profile(profile)
|
ProfileController.disable(
|
||||||
|
profile, ignore=True, profile_observer=profile_observer, ticket_observer=ticket_observer, connection_observer=connection_observer)
|
||||||
self.text_output.emit("All profiles were successfully disabled")
|
self.text_output.emit("All profiles were successfully disabled")
|
||||||
except SudoScript as e:
|
except SudoScript as e:
|
||||||
self.text_output.emit(str(e))
|
self.text_output.emit(str(e))
|
||||||
|
|
@ -293,7 +244,8 @@ class WorkerThread(QThread):
|
||||||
try:
|
try:
|
||||||
profile = ProfileController.get(int(self.profile_data['id']))
|
profile = ProfileController.get(int(self.profile_data['id']))
|
||||||
if profile:
|
if profile:
|
||||||
self._disable_profile(profile)
|
ProfileController.disable(
|
||||||
|
profile, profile_observer=profile_observer, ticket_observer=ticket_observer, connection_observer=connection_observer, wipe_assassin=True)
|
||||||
else:
|
else:
|
||||||
self.text_output.emit(
|
self.text_output.emit(
|
||||||
f"No profile found with ID: {self.profile_data['id']}")
|
f"No profile found with ID: {self.profile_data['id']}")
|
||||||
|
|
|
||||||