Mail.py

public
1 month ago 14 views python
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()
Raw