# -*- coding: utf-8 -*-
"""用 CDP Input.dispatchFileChangeEvent 上传封面图到百家号"""
import sys, socket, json, time, os, glob
sys.stdout.reconfigure(encoding="utf-8")

HOST = "localhost"
PORT = 28800
PAGE_ID = "70039F329097C14448996F189E3A42A1"

imgs = glob.glob(r"D:\wxpic\*.jpg") + glob.glob(r"D:\wxpic\*.png")
IMG = max(imgs, key=os.path.getmtime) if imgs else None
print("[INFO] 图片:", IMG)

def make_ws():
    import urllib.parse
    u = "ws://%s:%s/devtools/page/%s" % (HOST, PORT, PAGE_ID)
    p = urllib.parse.urlparse(u)
    s = socket.create_connection((p.hostname, p.port or 80), timeout=15)
    req = (
        "GET %s HTTP/1.1\r\nHost: %s:%s\r\nUpgrade: websocket\r\n"
        "Connection: Upgrade\r\nSec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"
        "Sec-WebSocket-Version: 13\r\n\r\n"
    ) % (p.path, p.hostname, p.port)
    s.send(req.encode())
    resp = b""
    while b"\r\n\r\n" not in resp:
        resp += s.recv(4096)
    return s

def ws_send(sock, data):
    data = data.encode("utf-8")
    n = len(data)
    m = b"\x01\x02\x03\x04"
    if n <= 125:
        h = bytes([0x81, n | 0x80]) + m
    elif n <= 65535:
        h = bytes([0x81, 0xFE]) + n.to_bytes(2, "big") + m
    else:
        h = bytes([0x81, 0xFF]) + n.to_bytes(8, "big") + m
    sock.send(h + bytes(data[i] ^ m[i % 4] for i in range(n)))

def ws_recv(sock):
    h = b""
    while len(h) < 2:
        h += sock.recv(2 - len(h))
    n = h[1] & 0x7F
    if n == 126:
        nb = b""
        while len(nb) < 2:
            nb += sock.recv(2 - len(nb))
        n = int.from_bytes(nb, "big")
    elif n == 127:
        nb = b""
        while len(nb) < 8:
            nb += sock.recv(8 - len(nb))
        n = int.from_bytes(nb, "big")
    body = b""
    while len(body) < n:
        body += sock.recv(n - len(body))
    return body.decode("utf-8")

_mid = [0]

def cdp(sock, method, params=None):
    if params is None:
        params = {}
    _mid[0] += 1
    ws_send(sock, json.dumps({"id": _mid[0], "method": method, "params": params}))
    sock.settimeout(20)
    for _ in range(300):
        try:
            r = json.loads(ws_recv(sock))
            if r.get("id") == _mid[0]:
                return r
        except Exception:
            pass
    return None

sock = make_ws()
print("[OK] Connected\n")

# 先列出所有可用的 CDP domain 方法来找到正确的文件上传 API
# 测试 DOM.setFileInputFiles
print("[1] 测试 DOM.setFileInputFiles...")
r = cdp(sock, "DOM.setFileInputFiles", {"files": [IMG]})
print("  DOM.setFileInputFiles:", r)

# 如果上面失败，尝试 Input.dispatchFileChangeEvent
print("\n[2] 测试 Input.dispatchFileChangeEvent...")
r = cdp(sock, "Input.dispatchFileChangeEvent", {
    "type": "fileInput",
    "files": [IMG],
    "nodeId": None
})
print("  Input.dispatchFileChangeEvent:", r)

# 检查有哪些 file inputs，遍历 shadow DOM 和 iframe
print("\n[3] 深度扫描 file inputs...")
r = cdp(sock, "Runtime.evaluate", {
    "expression": """
    (function() {
        var allInputs = [];
        // 主文档
        var mainInputs = document.querySelectorAll('input[type=file]');
        for (var i = 0; i < mainInputs.length; i++) {
            allInputs.push({doc: 'main', index: i, accept: mainInputs[i].accept || ''});
        }
        // 所有 iframe
        var iframes = document.querySelectorAll('iframe');
        for (var f = 0; f < iframes.length; f++) {
            try {
                var iframeInputs = iframes[f].contentDocument.querySelectorAll('input[type=file]');
                for (var j = 0; j < iframeInputs.length; j++) {
                    allInputs.push({doc: 'iframe_' + f, index: j, accept: iframeInputs[j].accept || ''});
                }
            } catch(e) {
                allInputs.push({doc: 'iframe_' + f, error: e.message});
            }
        }
        // 所有 shadow DOM
        var allEls = document.querySelectorAll('*');
        for (var el of allEls) {
            if (el.shadowRoot) {
                var shadowInputs = el.shadowRoot.querySelectorAll('input[type=file]');
                for (var k = 0; k < shadowInputs.length; k++) {
                    allInputs.push({doc: 'shadow_' + el.tagName, index: k, accept: shadowInputs[k].accept || ''});
                }
            }
        }
        return JSON.stringify(allInputs);
    })()
    """,
    "returnByValue": True
})
val = r.get("result", {}).get("result", {}).get("value", "") if r else ""
print("  file inputs:", val[:500])

sock.close()
print("\nDone")
