import tkinter as tk from tkinter import ttk, scrolledtext, messagebox import requests import json import time import threading # To run network requests in the background # --- API Configuration (will be managed by GUI) --- MAILSTORM_API_URL = "http://ypwri6hshlwgxltrzorrcptslirxk7qkrvrzdi6i5fvrm36kkgw764yd.onion" TOR_PROXY = { 'http': 'socks5h://127.0.0.1:9050', 'https': 'socks5h://127.0.0.1:9050' } # --- API Interaction Functions (modified to return results/errors) --- def check_health_api(access_key): """ Checks the health of the Mailstorm API. Returns a tuple: (success_boolean, message_string) """ endpoint = f"{MAILSTORM_API_URL}/health" try: response = requests.get(endpoint, proxies=TOR_PROXY, timeout=10) response.raise_for_status() if response.text.strip().lower() == "true": return True, "Service is healthy." else: return False, f"Service is not healthy. Response: {response.text}" except requests.exceptions.Timeout: return False, "Health Check Error: Request timed out." except requests.exceptions.RequestException as e: return False, f"Health Check Error: {e}" def check_access_usage_api(access_key): """ Checks the usage statistics for the provided access key. Returns a tuple: (success_boolean, message_string) """ endpoint = f"{MAILSTORM_API_URL}/access_usage" payload = {"access": access_key} try: response = requests.post(endpoint, proxies=TOR_PROXY, json=payload, timeout=10) response.raise_for_status() return True, f"Access Usage: {response.text}" except requests.exceptions.Timeout: return False, "Access Usage Error: Request timed out." except requests.exceptions.RequestException as e: return False, f"Access Usage Error: {e}" def bomb_mail_api(first_name: str, last_name: str, email: str, access_key: str, max_retries=5, retry_delay=5): """ Subscribes an email address to mailing lists. Retries on specific errors that should be ignored. Returns a tuple: (success_boolean, message_string) """ endpoint = f"{MAILSTORM_API_URL}/bombmail" payload = { "first_name": first_name, "last_name": last_name, "email": email, "access": access_key } ignored_errors = ["mail not sent", "csrf error", "link non existent"] for attempt in range(max_retries + 1): try: response = requests.post(endpoint, proxies=TOR_PROXY, json=payload, timeout=20) # Successful response if response.status_code == 200 and response.text.strip() == "sent": return True, f"Bomb Mail Success for {email}: Sent." # Handle specific ignored errors response_text = response.text.strip().lower() if any(err in response_text for err in ignored_errors): message = f"Bomb Mail Warning for {email}: Ignored error '{response_text}'. Retrying ({attempt+1}/{max_retries})..." if attempt < max_retries: time.sleep(retry_delay) continue # Retry the request else: return False, f"Bomb Mail Failed for {email} after {max_retries} retries due to ignored errors." # Handle other bad requests elif response.status_code == 400: error_detail = response.json() if response.headers.get('Content-Type') == 'application/json' else response.text return False, f"Bomb Mail Error for {email}: Bad Request. Response: {error_detail}" # Handle server errors elif response.status_code == 500: error_detail = response.json() if response.headers.get('Content-Type') == 'application/json' else response.text return False, f"Bomb Mail Error for {email}: Internal Server Error. Response: {error_detail}" else: response.raise_for_status() # Raise for any other unexpected status codes except requests.exceptions.Timeout: message = f"Bomb Mail Error for {email}: Request timed out. Retrying ({attempt+1}/{max_retries})..." if attempt < max_retries: time.sleep(retry_delay) continue else: return False, f"Bomb Mail Failed for {email} after {max_retries} retries due to timeout." except requests.exceptions.RequestException as e: message = f"Bomb Mail Error for {email}: {e}. Retrying ({attempt+1}/{max_retries})..." if attempt < max_retries: time.sleep(retry_delay) continue else: return False, f"Bomb Mail Failed for {email} after {max_retries} retries." return False, "Bomb Mail Failed: Unexpected error." # Fallback # --- GUI Application --- class MailstormGUI: def __init__(self, master): self.master = master master.title("Mailstorm API Client") master.geometry("600x550") # Adjusted size self.log_messages = [] # --- Access Key Input --- ttk.Label(master, text="Access Key:").grid(row=0, column=0, padx=5, pady=5, sticky="w") self.access_key_entry = ttk.Entry(master, width=50) self.access_key_entry.grid(row=0, column=1, padx=5, pady=5, columnspan=2, sticky="ew") self.access_key_entry.insert(0, "YOUR_ACCESS_KEY_HERE") # Placeholder # --- Input Fields for Bomb Mail --- ttk.Label(master, text="First Name:").grid(row=1, column=0, padx=5, pady=5, sticky="w") self.first_name_entry = ttk.Entry(master, width=40) self.first_name_entry.grid(row=1, column=1, padx=5, pady=5, columnspan=2, sticky="ew") self.first_name_entry.insert(0, "John") # Default value ttk.Label(master, text="Last Name:").grid(row=2, column=0, padx=5, pady=5, sticky="w") self.last_name_entry = ttk.Entry(master, width=40) self.last_name_entry.grid(row=2, column=1, padx=5, pady=5, columnspan=2, sticky="ew") self.last_name_entry.insert(0, "Doe") # Default value ttk.Label(master, text="Email:").grid(row=3, column=0, padx=5, pady=5, sticky="w") self.email_entry = ttk.Entry(master, width=40) self.email_entry.grid(row=3, column=1, padx=5, pady=5, columnspan=2, sticky="ew") self.email_entry.insert(0, "john.doe@example.com") # Default value # --- Buttons --- self.check_health_button = ttk.Button(master, text="Check Health", command=self.run_check_health) self.check_health_button.grid(row=4, column=0, padx=5, pady=10, sticky="ew") self.check_usage_button = ttk.Button(master, text="Check Usage", command=self.run_check_usage) self.check_usage_button.grid(row=4, column=1, padx=5, pady=10, sticky="ew") self.send_bombmail_button = ttk.Button(master, text="Send Bomb Mail", command=self.run_bomb_mail) self.send_bombmail_button.grid(row=4, column=2, padx=5, pady=10, sticky="ew") # --- Output Log Area --- ttk.Label(master, text="API Log:").grid(row=5, column=0, padx=5, pady=5, sticky="w") self.log_area = scrolledtext.ScrolledText(master, wrap=tk.WORD, width=70, height=15, state='disabled') self.log_area.grid(row=6, column=0, columnspan=3, padx=5, pady=5, sticky="nsew") # Configure grid weights for resizing master.grid_columnconfigure(1, weight=1) master.grid_columnconfigure(2, weight=1) master.grid_rowconfigure(6, weight=1) self.update_log("GUI initialized. Ensure Tor is running and proxy is accessible (127.0.0.1:9050).") self.update_log("Please enter your Access Key.") def update_log(self, message): """Appends a message to the log area.""" self.log_messages.append(message) self.log_area.config(state='normal') self.log_area.insert(tk.END, message + "\n") self.log_area.see(tk.END) # Auto-scroll to the bottom self.log_area.config(state='disabled') self.master.update_idletasks() # Force GUI update def run_api_action(self, action_func, *args): """Helper to run API functions in a separate thread and update GUI.""" self.update_log(f"Starting action: {action_func.__name__}...") # Disable buttons during API call self.check_health_button.config(state=tk.DISABLED) self.check_usage_button.config(state=tk.DISABLED) self.send_bombmail_button.config(state=tk.DISABLED) def task(): try: result, message = action_func(*args) self.update_log(message) if not result and "Error" in message: messagebox.showerror("API Error", message) except Exception as e: self.update_log(f"An unexpected error occurred in thread: {e}") messagebox.showerror("Application Error", f"An unexpected error occurred: {e}") finally: # Re-enable buttons after API call self.check_health_button.config(state=tk.NORMAL) self.check_usage_button.config(state=tk.NORMAL) self.send_bombmail_button.config(state=tk.NORMAL) thread = threading.Thread(target=task) thread.daemon = True # Allows the program to exit even if thread is running thread.start() def run_check_health(self): access_key = self.access_key_entry.get() if not access_key or access_key == "YOUR_ACCESS_KEY_HERE": messagebox.showwarning("Input Missing", "Please enter your Access Key.") return self.run_api_action(check_health_api, access_key) def run_check_usage(self): access_key = self.access_key_entry.get() if not access_key or access_key == "YOUR_ACCESS_KEY_HERE": messagebox.showwarning("Input Missing", "Please enter your Access Key.") return self.run_api_action(check_access_usage_api, access_key) def run_bomb_mail(self): access_key = self.access_key_entry.get() first_name = self.first_name_entry.get() last_name = self.last_name_entry.get() email = self.email_entry.get() if not access_key or access_key == "YOUR_ACCESS_KEY_HERE": messagebox.showwarning("Input Missing", "Please enter your Access Key.") return if not first_name or not last_name or not email: messagebox.showwarning("Input Missing", "Please fill in all recipient details (First Name, Last Name, Email).") return # Basic email format validation if "@" not in email or "." not in email.split('@')[1]: messagebox.showwarning("Invalid Input", "Please enter a valid email address.") return self.run_api_action(bomb_mail_api, first_name, last_name, email, access_key) # --- Main Execution --- if __name__ == "__main__": root = tk.Tk() gui = MailstormGUI(root) root.mainloop()