diff --git a/SMS-Exchange-Linux-GUI b/SMS-Exchange-Linux-GUI deleted file mode 160000 index d114604..0000000 --- a/SMS-Exchange-Linux-GUI +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d1146046c0a222d1c278cda5c8852980d79ddd23 diff --git a/SMS-Exchange-Linux-GUI/GUI.py b/SMS-Exchange-Linux-GUI/GUI.py new file mode 100755 index 0000000..914f935 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/GUI.py @@ -0,0 +1,2468 @@ +# 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 +import interact_with_rest_of_app.which_profile_is_being_used as which_profile_module + +# 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()) diff --git a/SMS-Exchange-Linux-GUI/README.md b/SMS-Exchange-Linux-GUI/README.md new file mode 100755 index 0000000..890cf24 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/README.md @@ -0,0 +1,21 @@ +# Step 1. clone it. +```bash +git clone https://git.simplifiedprivacy.com/Support/SMS-Exchange-Linux-GUI.git +``` +
+ +# Step 2. Setup venv +```bash +python3 -m venv venv +source venv/bin/activate +``` + +# Step 3. Install Prereqs +```bash +pip install -r requirements.txt +``` + +# Step 4. Run it +```bash +python3 GUI.py +``` diff --git a/SMS-Exchange-Linux-GUI/SCOPE.md b/SMS-Exchange-Linux-GUI/SCOPE.md new file mode 100755 index 0000000..9547849 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/SCOPE.md @@ -0,0 +1,17 @@ +# Scope + +## Add with Button +To the Left of Enable + +# Process Proxy: +Inside 'interact_with_rest_of_app' folder +Placing that proxy.json in the correct format in the correct profile folder. + +## Which Profile Used: +Inside 'interact_with_rest_of_app' folder +Using Core to Return which Profile the User has Up + +## Enable button Enables Proxy.json +Possible stack with Tor, or pre-existing Wireguard + + diff --git a/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_and_save_json_from_our_api.cpython-311.pyc b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_and_save_json_from_our_api.cpython-311.pyc new file mode 100755 index 0000000..08a6899 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_and_save_json_from_our_api.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_and_save_json_from_our_api.cpython-312.pyc b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_and_save_json_from_our_api.cpython-312.pyc new file mode 100755 index 0000000..ca6b36b Binary files /dev/null and b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_and_save_json_from_our_api.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_initial_email_data.cpython-311.pyc b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_initial_email_data.cpython-311.pyc new file mode 100755 index 0000000..9a7d67c Binary files /dev/null and b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_initial_email_data.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_initial_email_data.cpython-312.pyc b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_initial_email_data.cpython-312.pyc new file mode 100755 index 0000000..e8c1022 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/get_initial_email_data.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/send_data_to_flask.cpython-311.pyc b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/send_data_to_flask.cpython-311.pyc new file mode 100755 index 0000000..84189e0 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/send_data_to_flask.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/send_data_to_flask.cpython-312.pyc b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/send_data_to_flask.cpython-312.pyc new file mode 100755 index 0000000..2f2efdb Binary files /dev/null and b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/send_data_to_flask.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/should_api_be_triggered.cpython-311.pyc b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/should_api_be_triggered.cpython-311.pyc new file mode 100755 index 0000000..0ead439 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/should_api_be_triggered.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/should_api_be_triggered.cpython-312.pyc b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/should_api_be_triggered.cpython-312.pyc new file mode 100755 index 0000000..076d031 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/api_interaction/__pycache__/should_api_be_triggered.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/api_interaction/get_and_save_json_from_our_api.py b/SMS-Exchange-Linux-GUI/api_interaction/get_and_save_json_from_our_api.py new file mode 100755 index 0000000..1ffbb5b --- /dev/null +++ b/SMS-Exchange-Linux-GUI/api_interaction/get_and_save_json_from_our_api.py @@ -0,0 +1,36 @@ +import requests +from requests.exceptions import RequestException, Timeout, ConnectionError, HTTPError + +async def get_and_save_json_from_our_api(file_path_saved): + url = 'https://onboard.simplifiedprivacy.net/initial_data' + + try: + # Note: this is a GET request, because it has no customer data: + response = requests.get(url, timeout=5) # Adding a timeout for better control + + # Check if the request was successful + if response.status_code == 200 or response.status_code == 201: + # Save the content to a file + with open(file_path_saved, 'w', encoding='utf-8') as file: + file.write(response.text) + print("Saved JSON from API") + return "worked" + else: + print(f"Failed to retrieve the page: {response.status_code}") + return f"Server Connected But Replied with Error Status Code: {response.status_code}" + + except Timeout: + print("The request timed out. Please try again later.") + return "The request timed out. Please try again later." + + except ConnectionError: + print("Failed to connect to the server. Please check your internet connection.") + return "Failed to connect to the server. Please check your internet connection." + + except HTTPError as http_err: + print(f"HTTP error occurred: {http_err}") + return f"HTTP error occurred: {http_err}" + + except RequestException as req_err: + print(f"An error occurred while making the request: {req_err}") + return f"An error occurred while making the request: {req_err}" diff --git a/SMS-Exchange-Linux-GUI/api_interaction/get_initial_email_data.py b/SMS-Exchange-Linux-GUI/api_interaction/get_initial_email_data.py new file mode 100755 index 0000000..e5b9440 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/api_interaction/get_initial_email_data.py @@ -0,0 +1,38 @@ +import requests +from requests.exceptions import RequestException, Timeout, ConnectionError, HTTPError + +#from local_data_operations.search_or_setup_json import check_and_create_folder_and_json + +async def get_initial_email_data(file_path_saved): + url = 'https://onboard.simplifiedprivacy.net/initial_email_data' + + try: + # Send a GET request to the URL + response = requests.get(url, timeout=5) # Adding a timeout for better control + + # Check if the request was successful + if response.status_code == 200 or response.status_code == 201: + # Save the content to a file + with open(file_path_saved, 'w', encoding='utf-8') as file: + file.write(response.text) + print("Saved JSON from API") + return "worked" + else: + print(f"Failed to retrieve the page: {response.status_code}") + return f"Server Connected But Replied with Error Status Code: {response.status_code}" + + except Timeout: + print("The request timed out. Please try again later.") + return "The request timed out. Please try again later." + + except ConnectionError: + print("Failed to connect to the server. Please check your internet connection.") + return "Failed to connect to the server. Please check your internet connection." + + except HTTPError as http_err: + print(f"HTTP error occurred: {http_err}") + return f"HTTP error occurred: {http_err}" + + except RequestException as req_err: + print(f"An error occurred while making the request: {req_err}") + return f"An error occurred while making the request: {req_err}" diff --git a/SMS-Exchange-Linux-GUI/api_interaction/send_data_to_flask.py b/SMS-Exchange-Linux-GUI/api_interaction/send_data_to_flask.py new file mode 100755 index 0000000..31516f2 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/api_interaction/send_data_to_flask.py @@ -0,0 +1,113 @@ +import aiohttp +from aiohttp import ClientConnectorError, ClientResponseError +import asyncio +import json +import sys + +async def send_data_to_flask(chosen_payment_method, chosen_location, chosen_service_as_string, chosen_email_operator, chosen_proxy_status, peer_to_peer_billing_id): + async with aiohttp.ClientSession() as session: + # filter out if no email: + if chosen_email_operator == "email disabled" or chosen_email_operator == "profile already has an email": + chosen_email_operator = "disabled" + # send to API regardless: + try: + async with session.post('https://onboard.simplifiedprivacy.net/api', json={ + 'peer_to_peer_billing_id': peer_to_peer_billing_id, + 'chosen_payment_method': chosen_payment_method, + 'chosen_service': chosen_service_as_string, + 'chosen_email': chosen_email_operator, + 'chosen_proxy_status': chosen_proxy_status, + 'chosen_location': chosen_location + }) as response: + content_type = response.headers.get('Content-Type', '') + + if 'application/json' in content_type: + return await response.json() + else: + # If content type is HTML, return the text content + html_content = await response.text() + return {"error": f"It returned this as HTML: {content_type}, Content: {html_content}"} + except ClientConnectorError as e: + print(f"Connection error: {e}") # DNS failure, server is down, etc. + return {"error": "Connection error: Unable to reach the server"} + except ClientResponseError as e: + print(f"Response error: {e}") # e.g., 4xx or 5xx responses + return {"error": f"Bad response: {e.status} - {e.message}"} + +async def dispute_send_data_to_flask(how_did_it_go, peer_to_peer_billing_id): + async with aiohttp.ClientSession() as session: + try: + async with session.post('https://onboard.simplifiedprivacy.net/dispute', json={ + 'peer_to_peer_billing_id': peer_to_peer_billing_id, + 'dispute_status': f"{how_did_it_go}" + }) as response: + return await response.json() # Assuming the server returns JSON + + except ClientConnectorError as e: + print(f"Connection error: {e}") # DNS failure, server is down, etc. + return {"error": "Connection error: Unable to reach the server"} + except ClientResponseError as e: + print(f"Response error: {e}") # e.g., 4xx or 5xx responses + return {"error": f"Bad response: {e.status} - {e.message}"} + +async def check_if_paid_by_sending_data_to_flask(peer_to_peer_billing_id): + async with aiohttp.ClientSession() as session: + try: + async with session.post('https://onboard.simplifiedprivacy.net/checkpaid', json={ + 'peer_to_peer_billing_id':peer_to_peer_billing_id + }) as response: + return await response.json() # Assuming the server returns JSON + + except ClientConnectorError as e: + print(f"Connection error: {e}") # DNS failure, server is down, etc. + return {"error": "Connection error: Unable to reach the server"} + except ClientResponseError as e: + print(f"Response error: {e}") # e.g., 4xx or 5xx responses + return {"error": f"Bad response: {e.status} - {e.message}"} + +async def ready_for_code_by_sending_data_to_flask(peer_to_peer_billing_id): + async with aiohttp.ClientSession() as session: + try: + async with session.post('https://onboard.simplifiedprivacy.net/code', json={ + 'peer_to_peer_billing_id': peer_to_peer_billing_id + }) as response: + return await response.json() # Assuming the server returns JSON + + except ClientConnectorError as e: + print(f"Connection error: {e}") # DNS failure, server is down, etc. + return {"error": "Connection error: Unable to reach the server"} + except ClientResponseError as e: + print(f"Response error: {e}") # e.g., 4xx or 5xx responses + return {"error": f"Bad response: {e.status} - {e.message}"} + +async def send_crypto_address_for_refund(peer_to_peer_billing_id, refund_crypto_address): + async with aiohttp.ClientSession() as session: + try: + async with session.post('https://onboard.simplifiedprivacy.net/refund_address', json={ + 'peer_to_peer_billing_id': peer_to_peer_billing_id, + 'refund_crypto_address': refund_crypto_address + }) as response: + return await response.json() # Assuming the server returns JSON + + except ClientConnectorError as e: + print(f"Connection error: {e}") # DNS failure, server is down, etc. + return {"error": "Connection error: Unable to reach the server"} + except ClientResponseError as e: + print(f"Response error: {e}") # e.g., 4xx or 5xx responses + return {"error": f"Bad response: {e.status} - {e.message}"} + + +async def restore_an_already_finalized_number(peer_to_peer_billing_id, which_stage): + async with aiohttp.ClientSession() as session: + try: + async with session.post(f'https://onboard.simplifiedprivacy.net/{which_stage}', json={ + 'peer_to_peer_billing_id': peer_to_peer_billing_id + }) as response: + return await response.json() # Assuming the server returns JSON + + except ClientConnectorError as e: + print(f"Connection error: {e}") # DNS failure, server is down, etc. + return {"error": "Connection error: Unable to reach the server"} + except ClientResponseError as e: + print(f"Response error: {e}") # e.g., 4xx or 5xx responses + return {"error": f"Bad response: {e.status} - {e.message}"} diff --git a/SMS-Exchange-Linux-GUI/api_interaction/should_api_be_triggered.py b/SMS-Exchange-Linux-GUI/api_interaction/should_api_be_triggered.py new file mode 100755 index 0000000..c330860 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/api_interaction/should_api_be_triggered.py @@ -0,0 +1,14 @@ +import os +import time + +def should_api_be_triggered(file_path, period_of_time): + try: + # Get the last modification time of the file + modification_time = os.path.getmtime(file_path) + # Get the current time + current_time = time.time() + # Check if the file has been modified in the last hour (3600 seconds) + return (current_time - modification_time) <= period_of_time + except FileNotFoundError: + print("File not found.") + return False diff --git a/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/process_the_proxy.cpython-311.pyc b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/process_the_proxy.cpython-311.pyc new file mode 100755 index 0000000..667c57f Binary files /dev/null and b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/process_the_proxy.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/process_the_proxy.cpython-312.pyc b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/process_the_proxy.cpython-312.pyc new file mode 100644 index 0000000..274c838 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/process_the_proxy.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/which_profile_is_being_used.cpython-311.pyc b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/which_profile_is_being_used.cpython-311.pyc new file mode 100755 index 0000000..a0240b0 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/which_profile_is_being_used.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/which_profile_is_being_used.cpython-312.pyc b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/which_profile_is_being_used.cpython-312.pyc new file mode 100644 index 0000000..96b7510 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/__pycache__/which_profile_is_being_used.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/process_the_proxy.py b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/process_the_proxy.py new file mode 100755 index 0000000..f8b43ec --- /dev/null +++ b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/process_the_proxy.py @@ -0,0 +1,42 @@ +import os +import json +from core.Constants import Constants +import interact_with_rest_of_app.which_profile_is_being_used as which_profile_module + +current_profile_number = which_profile_module.which_profile_is_being_used() + +print('the current profile number is: ', current_profile_number) +print('the path is: ', Constants.HV_PROFILE_CONFIG_HOME) + + +def process_the_proxy(returned_data_as_a_dictionary): + proxy_ip_address_in_GUI = returned_data_as_a_dictionary.get( + 'proxy_ip_address') + proxy_password_in_GUI = returned_data_as_a_dictionary.get('proxy_password') + proxy_username_in_GUI = returned_data_as_a_dictionary.get('proxy_username') + try: + proxy_port_in_GUI = int( + returned_data_as_a_dictionary.get('proxy_port')) + except (ValueError, TypeError): + proxy_port_in_GUI = 1080 + + # Create a new dictionary: + new_formatted_dictonary_to_write_to_folder = { + "ip_address": proxy_ip_address_in_GUI, + "port_number": proxy_port_in_GUI, + "username": proxy_username_in_GUI, + "password": proxy_password_in_GUI, + "time_zone": "America/New_York" + } + + current_profile_number = which_profile_module.which_profile_is_being_used() + print('the current profile number is: ', current_profile_number) + custom_path = f'{Constants.HV_PROFILE_CONFIG_HOME}/{current_profile_number}/proxyTEST.json' + + # Ensure the directory exists + os.makedirs(os.path.dirname(custom_path), exist_ok=True) + + # Write the dictionary to a JSON file at the custom path + with open(custom_path, 'w') as json_file: + json.dump(new_formatted_dictonary_to_write_to_folder, + json_file, indent=4) diff --git a/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/which_profile_is_being_used.py b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/which_profile_is_being_used.py new file mode 100755 index 0000000..e2bedee --- /dev/null +++ b/SMS-Exchange-Linux-GUI/interact_with_rest_of_app/which_profile_is_being_used.py @@ -0,0 +1,25 @@ +import sys + +if len(sys.argv) > 1: + try: + print('the profile id is: ', sys.argv[1]) + assigned_profile_id = int(sys.argv[1]) + except ValueError: + print('Error: Profile ID must be an integer.') + sys.exit(1) +else: + print('Error: No profile ID provided.') + sys.exit(1) + + +def which_profile_is_being_used(): + if len(sys.argv) > 1: + try: + print('the profile id is: ', sys.argv[1]) + return int(sys.argv[1]) + except ValueError: + print('Error: Profile ID must be an integer.') + sys.exit(1) + else: + print('Error: No profile ID provided.') + sys.exit(1) diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/generate_random_number_string.cpython-311.pyc b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/generate_random_number_string.cpython-311.pyc new file mode 100755 index 0000000..f6968ba Binary files /dev/null and b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/generate_random_number_string.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/generate_random_number_string.cpython-312.pyc b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/generate_random_number_string.cpython-312.pyc new file mode 100755 index 0000000..6273058 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/generate_random_number_string.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/load_or_setup_config.cpython-311.pyc b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/load_or_setup_config.cpython-311.pyc new file mode 100755 index 0000000..7ae36b6 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/load_or_setup_config.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/load_or_setup_config.cpython-312.pyc b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/load_or_setup_config.cpython-312.pyc new file mode 100755 index 0000000..8de7f08 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/load_or_setup_config.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/local_logs.cpython-311.pyc b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/local_logs.cpython-311.pyc new file mode 100755 index 0000000..d87ff50 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/local_logs.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/local_logs.cpython-312.pyc b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/local_logs.cpython-312.pyc new file mode 100755 index 0000000..3f064b7 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/local_logs.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/search_or_setup_json.cpython-311.pyc b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/search_or_setup_json.cpython-311.pyc new file mode 100755 index 0000000..e4029fc Binary files /dev/null and b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/search_or_setup_json.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/search_or_setup_json.cpython-312.pyc b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/search_or_setup_json.cpython-312.pyc new file mode 100755 index 0000000..50d88cd Binary files /dev/null and b/SMS-Exchange-Linux-GUI/local_data_operations/__pycache__/search_or_setup_json.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/generate_random_number_string.py b/SMS-Exchange-Linux-GUI/local_data_operations/generate_random_number_string.py new file mode 100755 index 0000000..67ac5a3 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/local_data_operations/generate_random_number_string.py @@ -0,0 +1,16 @@ +import random + +def generate_random_number_string(length=10): + numbers = ''.join(random.choice('0123456789') for _ in range(length)) + # Insert dashes every 4 digits + formatted_string = '_'.join(numbers[i:i+4] for i in range(0, length, 4)) + final_seller_string = f"BUYER3_{formatted_string}" + return final_seller_string + +# Generate and print the random number string +#i = 0 +#while i < 5: +# print(generate_random_number_string()) +# i = i +1 + + diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/load_or_setup_config.py b/SMS-Exchange-Linux-GUI/local_data_operations/load_or_setup_config.py new file mode 100755 index 0000000..4873865 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/local_data_operations/load_or_setup_config.py @@ -0,0 +1,54 @@ +from local_data_operations.search_or_setup_json import get_the_path_of_the_folder_for, load_json_as_dict, replace_and_save_value_in_json +import json +import os + +def get_default_config(): + default_json_content = { + "error_log_file_path": "error_log_for_gui.txt", + "local_data": True, + "default_currency": False, + "turn_off_email_option": False, + "turn_off_proxy_option": False, + "cache_available": True, + } + return default_json_content + +def load_or_setup_local_api_data_folder(): + path_for_data = get_the_path_of_the_folder_for('.local/share', 'api_data') + + # Check if the folder exists + if not os.path.exists(path_for_data): + # Create the folder if it doesn't exist + os.makedirs(path_for_data) + print(f"Folder '{path_for_data}' created.") + else: + print(f"Folder '{path_for_data}' already exists.") + + return path_for_data + +def load_or_setup_config(json_file_name): + # setup configs file: + path_for_configs = get_the_path_of_the_folder_for('.config', 'sms') + + default_json_content = get_default_config() + + raw_data = load_json_as_dict(path_for_configs, json_file_name, default_json_content) + + # Return data in searchable format: + return raw_data + +# for the initial jsons from the API: +def setup_path_to_dump_initial_api_data(filename): + path_for_configs = get_the_path_of_the_folder_for('.config', 'api_data') + final_file_path = f"{path_for_configs}/{filename}" + return final_file_path + +def replace_config_value(json_file_name, key_value_to_find, value_to_replace): + # setup configs file: + path_for_configs = get_the_path_of_the_folder_for('.config', 'sms') + + default_json_content = get_default_config() + + new_data_set = replace_and_save_value_in_json(path_for_configs, json_file_name, default_json_content, key_value_to_find, value_to_replace) + + return new_data_set diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/local_logs.py b/SMS-Exchange-Linux-GUI/local_data_operations/local_logs.py new file mode 100755 index 0000000..6acdc34 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/local_data_operations/local_logs.py @@ -0,0 +1,290 @@ +from interact_with_rest_of_app.which_profile_is_being_used import which_profile_is_being_used + +# Loading and using JSONs +from local_data_operations.search_or_setup_json import get_the_path_of_the_folder_for, load_json_as_dict, search_key_in_json + +# Generic: +import json +import os + +def return_profile_filename_and_path(product): + # filename: + current_profile_number = which_profile_is_being_used() + if product == "email": + json_file_name = f"email_{current_profile_number}.json" + else: # this is sms: + json_file_name = f"{current_profile_number}.json" + + # folder path: + path_for_configs = get_the_path_of_the_folder_for('.config', 'sms') + + # full path: + # Check if the folder exists + if not os.path.exists(path_for_configs): + # Create the folder if it doesn't exist + os.makedirs(path_for_configs) + print(f"Folder '{path_for_configs}' created.") + else: + print(f"Folder '{path_for_configs}' already existed.") + + # Full path for the JSON file + full_json_file_path = os.path.join(path_for_configs, json_file_name) + + return json_file_name, path_for_configs, full_json_file_path + +def check_if_profile_exists_already(full_json_file_path): + # Check if the JSON file exists + if os.path.exists(full_json_file_path): + return True + else: + return False + +# When a profile is initiated, we want to know if it has a country and proxy already assigned. +def get_profile_location_and_proxy_status_as_tuple(): + # get the path and other variables that are not needed, but have to be unpacked: + json_file_name, path_for_configs, full_json_file_path = return_profile_filename_and_path("sms") + + # Check if the JSON file exists: + if os.path.exists(full_json_file_path): + # if so get the data: + with open(full_json_file_path, 'r') as json_file: + data = json.load(json_file) + + # get the location, or return "none" if it can't be found. + # return json_dict.get(key, default_value_if_not_found) + assigned_location = data.get("profile", {}).get("location", False) + assigned_proxy = data.get("profile", {}).get("assigned_proxy", False) + if assigned_location == "": + return False + else: + return (assigned_location, assigned_proxy) + else: + return False + +# If it does not exist already, then create it from scratch: # darth vadar +def setup_email_config(email_operator, full_email, email_password, email_url): + print("SETUP EMAIL CONFIG triggered") + email_json_file_name, path_for_configs, email_full_json_file_path = return_profile_filename_and_path("email") + # Add the email data: + new_data = { + "email_operator": email_operator, + "full_email": full_email, + "email_url": email_url, + "email_password": email_password + } + + try: + # Update the JSON Profile with new data: + with open(email_full_json_file_path, 'w') as json_file: + json.dump(new_data, json_file, indent=4) + return True # it worked: + except: + return "error" + +def wipe_entire_profile_data(): + json_file_name, path_for_configs, full_json_file_path = return_profile_filename_and_path("sms") + # Check if the file exists + if os.path.isfile(full_json_file_path): + # Delete the file + os.remove(full_json_file_path) + print(f"{full_json_file_path} has been deleted.") + return True + else: + print(f"{full_json_file_path} does not exist.") + return False + + +def wipe_email_data(): + email_json_file_name, path_for_configs, email_full_json_file_path = return_profile_filename_and_path("email") + # Check if the file exists + if os.path.isfile(email_full_json_file_path): + # Delete the file + os.remove(email_full_json_file_path) + print(f"{email_full_json_file_path} has been deleted.") + return True + else: + print(f"{email_full_json_file_path} does not exist.") + return False + +def get_email_data(): + print("READ EMAIL CONFIG triggered") + email_json_file_name, path_for_configs, email_full_json_file_path = return_profile_filename_and_path("email") + # Check if the JSON file exists + if os.path.exists(email_full_json_file_path): + try: + with open(email_full_json_file_path, 'r') as json_file: + email_data = json.load(json_file) + email_operator = email_data['email_operator'] + full_email = email_data['full_email'] + email_url = email_data['email_url'] + email_password = email_data['email_password'] + packed_email_data = (email_operator, full_email, email_url, email_password) + return packed_email_data + except: + return "error" + else: + return False + +def wipe_location_and_proxy(): + # then get the profile number & path: + json_file_name, path_for_configs, full_json_file_path = return_profile_filename_and_path("sms") + + if os.path.exists(full_json_file_path): + # if so get the data: + with open(full_json_file_path, 'r') as json_file: + data = json.load(json_file) + + # clear it: + data['profile']['location'] = "" + data['profile']['assigned_proxy'] = False + try: + # Update the JSON Profile with new data: + with open(full_json_file_path, 'w') as json_file: + json.dump(data, json_file, indent=4) + return True # it worked: + except: + return "error" + + # if it does NOT exist: + else: + return True + + +# If it does not exist already, then create it from scratch: +def create_or_add_to_profile_config(config_file_data, peer_to_peer_billing_id, chosen_location, proxy_or_not, chosen_service, email_operator): + # first check if the user disabled local data saving in the config file: + do_we_want_logs = search_key_in_json(config_file_data, "local_data", True) + if do_we_want_logs == False: + return False + else: + # then get the profile number & path: + json_file_name, path_for_configs, full_json_file_path = return_profile_filename_and_path("sms") + + if os.path.exists(full_json_file_path): + # if so get the data: + with open(full_json_file_path, 'r') as json_file: + data = json.load(json_file) + + # If it's a blank location, then update it with the new choices: + pre_existing_location = data['profile']['location'] + if pre_existing_location == "": + data['profile']['location'] = chosen_location + data['profile']['assigned_proxy'] = proxy_or_not + else: + pass + + # Add the new order: + data["orders"][peer_to_peer_billing_id] = { + "chosen_service": chosen_service, + "wants_sms": True, + "completed": False + } + + try: + # Update the JSON Profile with new data: + with open(full_json_file_path, 'w') as json_file: + json.dump(data, json_file, indent=4) + return True # it worked: + #updated_json = json.dumps(data, indent=4) + except: + return "error" + + # if it does NOT exist: + else: + # We are creating a new Profile Config: + + # stock the JSON with values for a new profile: + default_json_content = { + "profile": { + "location": chosen_location, + "assigned_proxy": proxy_or_not, + "got_proxy": False, + "email_operator": email_operator + }, + "orders": { + peer_to_peer_billing_id: { + "chosen_service": chosen_service, + "wants_sms": True, + "completed": False + } + } + } + + try: + # Create the JSON file: + with open(full_json_file_path, 'w') as json_file: + json.dump(default_json_content, json_file, indent=4) + return True # it worked: + except: + return "error" + + + +def return_all_locally_saved_orders_for_the_current_profile(): + # get the path and other variables that are not needed, but have to be unpacked: + json_file_name, path_for_configs, full_json_file_path = return_profile_filename_and_path("sms") + + # Check if the JSON file exists: + if os.path.exists(full_json_file_path): + # setup a list to save the orders: + list_of_all_past_orders = [] + + try: + # if so get the data: + with open(full_json_file_path, 'r') as json_file: + data = json.load(json_file) + + # Iterate through the orders & get their past details: + for order in data["orders"]: + chosen_service = data["orders"][order]["chosen_service"] + completed = data["orders"][order]["completed"] + order_data_as_tuple = (order, chosen_service, completed) + list_of_all_past_orders.append(order_data_as_tuple) + except: + return False + finally: + return list_of_all_past_orders + else: + return False + + + +def delete_an_order(which_order): + # get the profile number & path: + json_file_name, path_for_configs, full_json_file_path = return_profile_filename_and_path("sms") + + if os.path.exists(full_json_file_path): + # if so get the data: + with open(full_json_file_path, 'r') as json_file: + data = json.load(json_file) + + try: + # delete it: + del data["orders"][which_order] + + # Update the JSON Profile with new data: + with open(full_json_file_path, 'w') as json_file: + json.dump(data, json_file, indent=4) + return True # it worked + except: + return "error" + +''' + # see if email operator is blank even though they have one from the past: + if email_operator == None: + # then get the profile number & path: + json_file_name, path_for_configs, full_json_file_path = return_profile_filename_and_path("sms") + try: + with open(full_json_file_path, 'r') as json_file: + data = json_file.load(json_file) + email_operator = data[profile][email_operator] + except: + email_operator = None +''' + +#email_operator = None +#full_email = "test@t.com" +#email_password = "dd" +#email_url = "aa.com" +#config_file_data = "a" +#setup_email_config(config_file_data, email_operator, full_email, email_password, email_url) \ No newline at end of file diff --git a/SMS-Exchange-Linux-GUI/local_data_operations/search_or_setup_json.py b/SMS-Exchange-Linux-GUI/local_data_operations/search_or_setup_json.py new file mode 100755 index 0000000..46dc0fb --- /dev/null +++ b/SMS-Exchange-Linux-GUI/local_data_operations/search_or_setup_json.py @@ -0,0 +1,74 @@ +import os +import json + +def check_and_create_folder_and_json(folder_path, json_file_name, default_json_content): + # Check if the folder exists + if not os.path.exists(folder_path): + # Create the folder if it doesn't exist + os.makedirs(folder_path) + print(f"Folder '{folder_path}' created.") + else: + print(f"Folder '{folder_path}' already exists.") + + # Full path for the JSON file + json_file_path = os.path.join(folder_path, json_file_name) + + # Check if the JSON file exists + if not os.path.exists(json_file_path): + # If it doesn't exist, then create the JSON file with "hello world" + with open(json_file_path, 'w') as json_file: + json.dump(default_json_content, json_file, indent=4) + print(f"JSON file '{json_file_name}' created with content: {{'message': 'hello world'}}.") + else: + print(f"JSON file '{json_file_name}' already exists in '{folder_path}'.") + +## Load JSON file and return it as a dictionary. +def load_json_as_dict(folder_path, json_file_name, default_json_content): + full_path_with_filename = f"{folder_path}/{json_file_name}" + if not os.path.exists(full_path_with_filename): + print(f"JSON file '{full_path_with_filename}' does not exist yet. So the program is now making it") + check_and_create_folder_and_json(folder_path, json_file_name, default_json_content) + + # Regardless of it was just made or not, load it, + with open(full_path_with_filename, 'r') as json_file: + data = json.load(json_file) + return data + +## Search for a key in the dictionary and return its value or a message if not found. +def search_key_in_json(json_dict, key, default_value_if_not_found): + return json_dict.get(key, default_value_if_not_found) + +def get_the_path_of_the_folder_for(which_folder_type, subfolder_name): + HOME = os.path.expanduser('~') # Get the user's home directory + config_path = os.path.join(HOME, which_folder_type) # Create a path to the .config directory + path_for_configs = f"{config_path}/sms-exchange/{subfolder_name}" + return path_for_configs + + +def replace_and_save_value_in_json(folder_path, json_file_name, default_json_content, key_value_to_find, value_to_replace): + # Check if the folder exists + if not os.path.exists(folder_path): + # Create the folder if it doesn't exist + os.makedirs(folder_path) + + # Full path for the JSON file + json_file_path = os.path.join(folder_path, json_file_name) + + # Check if the JSON file exists, + if os.path.exists(json_file_path): + # and if so then load it: + with open(json_file_path, 'r') as json_file: + data = json.load(json_file) + else: + # otherwise use the default values: + data = json.load(json_file_path) + + # Replace the value of the key "key_value_to_find" with "value_to_replace" + data[key_value_to_find] = value_to_replace + + # Now write the entire thing into the JSON file: + with open(json_file_path, 'w') as json_file: + json.dump(data, json_file, indent=4) + + return data + diff --git a/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/convert_to_dictonary.cpython-311.pyc b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/convert_to_dictonary.cpython-311.pyc new file mode 100755 index 0000000..8a6e20b Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/convert_to_dictonary.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/convert_to_dictonary.cpython-312.pyc b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/convert_to_dictonary.cpython-312.pyc new file mode 100755 index 0000000..1be5214 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/convert_to_dictonary.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/extract_a_list_of_values.cpython-311.pyc b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/extract_a_list_of_values.cpython-311.pyc new file mode 100755 index 0000000..f1113e9 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/extract_a_list_of_values.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/extract_a_list_of_values.cpython-312.pyc b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/extract_a_list_of_values.cpython-312.pyc new file mode 100755 index 0000000..d524ab6 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/extract_a_list_of_values.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/read_from_text_file_into_list.cpython-311.pyc b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/read_from_text_file_into_list.cpython-311.pyc new file mode 100755 index 0000000..dafe2e2 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/read_from_text_file_into_list.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/read_from_text_file_into_list.cpython-312.pyc b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/read_from_text_file_into_list.cpython-312.pyc new file mode 100755 index 0000000..57f9d79 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_api_data/__pycache__/read_from_text_file_into_list.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_api_data/convert_to_dictonary.py b/SMS-Exchange-Linux-GUI/process_api_data/convert_to_dictonary.py new file mode 100755 index 0000000..6697be4 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/process_api_data/convert_to_dictonary.py @@ -0,0 +1,13 @@ +import json + +def search_by_category(category, data): + # Initialize an empty list to hold matching items + matching_items = [] + for entry in data: + # Check if the category matches + if category in entry.values(): + matching_items.append(entry) + + # Convert dictonary of results into a list: + list_of_matching_items = [list(d.keys())[0] for d in matching_items] + return list_of_matching_items diff --git a/SMS-Exchange-Linux-GUI/process_api_data/extract_a_list_of_values.py b/SMS-Exchange-Linux-GUI/process_api_data/extract_a_list_of_values.py new file mode 100755 index 0000000..c7e4b4d --- /dev/null +++ b/SMS-Exchange-Linux-GUI/process_api_data/extract_a_list_of_values.py @@ -0,0 +1,31 @@ +import json + +def extract_a_list_of_values(json_list, what_to_look_for): + # Convert the list to a JSON string + json_string = json.dumps(json_list) + + # Convert the JSON string back to a list of dictionaries + data = json.loads(json_string) + + result = [] + for item in data: + if isinstance(item, dict): + #print(f"checking this item {item}") + if item.get('type') == what_to_look_for: + result.append(item.get('what')) + return result + + +def get_the_locations_for_a_service(json_list, what_to_look_for): + # Convert the list to a JSON string + json_string = json.dumps(json_list) + + # Convert the JSON string back to a list of dictionaries + data = json.loads(json_string) + + result = [] + for item in data: + if isinstance(item, dict): + if item.get('what') == what_to_look_for: + return item.get('locations', ["monkey"]) + diff --git a/SMS-Exchange-Linux-GUI/process_api_data/read_from_text_file_into_list.py b/SMS-Exchange-Linux-GUI/process_api_data/read_from_text_file_into_list.py new file mode 100755 index 0000000..01bc31f --- /dev/null +++ b/SMS-Exchange-Linux-GUI/process_api_data/read_from_text_file_into_list.py @@ -0,0 +1,18 @@ +# Specify the path to your text file +#file_path = 'Buyer_Client_for_phone_number/list_of_services.txt' + +def read_from_text_file_into_list(file_path): + + # Initialize an empty list to store the strings + string_list = [] + + # Open the file and read the contents + with open(file_path, 'r') as file: + # Read the entire content of the file + content = file.read() + + # Split the content by ', ' and strip single quotes from each item + string_list = [item.strip().strip("'") for item in content.split(',')] + + # Return the resulting list + return string_list diff --git a/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/filter_email_names.cpython-311.pyc b/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/filter_email_names.cpython-311.pyc new file mode 100755 index 0000000..6743b78 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/filter_email_names.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/filter_email_names.cpython-312.pyc b/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/filter_email_names.cpython-312.pyc new file mode 100755 index 0000000..d731f73 Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/filter_email_names.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/load_email_operators.cpython-311.pyc b/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/load_email_operators.cpython-311.pyc new file mode 100755 index 0000000..356080f Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/load_email_operators.cpython-311.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/load_email_operators.cpython-312.pyc b/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/load_email_operators.cpython-312.pyc new file mode 100755 index 0000000..92a86dd Binary files /dev/null and b/SMS-Exchange-Linux-GUI/process_email_data/__pycache__/load_email_operators.cpython-312.pyc differ diff --git a/SMS-Exchange-Linux-GUI/process_email_data/filter_email_names.py b/SMS-Exchange-Linux-GUI/process_email_data/filter_email_names.py new file mode 100755 index 0000000..a5672f4 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/process_email_data/filter_email_names.py @@ -0,0 +1,10 @@ +# Convert it to lowercase and replace spaces with underscores to prevent SQL injections: +def filter_email_names(uppercase_chosen_email): + lowercase_raw_email = uppercase_chosen_email.lower() + + if lowercase_raw_email == "random email operator": + return "random" + if lowercase_raw_email == "no email" or lowercase_raw_email == "email disabled": + return "no" + else: + return lowercase_raw_email.replace(" ", "_") \ No newline at end of file diff --git a/SMS-Exchange-Linux-GUI/process_email_data/load_email_operators.py b/SMS-Exchange-Linux-GUI/process_email_data/load_email_operators.py new file mode 100755 index 0000000..06f8954 --- /dev/null +++ b/SMS-Exchange-Linux-GUI/process_email_data/load_email_operators.py @@ -0,0 +1,13 @@ +import json + +def load_email_operators(path_to_email_operators_list): + try: + with open(path_to_email_operators_list, 'r') as json_file: + data = json.load(json_file) + return data + except FileNotFoundError as e: + print(f"Error: {e}") + except json.JSONDecodeError as e: + print(f"Invalid JSON: {e}") + except Exception as e: + print(f"An error occurred: {e}") diff --git a/SMS-Exchange-Linux-GUI/requirements.txt b/SMS-Exchange-Linux-GUI/requirements.txt new file mode 100755 index 0000000..282130f --- /dev/null +++ b/SMS-Exchange-Linux-GUI/requirements.txt @@ -0,0 +1,19 @@ +aiohappyeyeballs==2.6.1 +aiohttp==3.13.3 +aiosignal==1.4.0 +asyncio==4.0.0 +attrs==25.4.0 +certifi==2026.1.4 +charset-normalizer==3.4.4 +frozenlist==1.8.0 +idna==3.11 +multidict==6.7.0 +propcache==0.4.1 +PyQt6==6.10.2 +PyQt6-Qt6==6.10.1 +PyQt6_sip==13.11.0 +qasync==0.28.0 +requests==2.32.5 +typing_extensions==4.15.0 +urllib3==2.6.3 +yarl==1.22.0