# Loading and using the config from local_data_operations.search_or_setup_json import search_key_in_json from local_data_operations.load_or_setup_config import load_or_setup_config, replace_config_value, setup_path_to_dump_initial_api_data, load_or_setup_local_api_data_folder from local_data_operations.local_logs import get_profile_location_and_proxy_status_as_tuple, create_or_add_to_profile_config, return_all_locally_saved_orders_for_the_current_profile, delete_an_order, setup_email_config, get_email_data, wipe_email_data, wipe_location_and_proxy, wipe_entire_profile_data # generate buyer id: from local_data_operations.generate_random_number_string import generate_random_number_string # manipulating data from the API: from process_api_data.extract_a_list_of_values import extract_a_list_of_values, get_the_locations_for_a_service from process_api_data.read_from_text_file_into_list import read_from_text_file_into_list from process_api_data.convert_to_dictonary import search_by_category # manipulating email data to send or recieve from the API: from api_interaction.get_initial_email_data import get_initial_email_data from process_email_data.filter_email_names import filter_email_names from process_email_data.load_email_operators import load_email_operators # coordinating with API: from api_interaction.send_data_to_flask import send_data_to_flask, dispute_send_data_to_flask, check_if_paid_by_sending_data_to_flask, ready_for_code_by_sending_data_to_flask, send_crypto_address_for_refund, restore_an_already_finalized_number from api_interaction.get_and_save_json_from_our_api import get_and_save_json_from_our_api from api_interaction.should_api_be_triggered import should_api_be_triggered # process the proxy: from interact_with_rest_of_app.process_the_proxy import process_the_proxy # Generic imports: # async: import aiohttp from aiohttp import ClientConnectorError, ClientResponseError import asyncio # basic tools: import os import sys import json # error log, which line: import traceback # GUI visual: from PyQt6.QtWidgets import QApplication, QWidget, QVBoxLayout, QLabel, QComboBox, QPushButton, QSizePolicy, QMessageBox, QListWidget, QGridLayout, QHBoxLayout, QComboBox, QMainWindow, QInputDialog, QLineEdit from qasync import QEventLoop, asyncSlot from PyQt6.QtCore import QCoreApplication, QTimer # FOR DEBUG UNCOMMENT, if I want to start tracing memory allocations import tracemalloc tracemalloc.start() class BuyerPeerPeerGUI(QWidget): def __init__(self): super().__init__() # Name of the config file for entire app: self.name_of_config_file = "sms_config.json" # Load Config data for entire rest of the app to be searched with the 'search_key_in_json' function: self.config_file_data = load_or_setup_config(self.name_of_config_file) # for api data: self.folder_for_api_data = load_or_setup_local_api_data_folder() self.email_file_path = f"{self.folder_for_api_data}/email_operators.json" self.filename_for_locations_data = f"{self.folder_for_api_data}/services_by_type_and_location.json" # Commented out format to search config: # result = search_key_in_json(config_raw_data, "key", default) # setup error logs: self.error_log_file_path = search_key_in_json( self.config_file_data, "error_log_file_path", "error_log_for_gui.txt") self.setWindowTitle("SMS Exchange | HydraVeil") self.setFixedSize(800, 600) self.move(100, 100) # Set the overall style self.setStyleSheet("background-color: black; color: white;") # set styles for entire project: self.active_style_box = """ background-color: #282828; /* Dark background for contrast */ color: #FFD700; /* Gold color for text */ font-size: 30px; font-family: 'Press Start 2P', cursive; /* Retro font */ border: 3px solid #00FF00; /* Bright green border */ padding: 15px; /* Increased padding */ text-align: center; /* Centering text */ """ self.lowkey_style_box = """ background-color: #444444; /* Darker grey for a muted effect */ color: #A9A9A9; /* Light grey for text */ font-size: 30px; font-family: 'Press Start 2P', cursive; /* Keeping the retro font */ border: none; /* No border */ padding: 15px; /* Maintained padding */ text-align: center; /* Centering text */ """ self.blue_chill_style = """ QPushButton { font-size: 30px; font-weight: normal; color: #007BFF; /* Bright blue text */ background-color: transparent; /* No background */ border: 2px solid #007BFF; /* Blue border */ border-radius: 5px; /* Slightly rounded corners */ padding: 10px 15px; /* Vertical and horizontal padding */ } QPushButton:hover { background-color: rgba(0, 123, 255, 0.1); /* Light blue background on hover */ } QPushButton:pressed { background-color: rgba(0, 123, 255, 0.2); /* Darker blue when pressed */ } """ # Create layout self.layout = QVBoxLayout() # This label style is used in many spots throughout the various pages: self.label_style = "font-size: 40px; color: white;" # Set layout to the main window: self.setLayout(self.layout) # Display to the end-user that it's fetching data, although technically it didn't start doing it yet: self.label_for_api_fetch = QLabel(f"Fetching Available Services") self.label_for_api_fetch.setStyleSheet(self.label_style) self.label_for_api_fetch.setSizePolicy( # Expanding vertical size policy QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) self.layout.addWidget(self.label_for_api_fetch) # We don't want the GUI to not load until after the API call is done, so we schedule an async function to run after the GUI is shown, QTimer.singleShot(0, self.step_1_run_api_async) # Set layout to the main window self.setLayout(self.layout) # Start basic functions section: def show_error_message(self, message): msg_box = QMessageBox(self) msg_box.setIcon(QMessageBox.Icon.Warning) msg_box.setText(message) msg_box.setWindowTitle('Error') msg_box.exec() def log_an_error(self, error): with open(self.error_log_file_path, 'a') as file: file.write(f'{error}\n') # End basic functions section # We're getting the data from an API, and displaying either the data in lists, or an error message of why it failed to get the data, @asyncSlot() async def step_1_run_api_async(self): period_of_time = 3600 # an hour # does the user want to use existing data or new data? are_we_doing_an_api_call = should_api_be_triggered( self.filename_for_locations_data, period_of_time) if are_we_doing_an_api_call == False: # we need to do an API call to get fresh data: await self.step_2_actually_fetch_data_from_api() else: # do NOT do the API call, skip to using the existing local data: self.after_fetched_data_from_api_is_done() async def step_2_actually_fetch_data_from_api(self): non_binary_of_did_it_get_it_from_api_or_not = await get_and_save_json_from_our_api(self.filename_for_locations_data) if non_binary_of_did_it_get_it_from_api_or_not == "worked": self.label_for_api_fetch.setText("Fetching Email Operators") next_get_email_data = await get_initial_email_data(self.email_file_path) if next_get_email_data == "worked": self.after_fetched_data_from_api_is_done() else: self.display_error_message_from_failed_connection( next_get_email_data) else: self.display_error_message_from_failed_connection( non_binary_of_did_it_get_it_from_api_or_not) def display_error_message_from_failed_connection(self, error_message_content): # Format the Label for a long message, and Display the Error of why it failed to get the data: self.label_for_api_fetch.setStyleSheet("font-size: 40px;") self.label_for_api_fetch.setWordWrap(True) self.label_for_api_fetch.setFixedWidth(800) self.label_for_api_fetch.setText(str(error_message_content)) def after_fetched_data_from_api_is_done(self): try: # Check if the label exists and is not already hidden if self.label_for_api_fetch is not None: if not self.label_for_api_fetch.isHidden(): self.label_for_api_fetch.hide() else: pass except Exception as e: pass # self.show_error_message(f"An unexpected error occurred: {e}") # self.log_an_error(e) # Generate ID: self.peer_to_peer_billing_id = generate_random_number_string() # Display the ID (with that label style declared in the init section): label_for_peer_to_peer_billing_id = QLabel( f"Your P2P ID is: {self.peer_to_peer_billing_id}") label_for_peer_to_peer_billing_id.setStyleSheet(self.label_style) label_for_peer_to_peer_billing_id.setSizePolicy( # Expanding vertical size policy QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) self.layout.addWidget(label_for_peer_to_peer_billing_id) # Create a horizontal layout for the two QListWidgets self.top_ribbon_h_layout = QHBoxLayout() # Add the left column's label, above box: self.left_label_above_box = QLabel("General Categories") smaller_sub_label_style = "font-size: 30px; color: grey;" self.left_label_above_box.setStyleSheet(smaller_sub_label_style) # Add the right column's label, above box: self.right_label_above_box = QLabel("Specific Sites") self.right_label_above_box.setStyleSheet(smaller_sub_label_style) self.top_ribbon_h_layout.addWidget(self.left_label_above_box) self.top_ribbon_h_layout.addWidget(self.right_label_above_box) self.layout.addLayout(self.top_ribbon_h_layout) # Create a horizontal layout for the two QListWidgets self.h_layout = QHBoxLayout() # The initial API call saved the initial data JSON to this path: with open(self.filename_for_locations_data, 'r') as file: self.initial_data = json.load(file) # Each category on the left, gets the filtered values of the data on the right self.social_list = extract_a_list_of_values( self.initial_data, "social") self.big_tech_list = extract_a_list_of_values( self.initial_data, "big_tech") self.business_list = extract_a_list_of_values( self.initial_data, "business") self.travel_list = extract_a_list_of_values( self.initial_data, "travel") self.AI_list = extract_a_list_of_values(self.initial_data, "AI") # Mapping of items to their corresponding lists self.list_mapping = { 'social_media': self.social_list, 'big_tech': self.big_tech_list, 'business': self.business_list, 'travel': self.travel_list, 'AI': self.AI_list } # This is the left category column: self.list_widget1 = QListWidget() # Add items to list_widget1 self.list_widget1.addItems(self.list_mapping.keys()) self.list_widget1.setSelectionMode( QListWidget.SelectionMode.SingleSelection) self.list_widget1.setStyleSheet( "background-color: grey; color: white; font-size: 26px;") # Create the second QListWidget self.list_widget2 = QListWidget() self.list_widget2.setSelectionMode( QListWidget.SelectionMode.SingleSelection) self.list_widget2.setStyleSheet( "background-color: grey; color: white; font-size: 26px;") # Add the QListWidgets to the horizontal layout self.h_layout.addWidget(self.list_widget1) self.h_layout.addWidget(self.list_widget2) # Set the QListWidget style with larger font size self.list_widget1.setStyleSheet( # Adjust font size as needed "background-color: grey; color: white; font-size: 26px;") self.list_widget2.setStyleSheet( # Adjust font size as needed "background-color: grey; color: white; font-size: 26px;") # Connect the item selection change signal to the update method self.list_widget1.itemClicked.connect(self.on_item_clicked) self.list_widget2.itemClicked.connect(self.on_right_item_clicked) # Create dropdown menus: self.payment_method_dropdown = QComboBox() self.location_dropdown = QComboBox() self.email_dropdown = QComboBox() # Populate dropdowns with defaults: # Start Cryptocurrency dropdown section: default_currency = search_key_in_json( self.config_file_data, "default_currency", False) if default_currency == False: payment_methods_list = [ 'Monero', "Bitcoin_Lightning", "Litecoin", "Bitcoin"] else: payment_methods_list = [default_currency] self.payment_method_dropdown.addItems(payment_methods_list) # End Cryptocurrency dropdown section # Setup Location dropdown: locations_list = ["Pick a Location"] self.location_dropdown.addItems(locations_list) # end Locations dropdown # Start EMAIL dropdown section: # do we already have email data? packed_email_data = get_email_data() # calls function from local_logs.py # if we do NOT yet have an email: if packed_email_data == False or packed_email_data == "error": self.turn_off_email_option = search_key_in_json( self.config_file_data, "turn_off_email_option", False) # did the user disable email in the options? if self.turn_off_email_option == False: email_operators_list = load_email_operators( self.email_file_path) else: email_operators_list = ["email disabled"] # else we already have an email for this profile: else: email_operators_list = ["profile already has an email"] if email_operators_list is None: empty_list = ["random"] self.email_dropdown.addItems(empty_list) else: self.email_dropdown.addItems(email_operators_list) # end Email dropdown # Set text color and font size for dropdowns: self.payment_method_dropdown.setStyleSheet( "color: white; background-color: green; font-size: 24px;") self.location_dropdown.setStyleSheet( "color: white; background-color: red; font-size: 24px;") self.email_dropdown.setStyleSheet( "color: black; background-color: white; font-size: 20px;") # display the profile status once they select a service: self.label_for_profile_status = QLabel(f"Profile Status") self.label_for_profile_status.setStyleSheet( "font-size: 15px; color: white;") self.label_for_profile_status.setWordWrap(True) self.label_for_profile_status.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) # for now hide it. But later, this will appear after they select a service, self.label_for_profile_status.setVisible(False) # Create a button to show selected items self.button = QPushButton("Residential Proxy, SMS, and Email! (€11)") # Set background and text color self.button.clicked.connect(self.show_selected) self.button.setStyleSheet( "font-size: 30px; padding: 15px; background-color: green; color: yellow;") self.button.clicked.connect(self.on_button_click) # Button to clear selections self.just_sms_button = QPushButton("SMS Only (€5)") # self.just_sms_button.setStyleSheet("font-size: 20px; padding: 15px; background-color: grey; color: black;") # Set background and text color self.button.clicked.connect(self.show_selected) self.just_sms_button.setStyleSheet(""" QPushButton { font-size: 20px; font-weight: bold; color: #218838; /* dark green text */ background-color: #ffd700; /* Gold background */ border: 2px solid #ffcc00; /* Light gold border */ border-radius: 25px; /* Rounded corners */ padding: 15px 30px; /* Generous padding */ text-align: center; /* Center the text */ } QPushButton:hover { background-color: #f0c36d; /* Lighter gold on hover */ border-color: #ffdb58; /* Change border color on hover */ } QPushButton:pressed { background-color: #c8a23b; /* Darker gold when pressed */ border-color: #b89a26; /* Darker border when pressed */ } """) self.just_sms_button.clicked.connect(self.just_sms_no_proxy) # Create a button for more options, including to allow the previous sessions to be restored: self.more_options_button = QPushButton("More Options") # self.more_options_button.setStyleSheet("font-size: 15px; padding: 10px; background-color: grey; color: black;") # Set background and text color self.button.clicked.connect(self.show_selected) self.more_options_button.clicked.connect( self.update_ui_to_display_more_options) self.more_options_button.setStyleSheet(self.blue_chill_style) # Add widgets to layout: self.layout.addLayout(self.h_layout) self.layout.addWidget(self.payment_method_dropdown) self.layout.addWidget(self.location_dropdown) self.layout.addWidget(self.email_dropdown) self.layout.addWidget(self.label_for_profile_status) self.layout.addWidget(self.button) self.layout.addWidget(self.just_sms_button) # but hide the send button till they fill in the fields: self.button.setVisible(False) self.just_sms_button.setVisible(False) self.layout.addWidget(self.more_options_button) self.service_literally_clicked_already = "no" # Start: Display More Options Menu: def update_ui_to_display_more_options(self): try: # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Clear all widgets from the horizontal layout for i in reversed(range(self.h_layout.count())): item = self.h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() for i in reversed(range(self.top_ribbon_h_layout.count())): item = self.top_ribbon_h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() except: pass finally: # Get Locally Saved Profile Data: profile_location_and_proxy_tuple = get_profile_location_and_proxy_status_as_tuple() # Find out if it's a Pre-existing Profile. It's possible the person went to the menu before selecting a service if profile_location_and_proxy_tuple == False: profile_menu_data = "Brand New Profile.\nNo Location Yet." else: # Pre-existing profiles have a location already,: profile_location, profile_proxy_status = profile_location_and_proxy_tuple profile_menu_data = f"Profile Location: {profile_location}" # Options Menu Profile Display self.options_menu_label = QLabel(f"Menu\n{profile_menu_data}") self.options_menu_label.setStyleSheet(self.active_style_box) self.options_menu_label.setWordWrap(True) self.layout.addWidget(self.options_menu_label) # restore previous interrupted session button: self.restore_previous_button = QPushButton( "Restore a Previous Session") self.restore_previous_button.setStyleSheet( "font-size: 40px; padding: 10px; background-color: green; color: yellow;") self.restore_previous_button.clicked.connect( self.restore_previous_session) self.layout.addWidget(self.restore_previous_button) # email options button: self.get_only_an_email_button = QPushButton("Email Options") self.get_only_an_email_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: blue;") self.get_only_an_email_button.clicked.connect( self.email_options_ui) self.layout.addWidget(self.get_only_an_email_button) # proxy info button: self.proxy_options_button = QPushButton( "Proxy and Profile Options") self.proxy_options_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: black; color: white;") self.proxy_options_button.clicked.connect( self.update_ui_for_proxy_data) self.layout.addWidget(self.proxy_options_button) # disable local logs button: self.disable_local_logs_button = QPushButton("SMS Preferences") self.disable_local_logs_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: white; color: black;") self.disable_local_logs_button.clicked.connect( self.update_ui_to_change_local_logs) self.layout.addWidget(self.disable_local_logs_button) # change default cryptocurrency button: self.change_default_currency_button = QPushButton( "Change Default Cryptocurrency") self.change_default_currency_button.setStyleSheet( self.blue_chill_style) self.change_default_currency_button.clicked.connect( self.update_ui_to_change_default_currency) self.layout.addWidget(self.change_default_currency_button) # frequency button: # self.connection_frequency_button = QPushButton("Connection Frequency") # self.connection_frequency_button.setStyleSheet("font-size: 30px; padding: 10px; background-color: white; color: black;") # self.connection_frequency_button.clicked.connect(self.update_ui_to_change_local_logs) # self.layout.addWidget(self.connection_frequency_button) # return to main page button: self.return_to_the_main_page_button = QPushButton( "Return to the Main Page") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: green; color: yellow;") self.return_to_the_main_page_button.clicked.connect( self.update_ui_to_return_to_the_main_page) self.layout.addWidget(self.return_to_the_main_page_button) def update_ui_for_proxy_data(self): self.clear_the_main_page_ui() # Options Menu Profile Display self.options_menu_label = QLabel(f"Proxy Menu") self.options_menu_label.setStyleSheet(self.active_style_box) self.options_menu_label.setWordWrap(True) self.layout.addWidget(self.options_menu_label) # Get Locally Saved Profile Data: profile_location_and_proxy_tuple = get_profile_location_and_proxy_status_as_tuple() # Find out if it's a Pre-existing Profile. It's possible the person went to the menu before selecting a service if profile_location_and_proxy_tuple == False: profile_menu_data = "This Profile does not have a Location Yet." else: # Pre-existing profiles have a location already,: profile_location, profile_proxy_status = profile_location_and_proxy_tuple profile_menu_data = f"Profile Location: {profile_location}" # proxy data wipe button: self.proxy_wipe_button = QPushButton("Delete Proxy Only") self.proxy_wipe_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: white;") self.proxy_wipe_button.clicked.connect(self.wipe_email_data_in_gui) self.layout.addWidget(self.proxy_wipe_button) # proxy data wipe button: self.wipe_entire_profile_button = QPushButton( "Wipe entire Profile") self.wipe_entire_profile_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: black;") self.wipe_entire_profile_button.clicked.connect( self.wipe_entire_profile_in_gui) self.layout.addWidget(self.wipe_entire_profile_button) self.options_menu_label.setText(f"{profile_menu_data}") # return to main page button: self.return_to_the_main_page_button = QPushButton( "Return to the Main Page") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: green; color: yellow;") self.return_to_the_main_page_button.clicked.connect( self.update_ui_to_return_to_the_main_page) self.layout.addWidget(self.return_to_the_main_page_button) def wipe_entire_profile_in_gui(self): # Create a message box with warning icon msg_box = QMessageBox() msg_box.setIcon(QMessageBox.Icon.Warning) msg_box.setText( "Are you sure you want to delete your proxy and SMS data?") msg_box.setWindowTitle("Confirmation") # delete/cancel buttons delete_button = msg_box.addButton( "Delete", QMessageBox.ButtonRole.AcceptRole) msg_box.addButton("Cancel", QMessageBox.ButtonRole.RejectRole) msg_box.exec() # Determine which button was pressed if msg_box.clickedButton() == delete_button: print("delete") did_it_wipe = wipe_entire_profile_data() if did_it_wipe == True: self.options_menu_label.setText("Blank Location") else: self.options_menu_label.setText( "Error! Profile wipe failed, please do it manually.") else: pass # Do nothing on Cancel def wipe_email_data_in_gui(self): # call the function from local_logs module. (it returns true if deletes it OR also true if it doesn't exist) did_it_wipe = wipe_location_and_proxy() if did_it_wipe == True: self.update_ui_to_return_to_the_main_page() else: self.options_menu_label.setText( "Error! Failed to Wipe Location Data") def update_ui_to_return_to_the_main_page(self): try: # Call the method to remove the grid layout self.remove_grid_layout() except: pass try: # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() except: pass finally: self.after_fetched_data_from_api_is_done() def clear_the_main_page_ui(self): try: # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() except: pass def update_ui_to_change_default_currency(self): self.clear_the_main_page_ui() # Label to display the email: self.instructions_for_currency = QLabel( f"Select From the Dropdown Below:") self.instructions_for_currency.setStyleSheet(self.active_style_box) self.instructions_for_currency.setWordWrap(True) self.layout.addWidget(self.instructions_for_currency) self.payment_method_dropdown = QComboBox() self.payment_method_dropdown.setStyleSheet( "color: white; background-color: green; font-size: 50px;") payment_methods_list = [ 'Monero', "Bitcoin_Lightning", "Litecoin", "Bitcoin"] self.payment_method_dropdown.clear() self.payment_method_dropdown.addItems(payment_methods_list) self.payment_method_dropdown.activated.connect( self.update_ui_for_currency_pick) self.layout.addWidget(self.payment_method_dropdown) self.instructions_for_currency_two = QLabel(f"Then:") self.instructions_for_currency_two.setStyleSheet(self.lowkey_style_box) self.instructions_for_currency_two.setWordWrap(True) self.layout.addWidget(self.instructions_for_currency_two) # return to main page button: self.return_to_the_main_page_button = QPushButton("Apply the Change") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: green; color: yellow;") self.return_to_the_main_page_button.clicked.connect( self.implement_the_default_currency_change) self.layout.addWidget(self.return_to_the_main_page_button) # return to main page button: self.return_to_the_main_page_button = QPushButton( "Nevermind. Return to Main Page") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: black;") self.return_to_the_main_page_button.clicked.connect( self.update_ui_to_return_to_the_main_page) self.layout.addWidget(self.return_to_the_main_page_button) def update_ui_for_currency_pick(self): self.instructions_for_currency.setStyleSheet(self.lowkey_style_box) self.instructions_for_currency_two.setStyleSheet(self.active_style_box) def implement_the_default_currency_change(self): chosen_payment_method = self.payment_method_dropdown.currentText() # It's calling on a module to change the config JSON: self.config_file_data = replace_config_value( self.name_of_config_file, "default_currency", chosen_payment_method) # return back: self.update_ui_to_return_to_the_main_page() def email_options_ui(self): self.clear_the_main_page_ui() self.zero_set_of_instructions = QLabel( f"Do you want to see options for THIS profile or ALL profiles?") self.zero_set_of_instructions.setStyleSheet(self.active_style_box) self.zero_set_of_instructions.setWordWrap(True) self.layout.addWidget(self.zero_set_of_instructions) self.display_email_data_button = QPushButton( "THIS Profile's Email Data") self.display_email_data_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: blue; color: black;") self.display_email_data_button.clicked.connect( self.display_profile_email_data) self.layout.addWidget(self.display_email_data_button) self.all_profiles_email_options_button = QPushButton( "ALL profiles Email Options") self.all_profiles_email_options_button.setStyleSheet( "font-size: 30px; padding: 20px; background-color: black; color: white;") self.all_profiles_email_options_button.clicked.connect( self.all_profile_email_options_ui) self.layout.addWidget(self.all_profiles_email_options_button) # return to main page button: self.return_to_the_main_page_button = QPushButton( "Nevermind. Return to Main Page") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: black;") self.return_to_the_main_page_button.clicked.connect( self.update_ui_to_return_to_the_main_page) self.layout.addWidget(self.return_to_the_main_page_button) def display_profile_email_data(self): self.clear_the_main_page_ui() # this is a default flag: we_have_email_data = False # by default packed_email_data = get_email_data() # calls function from local_logs.py if packed_email_data == False: message_for_end_user = "Your current profile has no email data yet." elif packed_email_data == "error": message_for_end_user = "There was some kind of internal error with loading the email data" else: we_have_email_data = True self.email_operator_in_GUI, self.full_email_in_GUI, self.email_url_in_GUI, self.email_password_in_GUI = packed_email_data message_for_end_user = f""" This Profile has an Email: Email Address: {self.full_email_in_GUI} Web Client URL: {self.email_url_in_GUI} Server Operator: {self.email_operator_in_GUI} """ # First Label displays errors OR data self.first_set_of_instructions = QLabel(f"{message_for_end_user}") self.first_set_of_instructions.setStyleSheet(self.active_style_box) self.first_set_of_instructions.setWordWrap(True) self.layout.addWidget(self.first_set_of_instructions) if we_have_email_data == True: # Label to copy-paste the email url: self.copy_email_url_button = QPushButton("Copy Web Client URL") self.copy_email_url_button.setStyleSheet( "font-size: 15px; padding: 15px; background-color: green; color: yellow;") self.copy_email_url_button.clicked.connect( self.copy_email_url_function) self.layout.addWidget(self.copy_email_url_button) # Label to copy-paste the email: self.copy_full_email_button = QPushButton("Copy Email") self.copy_full_email_button.setStyleSheet( "font-size: 20px; padding: 15px; background-color: green; color: yellow;") self.copy_full_email_button.clicked.connect( self.copy_full_email_function) self.layout.addWidget(self.copy_full_email_button) # Label to copy-paste the email password: self.copy_email_password_button = QPushButton( "Copy Email's Password") self.copy_email_password_button.setStyleSheet( "font-size: 20px; padding: 15px; background-color: green; color: yellow;") self.copy_email_password_button.clicked.connect( self.copy_email_password_function) self.layout.addWidget(self.copy_email_password_button) self.wipe_email_data_button = QPushButton("Wipe this Email Data") self.wipe_email_data_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: grey;") self.wipe_email_data_button.clicked.connect( self.call_upon_wipe_email_data_function) self.layout.addWidget(self.wipe_email_data_button) # return to main page button: self.return_to_the_main_page_button = QPushButton( "Return to Main Page") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: black;") self.return_to_the_main_page_button.clicked.connect( self.update_ui_to_return_to_the_main_page) self.layout.addWidget(self.return_to_the_main_page_button) def call_upon_wipe_email_data_function(self): did_it_wipe = wipe_email_data() if did_it_wipe == True: message_for_end_user = "Hillary Clinton would be proud. Email data wiped." self.copy_email_url_button.setVisible(False) self.copy_full_email_button.setVisible(False) self.copy_email_password_button.setVisible(False) self.wipe_email_data_button.setVisible(False) else: message_for_end_user = "Some kind of File Error. Try to delete it manually" self.first_set_of_instructions.setText(message_for_end_user) def all_profile_email_options_ui(self): self.clear_the_main_page_ui() # do we have email data? packed_email_data = get_email_data() # calls function from local_logs.py if packed_email_data == False or packed_email_data == "error": email_display_message = f"Do you want email to continue to be offered on the main menu for ALL profiles?" else: email_display_message = f"This profile already has an email. But for OTHER profiles, do you want to keep it enabled or disable it?" # Label to display instructions self.first_set_of_instructions = QLabel(f"{email_display_message}") self.first_set_of_instructions.setStyleSheet(self.active_style_box) self.first_set_of_instructions.setWordWrap(True) self.layout.addWidget(self.first_set_of_instructions) self.email_preference_dropdown = QComboBox() self.email_preference_dropdown.setStyleSheet( "color: white; background-color: green; font-size: 50px;") list_of_email_preferences = ["yes, enable", "no, disable"] self.email_preference_dropdown.clear() self.email_preference_dropdown.addItems(list_of_email_preferences) self.email_preference_dropdown.activated.connect( self.show_the_apply_change_button) self.layout.addWidget(self.email_preference_dropdown) self.second_set_of_instructions = QLabel(f"Then Apply it:") self.second_set_of_instructions.setStyleSheet(self.active_style_box) self.second_set_of_instructions.setWordWrap(True) self.second_set_of_instructions.setVisible(False) self.layout.addWidget(self.second_set_of_instructions) self.save_preferences_button = QPushButton("Apply the Change") self.save_preferences_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: green; color: yellow;") self.save_preferences_button.clicked.connect( self.implement_email_disabled_change) self.save_preferences_button.setVisible(False) self.layout.addWidget(self.save_preferences_button) # return to main page button: self.return_to_the_main_page_button = QPushButton( "Nevermind. Return to Main Page") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: black;") self.return_to_the_main_page_button.clicked.connect( self.update_ui_to_return_to_the_main_page) self.layout.addWidget(self.return_to_the_main_page_button) def update_ui_to_change_local_logs(self): self.clear_the_main_page_ui() # Label to display change instructions self.first_set_of_instructions = QLabel( f"When you get an SMS code, your local computer saves the billing code so you can restore a session if disconnected, or potentially try to get the same phone number again in a future emergency. GOING FORWARD, Do you want to save this data locally? Select from the Green Dropdown below:") self.first_set_of_instructions.setStyleSheet(self.active_style_box) self.first_set_of_instructions.setWordWrap(True) self.layout.addWidget(self.first_set_of_instructions) self.local_data_preference_dropdown = QComboBox() self.local_data_preference_dropdown.setStyleSheet( "color: white; background-color: green; font-size: 50px;") list_of_local_data_preferences = ["save codes", "wipe codes"] self.local_data_preference_dropdown.clear() self.local_data_preference_dropdown.addItems( list_of_local_data_preferences) self.local_data_preference_dropdown.activated.connect( self.show_the_apply_change_button) self.layout.addWidget(self.local_data_preference_dropdown) self.second_set_of_instructions = QLabel(f"Then Apply it:") self.second_set_of_instructions.setStyleSheet(self.active_style_box) self.second_set_of_instructions.setWordWrap(True) self.second_set_of_instructions.setVisible(False) self.layout.addWidget(self.second_set_of_instructions) self.save_preferences_button = QPushButton("Apply the Change") self.save_preferences_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: green; color: yellow;") self.save_preferences_button.clicked.connect( self.implement_local_data_saving_change) self.save_preferences_button.setVisible(False) self.layout.addWidget(self.save_preferences_button) # return to main page button: self.return_to_the_main_page_button = QPushButton( "Nevermind. Return to Main Page") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: black;") self.return_to_the_main_page_button.clicked.connect( self.update_ui_to_return_to_the_main_page) self.layout.addWidget(self.return_to_the_main_page_button) def show_the_apply_change_button(self): self.first_set_of_instructions.setStyleSheet(self.lowkey_style_box) self.second_set_of_instructions.setVisible(True) self.save_preferences_button.setVisible(True) def implement_local_data_saving_change(self): chosen_log_choice = self.local_data_preference_dropdown.currentText() if chosen_log_choice == "wipe codes": update_config_with = False else: update_config_with = True # It's calling on a module to change the config JSON: self.config_file_data = replace_config_value( self.name_of_config_file, "local_data", update_config_with) # return back: self.update_ui_to_return_to_the_main_page() def implement_email_disabled_change(self): chosen_email_preference = self.email_preference_dropdown.currentText() list_of_email_preferences = ["yes, enable", "no, disable"] if chosen_email_preference == "no, disable": turn_off_email_option = True else: turn_off_email_option = False # It's calling on a module to change the config JSON: self.config_file_data = replace_config_value( self.name_of_config_file, "turn_off_email_option", turn_off_email_option) # return back: self.update_ui_to_return_to_the_main_page() def restore_previous_session(self): # get a list of all the locally saved past orders: past_orders_data = return_all_locally_saved_orders_for_the_current_profile() # setup a style for this page: self.hipster_ecommerce_style = """ QLabel { font-size: 14px; font-weight: bold; color: #333; /* Dark gray for text */ background-color: #f8f8f8; /* Light gray background */ border: 2px solid #007BFF; /* Blue border */ border-radius: 10px; /* Rounded corners */ padding: 10px; /* Padding for a spacious look */ margin: 5px; /* Margin to separate from other elements */ } QLabel:hover { background-color: #e0e0e0; /* Slightly darker gray on hover */ border-color: #0056b3; /* Darker blue border on hover */ } """ # error check if there are no past orders, or it's a new profile: if past_orders_data == False: self.clear_the_main_page_ui() sorry_no_data_message = "I'm sorry, but there is no local SMS/proxy data for this profile. This could be a brand new profile, or an error with accessing your local log files." sorry_no_data_label = QLabel(sorry_no_data_message) sorry_no_data_label.setStyleSheet(self.hipster_ecommerce_style) sorry_no_data_label.setWordWrap(True) self.layout.addWidget(sorry_no_data_label) else: self.clear_the_main_page_ui() # inform them it's on their local PC: self.local_computer_data_label = QLabel( f"This is data on your local computer. Btw you can't restore a Session you didn't pay for yet.") self.local_computer_data_label.setStyleSheet(self.active_style_box) self.local_computer_data_label.setWordWrap(True) self.layout.addWidget(self.local_computer_data_label) counter_of_how_many_rows = 0 # prep a grid layout to put them in: self.grid_of_orders = QGridLayout() # Create labels and buttons for each order in the list: for each_order in past_orders_data: # prep data: each_billing_id, each_service, each_complete_status = each_order # prep variables to display to the end user: if each_complete_status == True: display_status_to_user = "Completed" else: display_status_to_user = "Not yet Completed" message_to_display_for_each_billing_id = f"{each_billing_id} with {each_service}" each_order_label = QLabel( message_to_display_for_each_billing_id) each_order_label.setStyleSheet(self.hipster_ecommerce_style) each_order_label.setWordWrap(True) self.grid_of_orders.addWidget( each_order_label, counter_of_how_many_rows, 0) each_order_button = QPushButton(f"Restore {each_service}") each_order_button.clicked.connect( lambda checked, text=each_billing_id: self.restore_previous_session_via_button(text)) each_order_button.setStyleSheet(""" QPushButton { font-size: 16px; font-weight: bold; color: white; /* White text */ background-color: #28a745; /* Green background */ border: none; /* No border */ border-radius: 5px; /* Slightly rounded corners */ padding: 12px 20px; /* Vertical and horizontal padding */ } QPushButton:hover { background-color: #218838; /* Darker green on hover */ } QPushButton:pressed { background-color: #1e7e34; /* Even darker green when pressed */ } """) self.grid_of_orders.addWidget( each_order_button, counter_of_how_many_rows, 1) each_order_delete_button = QPushButton(f"Delete") each_order_delete_button.clicked.connect( lambda checked, text=each_billing_id: self.delete_a_previous_session(text)) each_order_delete_button.setStyleSheet(""" QPushButton { font-size: 15px; font-weight: bold; color: white; /* White text */ background-color: #FF0000; /* red background */ border: none; /* No border */ border-radius: 5px; /* Slightly rounded corners */ padding: 12px 20px; /* Vertical and horizontal padding */ } QPushButton:hover { background-color: #FF1000; /* Darker green on hover */ } QPushButton:pressed { background-color: #1e7e34; /* Even darker green when pressed */ } """) self.grid_of_orders.addWidget( each_order_delete_button, counter_of_how_many_rows, 2) # this counter is for the grid layout: counter_of_how_many_rows = counter_of_how_many_rows + 1 try: self.layout.addLayout(self.grid_of_orders) except: print("no grid layout because it's an empty profile config.") # manually enter billing code button: self.enter_manually_button = QPushButton( "Or Manually Enter a Billing Code") self.enter_manually_button.setStyleSheet(self.blue_chill_style) self.enter_manually_button.clicked.connect( self.manually_restore_previous_session) self.layout.addWidget(self.enter_manually_button) # return to main page button: self.return_to_the_main_page_button = QPushButton( "Nevermind. Return to Main Page") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: black;") self.return_to_the_main_page_button.clicked.connect( self.update_ui_to_return_to_the_main_page) self.layout.addWidget(self.return_to_the_main_page_button) def remove_grid_layout(self): try: # Remove the grid layout from the main layout self.layout.removeItem(self.grid_of_orders) # Delete all widgets in the grid layout for i in range(self.grid_of_orders.count()): widget = self.grid_of_orders.itemAt(i).widget() if widget: widget.deleteLater() # Safely delete the widget # Delete the grid layout itself del self.grid_of_orders except: print("no grid") def delete_a_previous_session(self, order): delete_an_order(order) self.update_ui_to_return_to_the_main_page() def restore_previous_session_via_button(self, order): try: # Call the method to remove the grid layout self.remove_grid_layout() except: pass self.peer_to_peer_billing_id = order self.restore_previous_session_function(self.peer_to_peer_billing_id) # start: restore previous section def manually_restore_previous_session(self): text, ok = QInputDialog.getText( self, 'Restore Previous Session', 'Enter your billing ID:') if ok: # If the user clicked ok: self.process_input(text) def process_input(self, input_text): try: # Call the method to remove the grid layout self.remove_grid_layout() except: pass # Save the input text as a variable self.input_variable = input_text # Show the input in a message box QMessageBox.information( self, "Restore", f"You entered: {self.input_variable}") # save the input as the buyer billing id: self.peer_to_peer_billing_id = self.input_variable # commented this out for the new options menu flow: # remove the main button, since it's not being removed by the upcoming function as we return to normal flow: # self.button.deleteLater() self.restore_previous_session_function(self.peer_to_peer_billing_id) # end: restore previous section def on_right_item_clicked(self, item): # We're filling in the locations that are available for that selected service on the right column/box: selected_service = item.text() list_of_locations = get_the_locations_for_a_service( self.initial_data, selected_service) self.location_dropdown.clear() # Get Locally Saved Profile Data: profile_location_and_proxy_tuple = get_profile_location_and_proxy_status_as_tuple() # Now we find out if it's a Pre-existing Profile if profile_location_and_proxy_tuple == False: # Since it's a new profile, we need to setup a location selection: self.location_dropdown.addItems(list_of_locations) # For a new profile, now that they selected a service from the right column, show the send button at bottom: self.button.setVisible(True) self.turn_off_email_option = search_key_in_json( self.config_file_data, "turn_off_email_option", False) if self.turn_off_email_option == True: self.button.setText("Residential Proxy and SMS (€10)") # they can also get only SMS for a new profile: self.just_sms_button.setVisible(True) self.label_for_profile_status.setText( f"Based on your local data, this is a new profile, that has not yet been assigned a residential proxy") else: # Pre-existing profiles have a location already, so they don't need to pick: profile_location, profile_proxy_status = profile_location_and_proxy_tuple # check if this service is available for the pre-existing profile location: if profile_location in list_of_locations: # display that location is available: only_that_location_list = [profile_location] self.location_dropdown.addItems(only_that_location_list) # they can only buy SMS or email for this service, self.just_sms_button.setVisible(True) self.label_for_profile_status.setText( f"Your locally saved data shows this profile has a proxy already for {profile_location}. But you can get more SMS numbers or emails. If you want to pick a new location, please make a new browser fingerprint profile.") else: self.label_for_profile_status.setText( f"That service is not available for {profile_location}. Your locally saved data shows this profile has a proxy for {profile_location}") self.label_for_profile_status.setVisible(True) def on_item_clicked(self, item): # Get the selected item's text selected_item = item.text() # Retrieve the corresponding list from the mapping new_list = self.list_mapping.get(selected_item, []) self.update_list_widget(new_list) def update_list_widget(self, new_list): # this clears the existing items from the list and adds the new ones when the selection on the left changes: self.list_widget2.clear() self.list_widget2.addItems(new_list) @asyncSlot() async def on_dispute_button_click(self): asyncio.create_task(self.dispute_send_to_flask("dispute_new_seller")) @asyncSlot() async def on_yes_button_click(self): asyncio.create_task(self.dispute_send_to_flask("yes_worked")) @asyncSlot() async def just_sms_no_proxy(self): # this is for NO proxy: self.proxy_status = False # Step 1: Run the async function asyncio.create_task(self.button_pushed_send_to_flask()) # Step 2: Show dialog and hide button after a short delay QTimer.singleShot(0, self.show_dialog_and_hide_button) @asyncSlot() # Decorator to allow this method to be async async def on_button_click(self): # this is for a PROXY: self.proxy_status = True # Step 1: Run the async function asyncio.create_task(self.button_pushed_send_to_flask()) # Step 2: Show dialog and hide button after a short delay QTimer.singleShot(0, self.show_dialog_and_hide_button) def show_dialog_and_hide_button(self): self.show_dialog() try: self.button.hide() except: pass try: self.just_sms_button.hide() except: pass def show_dialog(self): msg = QMessageBox() msg.setText("Sent! Wait a few seconds") msg.exec() # Start: Send data to Flask Section: async def button_pushed_send_to_flask(self): # get data from dropdowns to send to the API: chosen_payment_method = self.payment_method_dropdown.currentText() uppercase_chosen_location = self.location_dropdown.currentText() chosen_location = uppercase_chosen_location.lower() selected_items = self.list_widget2.selectedItems() chosen_service_as_string = selected_items[0].text() # email section: uppercase_chosen_email = self.email_dropdown.currentText() self.chosen_email = filter_email_names(uppercase_chosen_email) # could uncomment for debug: # print(f"This is the chosen service as a string: {chosen_service_as_string}") # Send this data to the Flask server: returned_data = await send_data_to_flask(chosen_payment_method, chosen_location, chosen_service_as_string, self.chosen_email, self.proxy_status, self.peer_to_peer_billing_id) print(f"this is data: {returned_data}") # self.record_of_complete = True accepted_codes = [200, 201] rejected_codes = [400, 401, 404] # We're checking if it returned an error or registered the user: if 'message' in returned_data: message = returned_data['message'] if message == "Registered" or message == "registered": # It replied with crypto addresses, we're game on, # First, Reload the Config data because we want to see if the user doesn't want logs: self.config_file_data = load_or_setup_config( self.name_of_config_file) # We're checking if they want logs, and if they do we're now updating the Local Logs, to make or update the profile: did_it_work = create_or_add_to_profile_config( self.config_file_data, self.peer_to_peer_billing_id, chosen_location, self.proxy_status, chosen_service_as_string, self.chosen_email) if did_it_work == "error": print("Error with saving billing code to local log") # Update the GUI with the crypto addresses and begin process: self.update_ui(returned_data) else: # Display Errors: self.update_ui_for_errors(message) # Display Connection Errors: elif 'error' in returned_data: connection_error_is = returned_data['error'] self.update_ui_for_errors(connection_error_is) else: self.update_ui_for_errors(returned_data) return returned_data # End: Send data to Flask Section # Start: DISPUTE Send data to Flask Section: async def dispute_send_to_flask(self, how_did_it_go): # Send this data to the Flask server returned_data = await dispute_send_data_to_flask(how_did_it_go, self.peer_to_peer_billing_id) filter_returned_data_message = returned_data.get('message') # self.record_of_complete = True # not sure if I need this line anymore # Update the UI if the dispute was accepted or rejected. # filter_returned_data_message is what the API returned # how_did_it_go is how the user said it went. if how_did_it_go == "dispute_new_seller": if filter_returned_data_message == "dispute_accepted": # Dispute accepted: self.update_ui_for_dispute() else: # Dispute Rejected: self.update_ui_for_dispute_rejected() elif how_did_it_go == "yes_worked": # NOT disputed, accepted: self.update_ui_for_peaceful_finalized_acceptance() return returned_data # End: DISPUTE send to Flask Section # Start SECOND: Send data to Flask Section, CHECK PAYMENT. @asyncSlot() # Decorator to allow this method to be async async def check_if_paid_on_timer(self): # Step 1: Run the async function asyncio.create_task(self.check_payment_button_pushed_send_to_flask()) # Step 2: Show dialog and hide button after a short delay # QTimer.singleShot(0, self.show_dialog_and_hide_button) # def show_dialog_and_hide_button(self): # self.show_dialog() # self.button.hide() async def check_payment_button_pushed_send_to_flask(self): # Send this data to the Flask server returned_data = await check_if_paid_by_sending_data_to_flask(self.peer_to_peer_billing_id) print(f"this is data: {returned_data}") # self.record_of_complete = True # Update the GUI with the response on if it's paid and the SMS status: self.update_ui_after_checking_if_paid(returned_data) return returned_data def show_an_error(self, error_message): msg = QMessageBox() msg.setText(f"{error_message}") msg.exec() def update_ui_after_checking_if_paid(self, returned_data): try: if isinstance(returned_data, dict): # Directly use the dictionary returned_data_as_a_dictionary = returned_data else: # If it's a string, format it and load it as a dictionary valid_json_string = returned_data.replace("'", '"') returned_data_as_a_dictionary = json.loads(valid_json_string) # Get the data from that: self.payment_status_in_GUI = returned_data_as_a_dictionary.get( 'payment_status') self.code_status_in_GUI = returned_data_as_a_dictionary.get( 'code_status') # this is used further down, and it's for also checking if it's in the restoration flow: list_of_all_types_of_confirmed = ["confirmed", "restore_confirmed"] # if it's already given a code, move to next screen: if self.code_status_in_GUI == "send_code_to_buyer": # Stop checking if it's paid, we're past that if it's "send_code_to_buyer": self.timer.stop() # update UI to get code, and switch to code endpoint: self.update_ui_after_to_get_code() elif self.code_status_in_GUI == "already_finalized": # Stop checking if it's paid, we're past that if it's "send_code_to_buyer": self.timer.stop() # update UI to get code, and switch to code endpoint: self.bring_trade_back_from_dead() # If it's fully paid, then we're giving the proxy info, so check if paid: elif self.code_status_in_GUI == "rejected": # Stop checking if it's paid: self.timer.stop() elif self.code_status_in_GUI == "First Seller has 10 minutes to confirm": self.update_status_label.setStyleSheet(self.lowkey_style_box) self.update_sms_status_label.setStyleSheet( self.active_style_box) elif "refund_crypto" in returned_data_as_a_dictionary: # this is triggered when we're doing a refund, self.refund_crypto = returned_data_as_a_dictionary.get( 'refund_crypto') self.timer.stop() # get the refund crypto address with a text box, self.refund_crypto_text_box = QLineEdit(self) # Set the size policy to make the box bigger self.refund_crypto_text_box.setMinimumSize(300, 40) # Set the stylesheet for the grey background self.refund_crypto_text_box.setStyleSheet( "background-color: blue;") self.refund_crypto_text_box.setPlaceholderText( f"Enter your {self.refund_crypto} refund address") self.layout.addWidget(self.refund_crypto_text_box) # button to submit crypto address: self.button_to_submit_the_refund_address = QPushButton( f"Submit {self.refund_crypto} Address") self.button_to_submit_the_refund_address.setStyleSheet( "font-size: 20px; padding: 30px; background-color: green; color: yellow;") self.button_to_submit_the_refund_address.clicked.connect( self.gui_calls_send_crypto_address_for_refund) self.layout.addWidget(self.button_to_submit_the_refund_address) # If it's fully paid and confirmed, then we're moving on to the next section with giving the phone number: elif self.payment_status_in_GUI == "paid" and self.code_status_in_GUI in list_of_all_types_of_confirmed: # Get the phone number and set variable to display it: self.assigned_phone_number_in_GUI = returned_data_as_a_dictionary.get( 'assigned_phone_number') if self.code_status_in_GUI == "restore_confirmed": self.display_code_status_in_GUI = f"The Seller is Ready to Restore. If you need the Phone number, then it's {self.assigned_phone_number_in_GUI}. Hit 'copy' to proceed." else: self.display_code_status_in_GUI = f"Step 1. Go sign-up for the service. Phone number is {self.assigned_phone_number_in_GUI}" # check if there's proxy data, and if so then process it: if "proxy_ip_address" in returned_data_as_a_dictionary: process_the_proxy(returned_data_as_a_dictionary) else: print("No Proxy") # Stop checking if it's paid & confirmed: self.timer.stop() # check if there's email data, if "full_email" in returned_data_as_a_dictionary: self.full_email_in_GUI = returned_data_as_a_dictionary.get( 'full_email') self.email_password_in_GUI = returned_data_as_a_dictionary.get( 'email_password') self.email_url_in_GUI = returned_data_as_a_dictionary.get( 'email_url') self.email_operator_in_GUI = returned_data_as_a_dictionary.get( 'email_operator_name') self.display_code_status_in_GUI = f"Step 2. Phone number is {self.assigned_phone_number_in_GUI}" # save it, # if we recieved this email data in the same session as the initial order, then self.chosen_email has the operator try: did_it_create_config = setup_email_config( self.email_operator_in_GUI, self.full_email_in_GUI, self.email_password_in_GUI, self.email_url_in_GUI) print(f"did_it_create_config: {did_it_create_config}") except: print("Internal error with saving the email data") else: print("No Email") self.full_email_in_GUI = None # Update the GUI for the whole next section on the phone number: self.update_ui_after_getting_phone_number() # end confirmed section # Start: PROCESS SECTION # But if it's just processing, we can slow the timer during this: elif self.payment_status_in_GUI == "processing": self.timer.start(9000) else: print("checked, but it's not showing yet") # self.sms_status_in_GUI = "Waiting on Payment" # Paid or Not, update the label: try: # if restore flow.. if self.payment_status_in_GUI == "not_paid": self.update_status_label.setText( f"Payment Status: Not yet Paid. Please pay") # self.update_status_label.styleSheet(self.active_style_box) else: # normal flow: self.update_status_label.setText( f"Payment Status: {self.payment_status_in_GUI}") except RuntimeError: print( "You're trying to change the text of a label, but it's already been deleted.") try: # doing more restore flow here, this is because for the original flow the messages were server-side, for the restore flow I switched to client-side: if self.code_status_in_GUI == "restore_confirmed": self.update_sms_status_label.setText( f"SMS Status: Seller for your specific restore number is ready.") try: if self.the_returned_crypto_address_is: pass # Except should trigger if this guy is restoring, but lost his crypto address. except: # so stop checking if he paid: self.timer.stop() # and let him get a new address: self.get_new_address_button = QPushButton( "Get a New Address") self.get_new_address_button.setStyleSheet( "font-size: 15px; padding: 15px; background-color: green; color: yellow;") self.get_new_address_button.clicked.connect( self.yes_pay_to_restore_send) self.layout.addWidget(self.get_new_address_button) else: # normal flow (non restore) self.update_sms_status_label.setText( f"SMS Status: {self.code_status_in_GUI}") except RuntimeError: print( "You're trying to change the text of a label, but it's already been deleted.") except json.JSONDecodeError as e: print(f"JSON decoding error: {e}") # Stop checking if it can't format the JSON: self.timer.stop() return # donkey cunt except Exception as e: exc_type, exc_value, exc_tb = sys.exc_info() tb_summary = traceback.extract_tb(exc_tb) # Get the line number of the error line_number = tb_summary[-1].lineno error_msg = f"An error on line {line_number} produced: {e}" print(error_msg) self.show_an_error(f"Error occurred: {e}") # stop checking: self.timer.stop() def update_ui_after_getting_phone_number(self): # first clear layouts: try: for i in reversed(range(self.grid_layout.count())): item = self.grid_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() except: print("No Grid layout") for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Is there email data? if self.full_email_in_GUI is not None: # Label to display the email: self.full_email_label = QLabel( f"Step 1. Go sign-up for the service. Email is {self.full_email_in_GUI}") self.full_email_label.setStyleSheet(self.active_style_box) self.full_email_label.setWordWrap(True) self.layout.addWidget(self.full_email_label) # Label to copy-paste the email url: self.copy_email_url_button = QPushButton("Copy Email's URL") self.copy_email_url_button.setStyleSheet( "font-size: 15px; padding: 15px; background-color: green; color: yellow;") self.copy_email_url_button.clicked.connect( self.copy_email_url_function) self.layout.addWidget(self.copy_email_url_button) # Label to copy-paste the email: self.copy_full_email_button = QPushButton("Copy Email") self.copy_full_email_button.setStyleSheet( "font-size: 20px; padding: 15px; background-color: green; color: yellow;") self.copy_full_email_button.clicked.connect( self.copy_full_email_function) self.layout.addWidget(self.copy_full_email_button) # Label to copy-paste the email password: self.copy_email_password_button = QPushButton( "Copy Email's Password") self.copy_email_password_button.setStyleSheet( "font-size: 20px; padding: 15px; background-color: green; color: yellow;") self.copy_email_password_button.clicked.connect( self.copy_email_password_function) self.layout.addWidget(self.copy_email_password_button) # Label to display the phone number: self.after_confirm_sms_status_label = QLabel( f"{self.display_code_status_in_GUI}") self.after_confirm_sms_status_label.setStyleSheet( self.active_style_box) self.after_confirm_sms_status_label.setWordWrap(True) self.after_confirm_sms_status_label.setFixedWidth(800) self.after_confirm_sms_status_label.setMaximumHeight(250) self.layout.addWidget(self.after_confirm_sms_status_label) # Label to copy-paste the phone number: # flag variable for the apperance of the button in copy_phone_number_function in the next step, which this button triggers self.get_code_button_already_visible = False self.copy_phone_number_button = QPushButton("Copy Phone Number") self.copy_phone_number_button.setStyleSheet( "font-size: 30px; padding: 15px; background-color: green; color: yellow;") self.copy_phone_number_button.clicked.connect( self.copy_phone_number_function) self.layout.addWidget(self.copy_phone_number_button) # Label to display instructions: self.label_for_instructions = QLabel( f"After that service sends the SMS code to that number, THEN hit the button below.") self.label_for_instructions.setStyleSheet(self.lowkey_style_box) # self.label_for_instructions.setStyleSheet("font-size: 20px;") self.label_for_instructions.setWordWrap(True) self.label_for_instructions.setFixedWidth(800) self.label_for_instructions.setMaximumHeight(300) self.layout.addWidget(self.label_for_instructions) self.label_for_instructions.setVisible(False) # Start: Display the Code Section @asyncSlot() # Decorator to allow this method to be async async def with_slot_async_get_code_function(self): # Step 1: Run the async function asyncio.create_task(self.send_to_get_code_function()) async def send_to_get_code_function(self): # Send this data to the Flask server returned_data = await ready_for_code_by_sending_data_to_flask(self.peer_to_peer_billing_id) # DEBUG SECTION! uncomment to debug, # print (f"this is data: {returned_data}") # self.record_of_complete = True # Update the GUI with the response data self.update_ui_after_checking_for_code(returned_data) return returned_data def update_ui_after_checking_for_code(self, returned_data): try: if isinstance(returned_data, dict): # Directly use the dictionary returned_data_as_a_dictionary = returned_data else: # If it's a string, format it and load it as a dictionary valid_json_string = returned_data.replace("'", '"') returned_data_as_a_dictionary = json.loads(valid_json_string) # Get the data from that: self.code_status_in_GUI = returned_data_as_a_dictionary.get( 'code_status') self.code_in_GUI = returned_data_as_a_dictionary.get( 'assigned_code') if self.code_in_GUI is None: self.code_in_GUI = "Please wait" elif self.code_status_in_GUI == "timed_out_buyer": # stop timer to check for code and display the "code" which is just telling them they took too long: self.get_code_timer.stop() self.after_requesting_code_label.setText( f"Please close this window and try again. {self.code_in_GUI}") # else triggered means they got a code. They weren't timed out and it isn't still waiting else: if self.label_for_instructions.styleSheet() == self.active_style_box: pass else: self.after_requesting_code_label.setStyleSheet( self.active_style_box) # this was the original way of doing it, prior to the upgrade of universal styles in the __init__ # self.after_requesting_code_label.setStyleSheet("font-size: 50px;") # self.label_for_instructions.setStyleSheet("font-size: 50px;") # print it out for debug status: print(self.code_status_in_GUI) print(self.code_in_GUI) # display code in GUI: if self.code_in_GUI == "Please wait": self.after_requesting_code_label.setText( f"Contacting Seller to Fetch the Code. Make sure you told the website to send it.") self.after_requesting_code_label.setStyleSheet( self.active_style_box) else: self.after_requesting_code_label.setText( f"Code: {self.code_in_GUI}") self.copy_paste_code.setVisible(True) # stop timer to check: self.get_code_timer.stop() except: print("No code yet") def update_ui_after_to_get_code(self): # first clear layouts: for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # We're doing a 2 second timer to check if the code is ready from API: self.timer = QTimer() self.timer.timeout.connect(self.with_slot_async_get_code_function) self.timer.start(2000) # Label to display the code: self.code_in_GUI = "Getting Seller's Reply..." self.after_requesting_code_label = QLabel(f"{self.code_in_GUI}") self.after_requesting_code_label.setStyleSheet( "background-color: white; color: black; font-size: 30px;") self.after_requesting_code_label.setWordWrap(True) self.after_requesting_code_label.setFixedWidth(800) self.after_requesting_code_label.setMaximumHeight(250) self.layout.addWidget(self.after_requesting_code_label) # button to copy-paste the code: self.copy_paste_code = QPushButton("Copy Code") self.copy_paste_code.setStyleSheet( "font-size: 30px; padding: 15px; background-color: green; color: yellow;") self.copy_paste_code.clicked.connect(self.copy_code_function) self.layout.addWidget(self.copy_paste_code) self.copy_paste_code.setVisible(False) # Label to display instructions: self.label_for_instructions = QLabel(f"Did this work?") self.label_for_instructions.setStyleSheet(self.lowkey_style_box) self.label_for_instructions.setWordWrap(True) self.label_for_instructions.setFixedWidth(800) self.label_for_instructions.setMaximumHeight(200) self.layout.addWidget(self.label_for_instructions) # End: Display the Code Section # Start: Display the Error Section: def update_ui_for_errors(self, returned_data_as_dict): returned_data = str(returned_data_as_dict) # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Clear all widgets from the horizontal layout for i in reversed(range(self.h_layout.count())): item = self.h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() for i in reversed(range(self.top_ribbon_h_layout.count())): item = self.top_ribbon_h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Display the header label for the Error page (with the label style declared in the init section) label_for_peer_to_peer_billing_id = QLabel( f"Error! Something went wrong") label_for_peer_to_peer_billing_id.setStyleSheet(self.label_style) label_for_peer_to_peer_billing_id.setSizePolicy( QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) self.layout.addWidget(label_for_peer_to_peer_billing_id) # This label gives the raw error message in a box: self.update_status_label = QLabel(returned_data) self.update_status_label.setStyleSheet( "background-color: white; color: black; font-size: 25px;") self.update_status_label.setWordWrap(True) self.update_status_label.setFixedWidth(700) self.update_status_label.setMaximumHeight(400) self.layout.addWidget(self.update_status_label) # Start: Display the Result Section: # We're displaying the crypto payment data and SMS status windows: def update_ui(self, returned_data): self.the_returned_crypto_address_is = returned_data.get('address') self.the_returned_crypto_amount_is = returned_data.get('amount_owed') # Clear the layout and display the new label with the data # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Clear all widgets from the horizontal layout for i in reversed(range(self.h_layout.count())): item = self.h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() for i in reversed(range(self.top_ribbon_h_layout.count())): item = self.top_ribbon_h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Display the ID: label_for_peer_to_peer_billing_id = QLabel( f"Your P2P ID: {self.peer_to_peer_billing_id}") label_for_peer_to_peer_billing_id.setStyleSheet(self.label_style) label_for_peer_to_peer_billing_id.setWordWrap(True) label_for_peer_to_peer_billing_id.setSizePolicy( # Expanding vertical size policy QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) self.layout.addWidget(label_for_peer_to_peer_billing_id) # Create a button to Copy Paste ID: self.copy_amount_button = QPushButton("Copy Your Billing ID") # Set background and text color self.button.clicked.connect(self.show_selected) self.copy_amount_button.setStyleSheet( "font-size: 20px; padding: 20px; background-color: green; color: yellow;") self.copy_amount_button.clicked.connect(self.copy_billing_id_function) self.layout.addWidget(self.copy_amount_button) # Create new labels with the response data crypto_long_address_as_string = str( self.the_returned_crypto_address_is) length_of_long_address = len(crypto_long_address_as_string) # Litecoin is 43, so let's see if it's long enough for one line, if so break it up: if length_of_long_address >= 44: break_into_three = length_of_long_address / 2 import math round_up_breaking_into_three_groups = math.ceil(break_into_three) first_group = crypto_long_address_as_string[0: round_up_breaking_into_three_groups] second_group = crypto_long_address_as_string[ round_up_breaking_into_three_groups:length_of_long_address] else: # probably litecoin, so one line: first_group = crypto_long_address_as_string second_group = "" # Create a QGridLayout self.grid_layout = QGridLayout() # This next section creates four widgets to add to the grid layout for the crypto data and copy buttons, # Address Label: self.address_label = QLabel(f"{first_group}\n{second_group}") self.address_label.setWordWrap(True) self.address_label.setFixedWidth(600) self.address_label.setMaximumHeight(100) self.address_label.setStyleSheet( "background-color: grey; color: black; font-size: 15px;") self.layout.addWidget(self.address_label) # Create a button to Copy Paste Address self.copy_crypto_address_button = QPushButton("Copy Address") self.copy_crypto_address_button.setStyleSheet( # Set background and text color "font-size: 30px; padding: 15px; background-color: green; color: yellow;") self.copy_crypto_address_button.clicked.connect( self.copy_crypto_address_function) self.layout.addWidget(self.copy_crypto_address_button) # Amount label: self.amount_label = QLabel(str(self.the_returned_crypto_amount_is)) self.amount_label.setStyleSheet( "background-color: grey; color: black; font-size: 20px;") self.amount_label.setFixedWidth(600) self.amount_label.setMaximumHeight(100) self.layout.addWidget(self.amount_label) # Create a button to Copy Paste Amount self.copy_amount_button = QPushButton("Copy Amount") # Set background and text color self.button.clicked.connect(self.show_selected) self.copy_amount_button.setStyleSheet( "font-size: 30px; padding: 30px; background-color: green; color: yellow;") self.copy_amount_button.clicked.connect(self.copy_amount_function) self.layout.addWidget(self.copy_amount_button) # Add widgets to the grid layout self.grid_layout.addWidget(self.address_label, 0, 0) # Row 0, Column 0 self.grid_layout.addWidget( self.copy_crypto_address_button, 0, 1) # Row 0, Column 1 self.grid_layout.addWidget(self.amount_label, 1, 0) # Row 1, Column 0 self.grid_layout.addWidget( self.copy_amount_button, 1, 1) # Row 1, Column 1 # Add the grid layout to the main layout self.layout.addLayout(self.grid_layout) # Payment check label: self.update_status_label = QLabel("Payment Checker: Connecting..") self.update_status_label.setStyleSheet(self.active_style_box) self.update_status_label.setWordWrap(True) self.update_status_label.setFixedWidth(800) self.update_status_label.setMaximumHeight(120) self.layout.addWidget(self.update_status_label) # SMS status label: self.update_sms_status_label = QLabel("SMS Status: Connecting..") self.update_sms_status_label.setStyleSheet(self.lowkey_style_box) self.update_sms_status_label.setWordWrap(True) self.update_sms_status_label.setFixedWidth(800) self.update_sms_status_label.setMaximumHeight(250) self.layout.addWidget(self.update_sms_status_label) # Create a timer self.timer = QTimer() self.timer.timeout.connect(self.check_if_paid_on_timer) self.timer.start(5000) # Start the timer with a 5-second interval def restore_previous_session_function(self, buyer_billing_id_entered): # Clear the layout and display the new label with the data try: # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Clear all widgets from the horizontal layout for i in reversed(range(self.h_layout.count())): item = self.h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() for i in reversed(range(self.top_ribbon_h_layout.count())): item = self.top_ribbon_h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() except: pass finally: # Payment check label: self.update_status_label = QLabel("Payment Checker: Connecting..") self.update_status_label.setStyleSheet( "background-color: white; color: black; font-size: 30px;") self.update_status_label.setFixedWidth(700) self.update_status_label.setMaximumHeight(100) self.layout.addWidget(self.update_status_label) # Amount label: self.update_sms_status_label = QLabel("SMS Status: Connecting..") self.update_sms_status_label.setStyleSheet( "background-color: white; color: black; font-size: 30px;") self.update_sms_status_label.setWordWrap(True) self.update_sms_status_label.setFixedWidth(800) self.update_sms_status_label.setMaximumHeight(250) self.layout.addWidget(self.update_sms_status_label) # Create a timer self.timer = QTimer() self.timer.timeout.connect(self.check_if_paid_on_timer) self.timer.start(5000) # Start the timer with a 5-second interval def copy_crypto_address_function(self): clipboard = QApplication.clipboard() clipboard.setText(self.the_returned_crypto_address_is) def copy_amount_function(self): clipboard = QApplication.clipboard() clipboard.setText(self.the_returned_crypto_amount_is) def copy_billing_id_function(self): clipboard = QApplication.clipboard() clipboard.setText(self.peer_to_peer_billing_id) def copy_code_function(self): clipboard = QApplication.clipboard() clipboard.setText(self.code_in_GUI) if self.code_in_GUI is not None: self.after_requesting_code_label.setStyleSheet( self.lowkey_style_box) self.label_for_instructions.setStyleSheet(self.active_style_box) # Yes Button: self.yes_button = QPushButton("Yes") self.yes_button.setStyleSheet( "font-size: 30px; padding: 15px; background-color: green; color: yellow;") self.yes_button.clicked.connect(self.on_yes_button_click) self.layout.addWidget(self.yes_button) # No Button: self.no_button = QPushButton("No, try a NEW seller & number") self.no_button.setStyleSheet( "font-size: 15px; padding: 15px; background-color: red; color: black;") self.no_button.clicked.connect(self.on_dispute_button_click) self.layout.addWidget(self.no_button) def hide_crypto_address_grid_layout(self): for i in range(self.grid_layout.count()): widget = self.grid_layout.itemAt(i).widget() if widget is not None: widget.hide() def copy_full_email_function(self): clipboard = QApplication.clipboard() clipboard.setText(self.full_email_in_GUI) def copy_email_password_function(self): clipboard = QApplication.clipboard() clipboard.setText(self.email_password_in_GUI) try: self.full_email_label.setStyleSheet(self.lowkey_style_box) except: pass def copy_email_url_function(self): clipboard = QApplication.clipboard() clipboard.setText(self.email_url_in_GUI) def copy_phone_number_function(self): clipboard = QApplication.clipboard() clipboard.setText(self.assigned_phone_number_in_GUI) self.after_confirm_sms_status_label.setStyleSheet( self.lowkey_style_box) self.label_for_instructions.setStyleSheet(self.active_style_box) self.label_for_instructions.setVisible(True) try: self.full_email_label.setVisible(False) except: pass # only add this button if it isn't already visible or added: if self.get_code_button_already_visible == False: self.ready_to_get_code_button = QPushButton("Step 3. Get the Code") self.ready_to_get_code_button.setStyleSheet( "font-size: 30px; padding: 15px; background-color: green; color: yellow;") self.ready_to_get_code_button.clicked.connect( self.update_ui_after_to_get_code) self.layout.addWidget(self.ready_to_get_code_button) self.get_code_button_already_visible = True # End: Display the Result Section # Start: Dispute display UI # Start: Dispute ACCEPTED Display the Result Section: def update_ui_for_dispute(self): # Clear the layout and display the new label with the data # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Clear all widgets from the horizontal layout for i in reversed(range(self.h_layout.count())): item = self.h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Display the ID: label_for_peer_to_peer_billing_id = QLabel( f"Dispute! Matching you with a New Number. {self.peer_to_peer_billing_id}") label_for_peer_to_peer_billing_id.setStyleSheet(self.label_style) label_for_peer_to_peer_billing_id.setWordWrap(True) label_for_peer_to_peer_billing_id.setSizePolicy( # Expanding vertical size policy QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) self.layout.addWidget(label_for_peer_to_peer_billing_id) # Create a button to Copy Paste ID: self.copy_amount_button = QPushButton("Copy Your Billing ID") # Set background and text color self.button.clicked.connect(self.show_selected) self.copy_amount_button.setStyleSheet( "font-size: 20px; padding: 20px; background-color: green; color: yellow;") self.copy_amount_button.clicked.connect(self.copy_billing_id_function) self.layout.addWidget(self.copy_amount_button) # Payment check label: self.update_status_label = QLabel("Payment Paid") self.update_status_label.setStyleSheet( "background-color: white; color: black; font-size: 30px;") self.update_status_label.setFixedWidth(700) self.update_status_label.setMaximumHeight(100) self.layout.addWidget(self.update_status_label) # Status label: self.update_sms_status_label = QLabel("SMS Status: Connecting..") self.update_sms_status_label.setStyleSheet( "background-color: white; color: black; font-size: 30px;") self.update_sms_status_label.setWordWrap(True) self.update_sms_status_label.setFixedWidth(800) self.update_sms_status_label.setMaximumHeight(250) self.layout.addWidget(self.update_sms_status_label) # Create a timer self.timer = QTimer() self.timer.timeout.connect(self.check_if_paid_on_timer) self.timer.start(5000) # Start the timer with a 5-second interval # End dispute Accepted UI GUI # Start: Dispute REJECED UI GUI def update_ui_for_dispute_rejected(self): # Clear the layout and display the new label with the data # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Clear all widgets from the horizontal layout for i in reversed(range(self.h_layout.count())): item = self.h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Display the ID: label_for_peer_to_peer_billing_id = QLabel( f"Any billing issue? Your P2P ID is: {self.peer_to_peer_billing_id}") label_for_peer_to_peer_billing_id.setWordWrap(True) label_for_peer_to_peer_billing_id.setStyleSheet(self.label_style) label_for_peer_to_peer_billing_id.setSizePolicy( # Expanding vertical size policy QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Fixed) self.layout.addWidget(label_for_peer_to_peer_billing_id) # Create a button to Copy Paste ID: self.copy_amount_button = QPushButton("Copy Your Billing ID") # Set background and text color self.button.clicked.connect(self.show_selected) self.copy_amount_button.setStyleSheet( "font-size: 20px; padding: 20px; background-color: green; color: yellow;") self.copy_amount_button.clicked.connect(self.copy_billing_id_function) self.layout.addWidget(self.copy_amount_button) # Status label: self.update_sms_status_label = QLabel( "I am so sorry to inform you that the automated system rejected your dispute. It's possible this is a technical error, please contact customer support with your Billing ID") self.update_sms_status_label.setStyleSheet( "background-color: white; color: black; font-size: 20px;") self.update_sms_status_label.setWordWrap(True) self.update_sms_status_label.setFixedWidth(800) self.update_sms_status_label.setMaximumHeight(250) self.layout.addWidget(self.update_sms_status_label) # End: Dispute REJECED UI GUI # Start: Zero Dispute. Finalize & End Peacefully. No Dispute. UI GUI def update_ui_for_peaceful_finalized_acceptance(self): # Clear the layout and display the new label with the data # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Clear all widgets from the horizontal layout for i in reversed(range(self.h_layout.count())): item = self.h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Status label: self.update_sms_status_label = QLabel( "Thank you! This is complete. You can close the window.") self.update_sms_status_label.setStyleSheet(self.active_style_box) self.update_sms_status_label.setWordWrap(True) self.update_sms_status_label.setFixedWidth(800) self.update_sms_status_label.setMaximumHeight(250) self.layout.addWidget(self.update_sms_status_label) # End: Zero Dispute. Finalize & End Peacefully. No Dispute. UI GUI # Start: Send REFUND CRYPTO address & Display result: @asyncSlot() async def gui_calls_send_crypto_address_for_refund(self): self.timer.stop() # Step 1: Run the async function asyncio.create_task( self.async_gui_calls_send_crypto_address_for_refund()) async def async_gui_calls_send_crypto_address_for_refund(self): # Send this data to the Flask server returned_data = await send_crypto_address_for_refund(self.peer_to_peer_billing_id, self.refund_crypto_text_box.text()) # DEBUG SECTION! uncomment to debug, print(f"this is data: {returned_data}") # Update the GUI with the response data self.update_ui_after_sending_crypto_address(returned_data) return returned_data # Start: Display the Error Section: def update_ui_after_sending_crypto_address(self, returned_data): # Clear all widgets from the vertical layout for i in reversed(range(self.layout.count())): item = self.layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # Clear all widgets from the horizontal layout for i in reversed(range(self.h_layout.count())): item = self.h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() for i in reversed(range(self.top_ribbon_h_layout.count())): item = self.top_ribbon_h_layout.itemAt(i) if item is not None: widget = item.widget() if widget is not None: widget.deleteLater() # This label gives the raw error message in a box: self.update_status_label = QLabel(str(returned_data)) self.update_status_label.setStyleSheet( "background-color: white; color: black; font-size: 25px;") self.update_status_label.setWordWrap(True) self.update_status_label.setFixedWidth(700) self.update_status_label.setMaximumHeight(400) self.layout.addWidget(self.update_status_label) # End: Send REFUND CRYPTO address & Display result def bring_trade_back_from_dead(self): self.clear_the_main_page_ui() message_for_user = f"The trade for {self.peer_to_peer_billing_id} is already finalized. Do you wish to pay 5 euros to restore it and get another code from the same service with the same number?" self.label_for_api_fetch = QLabel(f"{message_for_user}") self.label_for_api_fetch.setStyleSheet(self.active_style_box) self.label_for_api_fetch.setWordWrap(True) self.layout.addWidget(self.label_for_api_fetch) # yes pay: self.yes_restore_dead_session_button = QPushButton( "Yes, Pay 5 Euros to Restore") self.yes_restore_dead_session_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: green; color: yellow;") self.yes_restore_dead_session_button.clicked.connect( self.yes_pay_to_restore_send) self.layout.addWidget(self.yes_restore_dead_session_button) # return to main page button: self.return_to_the_main_page_button = QPushButton( "Nevermind. Return to Main Page") self.return_to_the_main_page_button.setStyleSheet( "font-size: 30px; padding: 10px; background-color: red; color: black;") self.return_to_the_main_page_button.clicked.connect( self.update_ui_to_return_to_the_main_page) self.layout.addWidget(self.return_to_the_main_page_button) @asyncSlot() # Decorator to allow this method to be async async def yes_pay_to_restore_send(self): # Step 1: Run the async function asyncio.create_task(self.restore_dead_trade_by_sending_to_flask()) # Step 2: Show dialog and hide button after a short delay QTimer.singleShot(0, self.hide_restore_buttons) # This button hides elements while being sent to the server, but these elements may already be hidden. def hide_restore_buttons(self): self.show_dialog() # this is the regular restore flow: try: self.yes_restore_dead_session_button.setVisible(False) self.return_to_the_main_page_button.setVisible(False) self.label_for_api_fetch.setText( "Please hold while we contact that SMS seller.") except: # objects have been deleted already: pass # this is the "lost address restore flow" try: self.get_new_address_button.setVisible(False) except: # objects have been deleted already: pass # Start: RESTORE Send data to Flask Section # THIS IS STEP 1 OF RESTORE async def restore_dead_trade_by_sending_to_flask(self): # Send this data to the Flask server: returned_data = await restore_an_already_finalized_number(self.peer_to_peer_billing_id, "restore") print(f"this is data: {returned_data}") are_we_checking_again = True # We're checking if it returned an error or registered the user: if 'code_status' in returned_data: code_status = returned_data.get('code_status') if code_status == "restore_requested" or code_status == "restore_initiated": display_message_for_user = "Contacting the Seller to request a restoration, please remain patient. If the seller is busy with another buyer or asleep in their timezone, it may temporarily reject you. If so, then try again." elif code_status == "restore_but_seller_busy": display_message_for_user = "The Seller is busy. It might be sleeping time in his/her timezone, vacation time, or they are on a different gig. Please try back later." are_we_checking_again = False elif code_status == "restore_confirmed": display_message_for_user = "Seller confirmed, one moment please, as you must pay" else: display_message_for_user = "One moment please" try: self.label_for_api_fetch.setText(f"{display_message_for_user}") except: # incase it does not exist: self.label_for_api_fetch = QLabel( f"{display_message_for_user}") self.label_for_api_fetch.setStyleSheet(self.active_style_box) self.label_for_api_fetch.setWordWrap(True) self.layout.addWidget(self.label_for_api_fetch) if are_we_checking_again == True: # We're doing a 5 second timer to check if the seller is ready: self.timer = QTimer() self.timer.timeout.connect( self.step_two_of_restore_get_confirm) self.timer.start(5000) # Display Connection Errors: elif 'error' in returned_data: connection_error_is = returned_data['error'] self.update_ui_for_errors(connection_error_is) elif 'message' in returned_data: # Display Errors: message = returned_data['message'] self.update_ui_for_errors(message) else: self.update_ui_for_errors(returned_data) # Here we're on a repeat timer, checking the /restore endpoint if the guy is confirmed ready. @asyncSlot() async def step_two_of_restore_get_confirm(self): # Step 1: Run the async function asyncio.create_task( self.on_timer_step_two_of_restore_dead_trade_by_sending_to_flask()) # THIS IS STEP 2 OF RESTORE async def on_timer_step_two_of_restore_dead_trade_by_sending_to_flask(self): # Send this data to the Flask server: returned_data = await restore_an_already_finalized_number(self.peer_to_peer_billing_id, "is_restore_seller_ready") print(returned_data) if 'address' in returned_data: print("We got an address") # now this "restore flow" rejoins the main flow for new orders: self.update_ui(returned_data) elif 'code_status' in returned_data: code_status = returned_data.get('code_status') if code_status == "restore_confirmed": self.timer.stop() # We're now getting a crypto address: self.get_restore_crypto_address() elif code_status == "restore_but_seller_busy": self.timer.stop() display_message_for_user = "The Seller is busy. It might be sleeping time in his/her timezone, vacation time, or they are on a different gig. Please try back later." self.label_for_api_fetch.setText(f"{display_message_for_user}") # return to main page button: self.return_to_the_main_page_button.setText( "Fine. Return to Main Page") self.return_to_the_main_page_button.setVisible(True) # Display Connection Errors: elif 'error' in returned_data: connection_error_is = returned_data['error'] self.update_ui_for_errors(connection_error_is) elif 'message' in returned_data: message = returned_data['message'] # Display Errors: self.update_ui_for_errors(message) else: returned_data_as_string = str(returned_data) self.update_ui_for_errors(returned_data_as_string) return returned_data # End: RESTORE Send data to Flask Section @asyncSlot() # Decorator to allow this method to be async async def get_restore_crypto_address(self): # Step 1: Run the async function asyncio.create_task(self.get_crypto_addy_for_restore()) async def get_crypto_addy_for_restore(self): # Send this data to the Flask server returned_data = await restore_an_already_finalized_number(self.peer_to_peer_billing_id, "is_restore_seller_ready") print(f"returned_data: {returned_data}") if 'address' in returned_data: print("We got an address") # now this "restore flow" rejoins the main flow for new orders: self.update_ui(self, returned_data) else: print("Error, we did not get a crypto address") # this may not be needed return returned_data ''' def update_ui_to_pay_for_restore(self): payment_address = returned_data_as_a_dictionary.get('address') display_address = f"We got a payment_address: {payment_address}" print(f"{display_address}") self.label_for_api_fetch.setText(f"{display_address}") # We're doing a 5 second timer to check if the code is ready from API: self.timer = QTimer() self.timer.timeout.connect(self.check_if_seller_paid_for_a_restore) self.timer.start(5000) @asyncSlot() # Decorator to allow this method to be async async def check_if_seller_paid_for_a_restore(self): # Step 1: Run the async function asyncio.create_task(self.check_if_seller_paid_for_a_restore_function()) async def check_if_seller_paid_for_a_restore_function(self): # Send this data to the Flask server returned_data = await restore_an_already_finalized_number(self.peer_to_peer_billing_id, "is_restore_seller_ready") # Update the GUI with the response data print(f"returned_data: {returned_data}") return returned_data ''' if __name__ == "__main__": app = QApplication(sys.argv) # async loop for app so that it can fetch the API while it displays the intro text. Then go right into doing network calls with GUI changes, loop = QEventLoop(app) asyncio.set_event_loop(loop) my_app = BuyerPeerPeerGUI() my_app.show() with loop: sys.exit(loop.run_forever())