import smtplib
import ssl
import imaplib
import email
import threading
import time
import random
import os
import datetime
from concurrent.futures import ThreadPoolExecutor, as_completed
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from queue import Queue
from colorama import init, Fore
import requests
from tqdm import tqdm
import re
from html import unescape
import socks
import socket

init(autoreset=True)

print(Fore.CYAN + "Ai by Revo - ULTRA FAST SMTP Checker [Per-Thread Proxy Edition]")

# ----------------------- Konfiguracja -----------------------
CONNECT_TIMEOUT = 5
SMTP_OP_TIMEOUT = 6
IMAP_POLL_INTERVAL = 5

# ----------------------- Funkcje -----------------------
def ask_proxy_mode(proxy_list):
    print("\n=== Proxy Mode ===")
    print(f"Wczytano {len(proxy_list)} proxy.")
    print("1) Najpierw sprawdź proxy (wolniej, ale pewność działania)")
    print("2) Pomijam test — od razu korzystaj z proxy do SMTP")

    while True:
        choice = input("Wybierz opcję 1 lub 2: ").strip()
        if choice == "1":
            return "check"
        elif choice == "2":
            return "skip"
        else:
            print("Niepoprawna opcja!")

def load_smtp_servers(filename):
    with open(filename, 'r', encoding='utf-8', errors='ignore') as f:
        return [line.strip().split('|') for line in f if line.strip() and 
len(line.strip().split('|')) == 4]

def load_proxies(filename):
    with open(filename, 'r', encoding='utf-8', errors='ignore') as f:
        return [line.strip() for line in f if line.strip()]

def test_proxy_worker_fast(proxy_type, proxy_str):
    """Ultra fast proxy test using requests library."""
    try:
        if proxy_type in ['http', 'https']:
            proxies = {
                'http': f'http://{proxy_str}',
                'https': f'http://{proxy_str}'
            }
        elif proxy_type == 'socks4':
            proxies = {
                'http': f'socks4://{proxy_str}',
                'https': f'socks4://{proxy_str}'
            }
        elif proxy_type == 'socks5':
            proxies = {
                'http': f'socks5://{proxy_str}',
                'https': f'socks5://{proxy_str}'
            }
        else:
            return None

        response = requests.get(
            'http://www.google.com',
            proxies=proxies,
            timeout=2,
            allow_redirects=False
        )
        
        if response.status_code:
            return proxy_str
        return None
        
    except Exception:
        return None

def test_proxy_ultra_fast(proxy_type, proxy_list):
    """Ultra fast proxy testing."""
    print(Fore.YELLOW + "[i] Using ULTRA FAST mode")
    good = []
    with ThreadPoolExecutor(max_workers=300) as ex:
        futures = {ex.submit(test_proxy_worker_fast, proxy_type, p): p for p in proxy_list}
        for f in tqdm(as_completed(futures), total=len(futures), desc="Testing proxy"):
            try:
                r = f.result()
            except Exception:
                r = None
            if r:
                good.append(r)
    return good

class ProxiedSMTPConnection:
    """
    SMTP connection with per-instance proxy support.
    Each instance has its own proxy - NO GLOBAL SOCKET PATCHING!
    """
    def __init__(self, host, port, proxy_type=None, proxy_str=None, timeout=10):
        self.host = host
        self.port = int(port)
        self.proxy_type = proxy_type
        self.proxy_str = proxy_str
        self.timeout = timeout
        self.smtp = None
        
    def connect(self):
        """Create SMTP connection through proxy (if specified)."""
        context = ssl.create_default_context()
        
        if self.proxy_str and self.proxy_type:
            # Create socket with proxy
            proxy_ip, proxy_port = self.proxy_str.split(':')
            proxy_port = int(proxy_port)
            
            # Map proxy type
            proxy_types = {
                'http': socks.HTTP,
                'https': socks.HTTP,
                'socks4': socks.SOCKS4,
                'socks5': socks.SOCKS5
            }
            
            # Create proxied socket
            sock = socks.socksocket()
            sock.set_proxy(
                proxy_type=proxy_types.get(self.proxy_type, socks.SOCKS5),
                addr=proxy_ip,
                port=proxy_port
            )
            sock.settimeout(self.timeout)
            sock.connect((self.host, self.port))
            
            # Wrap in SMTP
            if self.port == 465:
                # Direct SSL
                self.smtp = smtplib.SMTP_SSL(self.host, self.port, timeout=self.timeout, 
context=context)
                self.smtp.sock = context.wrap_socket(sock, server_hostname=self.host)
            else:
                # STARTTLS
                self.smtp = smtplib.SMTP(self.host, self.port, timeout=self.timeout)
                self.smtp.sock = sock
                self.smtp.ehlo()
                self.smtp.starttls(context=context)
                self.smtp.ehlo()
        else:
            # No proxy - direct connection
            if self.port == 465:
                self.smtp = smtplib.SMTP_SSL(self.host, self.port, timeout=self.timeout, 
context=context)
            else:
                self.smtp = smtplib.SMTP(self.host, self.port, timeout=self.timeout)
                self.smtp.starttls(context=context)
        
        return self.smtp
    
    def login(self, user, password):
        """Login to SMTP server."""
        if self.smtp:
            self.smtp.login(user, password)
    
    def sendmail(self, from_addr, to_addr, msg):
        """Send email."""
        if self.smtp:
            self.smtp.sendmail(from_addr, to_addr, msg)
    
    def quit(self):
        """Close connection."""
        if self.smtp:
            try:
                self.smtp.quit()
            except:
                try:
                    self.smtp.close()
                except:
                    pass

def send_email_fast(smtp_info, to_email, subject, content, use_proxy, proxy_type, proxy_list, 
success_queue):
    """
    Ultra-fast email sender with per-thread proxy.
    NO LOCKS, NO SEMAPHORES - Pure parallelism!
    """
    host, port, user, password = smtp_info
    port = int(port)
    
    unique_id = f"{host}|{port}|{user}|{password}"
    body = content + "\n\n[SMTP-ID] " + unique_id
    
    message = MIMEMultipart()
    message['From'] = user
    message['To'] = to_email
    message['Subject'] = subject
    message['X-SMTP-ID'] = unique_id
    message.attach(MIMEText(body, 'plain'))
    
    chosen_proxy = None
    connection = None
    
    try:
        # Choose random proxy if enabled
        if use_proxy and proxy_list:
            chosen_proxy = random.choice(proxy_list)
        
        # Create connection with dedicated proxy
        connection = ProxiedSMTPConnection(
            host=host,
            port=port,
            proxy_type=proxy_type if use_proxy else None,
            proxy_str=chosen_proxy if use_proxy else None,
            timeout=SMTP_OP_TIMEOUT
        )
        
        # Connect, login, send
        connection.connect()
        connection.login(user, password)
        connection.sendmail(user, to_email, message.as_string())
        connection.quit()
        
        # Success!
        success_queue.put(unique_id)
        if use_proxy and chosen_proxy:
            print(Fore.GREEN + f"[+] ✓ {user[:30]} via {chosen_proxy}")
        else:
            print(Fore.GREEN + f"[+] ✓ {user[:30]}")
            
    except Exception as e:
        error_msg = str(e)[:60]
        print(Fore.RED + f"[-] ✗ {user[:30]} -> {error_msg}")
    
    finally:
        if connection:
            try:
                connection.quit()
            except:
                pass

def imap_check(server, user, password, subject, duration_minutes, success_queue):
    """IMAP verification with smart ID detection."""
    id_pattern = re.compile(r'\[SMTP[-_\s]?ID\]\s*([^\s]+(?:\|[^\s]+)*)', re.IGNORECASE)
    found_ids = set()
    end_time = time.time() + duration_minutes * 60

    print(Fore.CYAN + "[i] IMAP monitor running...")

    while time.time() < end_time:
        try:
            mail = imaplib.IMAP4_SSL(server)
            mail.login(user, password)
            mail.select("inbox")

            today = datetime.date.today().strftime("%d-%b-%Y")
            result, data = mail.search(None, f'SINCE "{today}"')

            scanned = 0
            if result == 'OK' and data and data[0]:
                ids = data[0].split()
                scanned = len(ids)
                for msg_id in ids:
                    try:
                        res2, msg_data = mail.fetch(msg_id, '(RFC822)')
                        if res2 != 'OK' or not msg_data or not msg_data[0]:
                            continue
                        raw = msg_data[0][1]
                        msg = email.message_from_bytes(raw)

                        smtp_id = msg.get('X-SMTP-ID')
                        if smtp_id:
                            smtp_id = smtp_id.strip()
                        else:
                            smtp_id = None

                        if not smtp_id:
                            body_text = ''
                            if msg.is_multipart():
                                for part in msg.walk():
                                    ctype = part.get_content_type()
                                    disp = str(part.get('Content-Disposition') or '')
                                    if 'attachment' in disp.lower():
                                        continue
                                    try:
                                        payload = part.get_payload(decode=True)
                                    except:
                                        payload = None
                                    if not payload:
                                        continue
                                    charset = part.get_content_charset() or part.get_charsets() or 'utf-8'
                                    if isinstance(charset, (list, tuple)):
                                        charset = charset[0] or 'utf-8'
                                    try:
                                        text = payload.decode(charset, errors='ignore')
                                    except:
                                        try:
                                            text = payload.decode('utf-8', errors='ignore')
                                        except:
                                            text = str(payload)
                                    if ctype == 'text/plain':
                                        body_text += '\n' + text
                                    elif ctype == 'text/html':
                                        txt = re.sub(r'<script.*?>.*?</script>', '', text, 
flags=re.S|re.I)
                                        txt = re.sub(r'<style.*?>.*?</style>', '', txt, 
flags=re.S|re.I)
                                        txt = re.sub(r'<[^>]+>', ' ', txt)
                                        txt = unescape(txt)
                                        body_text += '\n' + txt
                            else:
                                try:
                                    payload = msg.get_payload(decode=True)
                                    if payload:
                                        ch = msg.get_content_charset() or 'utf-8'
                                        try:
                                            body_text = payload.decode(ch, errors='ignore')
                                        except:
                                            body_text = payload.decode('utf-8', errors='ignore')
                                    else:
                                        body_text = ''
                                except:
                                    body_text = ''

                            if body_text:
                                m = id_pattern.search(body_text)
                                if m:
                                    smtp_id = m.group(1).strip()

                        if smtp_id:
                            if smtp_id not in found_ids:
                                print(Fore.YELLOW + f"[+] Delivered: {smtp_id}")
                                found_ids.add(smtp_id)

                    except Exception as e_msg:
                        print(Fore.RED + f"[IMAP FETCH ERROR] {e_msg}")
                        continue

            mail.logout()
            if scanned:
                print(Fore.CYAN + f"[i] Skanowano {scanned} wiadomości")

        except Exception as e:
            print(Fore.RED + f"[IMAP ERROR] {e}")

        time.sleep(IMAP_POLL_INTERVAL)

    # Clear queue and add only confirmed IDs
    try:
        while True:
            success_queue.get_nowait()
    except Exception:
        pass

    for sid in found_ids:
        success_queue.put(sid)

    print(Fore.GREEN + "[+] Finished IMAP monitoring.")

# ----------------------- Main -----------------------
def main():
    print(Fore.CYAN + "\n" + "="*60)
    print(Fore.CYAN + "  ULTRA FAST SMTP Checker - Per-Thread Proxy Edition")
    print(Fore.CYAN + "  NO LOCKS | NO SEMAPHORES | PURE SPEED")
    print(Fore.CYAN + "="*60 + "\n")
    
    smtp_file = input("Enter SMTP servers filename: ")
    threads = int(input("Enter number of threads (recommended: 100-200): "))
    to_email = input("Enter recipient email: ")
    subject = input("Enter email subject: ")
    content = input("Enter email content: ")
    use_proxy = input("Use proxy? (y/n): ").lower() == 'y'

    proxy_list = []
    proxy_type = ''

    if use_proxy:
        proxy_file = input("Enter proxy list filename: ")
        proxy_type = input("Proxy type (http/https/socks4/socks5): ").lower()
        proxy_list_raw = load_proxies(proxy_file)

        print(Fore.CYAN + f"[i] Wczytano proxy: {len(proxy_list_raw)}")

        mode = ask_proxy_mode(proxy_list_raw)

        if mode == "check":
            print(Fore.CYAN + "[i] Testing proxy (ULTRA FAST mode)...")
            proxy_list = test_proxy_ultra_fast(proxy_type, proxy_list_raw)

            print(Fore.CYAN + f"[i] Working proxy: {len(proxy_list)}")

            if not proxy_list:
                print(Fore.RED + "Brak działających proxy. Kończę.")
                return
        else:
            print(Fore.YELLOW + "[i] Pomijam test proxy — używam pełnej listy.")
            proxy_list = proxy_list_raw

    # Load SMTP servers
    smtp_servers = load_smtp_servers(smtp_file)
    success_queue = Queue()

    print(Fore.CYAN + f"\n[i] Loaded {len(smtp_servers)} SMTP servers")
    print(Fore.YELLOW + f"[i] Starting with {threads} parallel threads")
    print(Fore.GREEN + "[i] NO SEMAPHORE - Full parallelism enabled! 🚀\n")

    # ULTRA FAST sending - NO SEMAPHORE!
    with ThreadPoolExecutor(max_workers=threads) as executor:
        futures = []
        for smtp in smtp_servers:
            if len(smtp) == 4:
                futures.append(
                    executor.submit(
                        send_email_fast, 
                        smtp, 
                        to_email, 
                        subject, 
                        content, 
                        use_proxy, 
                        proxy_type, 
                        proxy_list, 
                        success_queue
                    )
                )

        for _ in tqdm(as_completed(futures), total=len(futures), desc="Sending emails"):
            pass

    print(Fore.CYAN + "\n[i] Wysyłka zakończona. Rozpoczynam IMAP verification.")

    imap_server = input("IMAP server: ")
    imap_user = input("IMAP user: ")
    imap_pass = input("IMAP password: ")
    duration = int(input("Minutes to check inbox for replies: "))

    imap_check(imap_server, imap_user, imap_pass, subject, duration, success_queue)

    good = set()
    while not success_queue.empty():
        good.add(success_queue.get())

    with open("good.txt", "w", encoding="utf-8") as f:
        for s in good:
            f.write(s + "\n")

    print(Fore.GREEN + f"\n[+] Zapisano {len(good)} działających SMTP do good.txt")
    print(Fore.CYAN + "="*60)
    print(Fore.CYAN + "Ai by Revo - ULTRA FAST Edition - koniec programu")
    print(Fore.CYAN + "="*60)

if __name__ == "__main__":
    main()
