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 socks
import socket
from tqdm import tqdm

init(autoreset=True)

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

# ----------------------- Konfiguracja -----------------------
MAX_WORKERS = 100
SEMAPHORE_LIMIT = 30
CONNECT_TIMEOUT = 6
SMTP_OP_TIMEOUT = 8
RETRY_COUNT = 1
BACKOFF_BASE = 1.5
PROXY_CHECK_TIMEOUT = 3
IMAP_POLL_INTERVAL = 5

# ----------------------- Funkcje -----------------------
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 set_proxy(proxy_type, proxy_str):
    ip, port = proxy_str.split(':')
    port = int(port)
    types = {
        'http': socks.HTTP,
        'https': socks.HTTP,
        'socks4': socks.SOCKS4,
        'socks5': socks.SOCKS5
    }
    socks.setdefaultproxy(types[proxy_type], ip, port)
    socket.socket = socks.socksocket

def reset_socket():
    import _socket
    socket.socket = _socket.socket

def test_proxy_worker(proxy_type, proxy_str):
    try:
        set_proxy(proxy_type, proxy_str)
        sock = socket.create_connection(("142.250.195.108", 587), timeout=PROXY_CHECK_TIMEOUT)
        sock.close()
        reset_socket()
        return proxy_str
    except:
        reset_socket()
        return None

def test_proxy_fast(proxy_type, proxy_list):
    good = []
    with ThreadPoolExecutor(max_workers=MAX_WORKERS) as ex:
        futures = {ex.submit(test_proxy_worker, proxy_type, p): p for p in proxy_list}
        for f in tqdm(as_completed(futures), total=len(futures), desc="Testing proxy"):
            r = f.result()
            if r:
                good.append(r)
    return good

def send_email(smtp_info, to_email, subject, content, use_proxy, proxy_type, proxy_list, 
success_queue, semaphore):
    host, port, user, password = smtp_info
    port = int(port)

    # Tworzenie unikalnego ID SMTP
    unique_id = f"{host}|{port}|{user}|{password}"

    # Dodajemy ID do treści (fallback, gdy header zostanie usunięty przez provider)
    body = content + "\n\n[SMTP-ID] " + unique_id

    message = MIMEMultipart()
    message['From'] = user
    message['To'] = to_email
    message['Subject'] = subject
    # nadal dodajemy nagłówek (nie zaszkodzi jeśli provider go zachowa)
    message['X-SMTP-ID'] = unique_id
    message.attach(MIMEText(body, 'plain'))

    for attempt in range(RETRY_COUNT + 1):
        try:
            if use_proxy:
                proxy = random.choice(proxy_list)
                set_proxy(proxy_type, proxy)

            with semaphore:
                context = ssl.create_default_context()
                if port == 465:
                    server = smtplib.SMTP_SSL(host, port, timeout=SMTP_OP_TIMEOUT, context=context)
                else:
                    server = smtplib.SMTP(host, port, timeout=SMTP_OP_TIMEOUT)
                    server.starttls(context=context)

                server.login(user, password)
                server.sendmail(user, to_email, message.as_string())
                server.quit()

                # jak wcześniej: zapisujemy ID do kolejki (wyjściowo będą w niej wysłane ID)
                success_queue.put(unique_id)
                print(Fore.GREEN + f"[+] Email sent: {unique_id}")
                break

        except Exception as e:
            wait = BACKOFF_BASE * (attempt + 1)
            print(Fore.YELLOW + f"[WARN] Attempt {attempt+1} failed for {user} -> {e}. Backing off {wait:.1f}s")
            time.sleep(wait)

        finally:
            if use_proxy:
                reset_socket()

import re
from html import unescape

def imap_check(server, user, password, subject, duration_minutes, success_queue):
    """
    Solidny IMAP check:
    - odczytuje nagłówek X-SMTP-ID
    - fallback: wyszukuje w text/plain lub text/html wzorzec [SMTP-ID] <id>
    - obsługuje różne charsety i dekodowanie
    - działa przez duration_minutes i zbiera potwierdzone ID w found_ids
    - na końcu opróżnia success_queue i wpisuje tam tylko potwierdzone ID
    """
    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")

            # Szukamy wszystkich maili o danym temacie
            # Uwaga: jeśli subject zawiera cudzysłowy lub znaki specjalne, IMAP SEARCH może się różnie zachować.
            # Jeśli masz problemy -> użyj prostszego tematu bez znaków specjalnych.
            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)

                        # 1) próbujemy nagłówek X-SMTP-ID
                        smtp_id = msg.get('X-SMTP-ID')
                        if smtp_id:
                            smtp_id = smtp_id.strip()
                        else:
                            smtp_id = None

                        # 2) fallback: przeszukaj text/plain i text/html na wzorzec [SMTP-ID] <id>
                        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 '')
                                    # pomijamy załączniki
                                    if 'attachment' in disp.lower():
                                        continue
                                    try:
                                        payload = part.get_payload(decode=True)
                                    except:
                                        payload = None
                                    if not payload:
                                        continue
                                    # spróbuj odszyfrować z odpowiednim charsetem
                                    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':
                                        # prosty strip html -> extract text
                                        # usuwamy tagi najszybciej jak się da
                                        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()

                        # 3) jeśli mamy smtp_id to zapamiętujemy
                        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:
                        # błąd fetch pojedynczego maila - kontynuuj
                        print(Fore.RED + f"[IMAP FETCH ERROR] {e_msg}")
                        continue

            mail.logout()
            # krótkie info debugowe (możesz usunąć)
            if scanned:
                print(Fore.CYAN + f"[i] Skanowano {scanned} wiadomości z tematem '{subject}'")

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

        time.sleep(IMAP_POLL_INTERVAL)

    # KONIEC monitoringu: opróżniamy oryginalną kolejkę (zawierającą wysłane ID)
    try:
        while True:
            success_queue.get_nowait()
    except Exception:
        pass

    # wstawiamy potwierdzone ID z powrotem do kolejki
    for sid in found_ids:
        success_queue.put(sid)

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

# ----------------------- Main -----------------------
def main():
    smtp_file = input("Enter SMTP servers filename: ")
    threads = int(input("Enter number of threads: "))
    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 + "[i] Testing proxy...")
        proxy_list = test_proxy_fast(proxy_type, proxy_list_raw)

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

        if not proxy_list:
            print(Fore.RED + "Brak działających proxy. Kończę.")
            return

    smtp_servers = load_smtp_servers(smtp_file)
    success_queue = Queue()
    semaphore = threading.Semaphore(SEMAPHORE_LIMIT)

    with ThreadPoolExecutor(max_workers=threads) as executor:
        futures = []
        for smtp in smtp_servers:
            if len(smtp) == 4:
                futures.append(
                    executor.submit(send_email, smtp, to_email, subject, content, use_proxy, 
proxy_type, proxy_list, success_queue, semaphore)
                )

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

    print(Fore.CYAN + "[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"[+] Zapisano {len(good)} działających SMTP do good.txt")
    print(Fore.CYAN + "Ai by Revo - koniec programu")

if __name__ == "__main__":
    main()
