Files
douyin/凭证采集工具版本e.html
2026-07-17 09:24:47 +08:00

2881 lines
72 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>鎶栭煶鍑�璇侀噰闆嗗伐鍏� 路 鏈�鍦扮敓鎴� storage_state</title>
<style>
:root {
--bg: #0f1020;
--panel: #1a1b2e;
--panel2: #23244044;
--border: #2c2e4a;
--text: #e6e6f0;
--muted: #9aa0c0;
--accent: #8b5cf6;
--accent2: #a855f7;
--ok: #34d399;
--warn: #fbbf24;
--err: #f87171;
}
* { box-sizing: border-box; }
body {
margin: 0;
background: radial-gradient(1200px 600px at 70% -10%, #2a1c4a55, transparent), var(--bg);
color: var(--text);
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Microsoft YaHei", sans-serif;
line-height: 1.6;
padding: 32px 16px 80px;
}
.wrap { max-width: 920px; margin: 0 auto; }
h1 { font-size: 24px; margin: 0 0 4px; }
.sub { color: var(--muted); margin: 0 0 24px; font-size: 14px; }
.card {
background: var(--panel);
border: 1px solid var(--border);
border-radius: 14px;
padding: 20px 22px;
margin-bottom: 18px;
}
.step-h {
display: flex; align-items: center; gap: 10px;
font-weight: 600; font-size: 16px; margin-bottom: 10px;
}
.badge {
width: 26px; height: 26px; flex: none;
background: linear-gradient(135deg, var(--accent), var(--accent2));
border-radius: 50%; display: grid; place-items: center;
font-size: 14px; font-weight: 700; color: #fff;
}
p.tip { color: var(--muted); font-size: 13.5px; margin: 6px 0; }
code.k { background: #00000055; padding: 1px 6px; border-radius: 5px; color: #c4b5fd; font-size: 13px; }
textarea {
width: 100%; min-height: 92px; resize: vertical;
background: #0c0d1a; color: var(--text);
border: 1px solid var(--border); border-radius: 10px;
padding: 12px; font-family: ui-monospace, Consolas, monospace; font-size: 12.5px;
outline: none;
}
textarea:focus { border-color: var(--accent); }
.snippet {
position: relative; background: #0c0d1a; border: 1px solid var(--border);
border-radius: 10px; padding: 12px 12px; margin: 10px 0;
font-family: ui-monospace, Consolas, monospace; font-size: 12.5px;
white-space: pre-wrap; word-break: break-all; color: #a5b4fc;
}
button {
background: linear-gradient(135deg, var(--accent), var(--accent2));
color: #fff; border: none; border-radius: 10px;
padding: 10px 18px; font-size: 14px; font-weight: 600; cursor: pointer;
transition: opacity .15s;
}
button:hover { opacity: .9; }
button.ghost {
background: transparent; border: 1px solid var(--border); color: var(--muted);
padding: 6px 12px; font-size: 12.5px; font-weight: 500;
}
.row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 12px; }
.out { min-height: 160px; }
.checks { margin-top: 12px; display: grid; gap: 6px; }
.chk { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }
.dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.ok .dot { background: var(--ok); } .ok { color: var(--ok); }
.warn .dot { background: var(--warn); } .warn { color: var(--warn); }
.err .dot { background: var(--err); } .err { color: var(--err); }
.hint-box {
background: #1e1b3a; border: 1px solid #3b2f6b; border-radius: 10px;
padding: 10px 14px; margin: 10px 0; font-size: 13px; color: #cbd5e1;
}
ol { margin: 6px 0 6px 4px; padding-left: 20px; }
ol li { margin: 4px 0; font-size: 13.5px; }
.pill { font-size: 11px; color: var(--muted); border: 1px solid var(--border); padding: 1px 8px; border-radius: 999px; }
.toast {
position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%);
background: #111; border: 1px solid var(--accent); color: #fff;
padding: 10px 18px; border-radius: 10px; opacity: 0; transition: opacity .2s; pointer-events: none;
font-size: 13.5px;
}
.toast.show { opacity: 1; }
.toast.warn { border-color: #fbbf24; background: #2b231c; max-width: min(92vw, 520px); text-align: left; line-height: 1.5; }
.ws-tag { font-size: 11px; color: #6ee7b7; border: 1px solid #2f5b39; background: #1c2b1e; padding: 2px 8px; border-radius: 999px; margin-left: 6px; }
textarea.auto-filled { border-color: #34d399 !important; box-shadow: 0 0 0 2px rgba(52, 211, 153, 0.2); }
.ws-missing-hint {
background: #2b231c; border: 1px solid #b45309; border-radius: 10px;
padding: 12px 14px; margin-top: 12px; font-size: 13px; color: #fcd34d;
}
.ws-missing-hint ul { margin: 8px 0 0; padding-left: 18px; color: #fde68a; }
.ws-missing-hint li { margin: 4px 0; }
/* Tabs Navigation */
.tabs-nav {
display: flex;
gap: 8px;
margin-bottom: 20px;
border-bottom: 1px solid var(--border);
padding-bottom: 8px;
flex-wrap: wrap;
}
.tab-btn {
background: transparent;
border: 1px solid transparent;
color: var(--muted);
padding: 10px 16px;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.25s;
display: flex;
align-items: center;
gap: 8px;
border-radius: 10px;
outline: none;
}
.tab-btn:hover {
color: var(--text);
background: var(--panel2);
}
.tab-btn.active {
color: #fff;
background: var(--panel2);
border-color: var(--border);
box-shadow: 0 4px 12px rgba(139, 92, 246, 0.15);
}
.tab-num {
width: 22px;
height: 22px;
border-radius: 50%;
background: var(--border);
display: grid;
place-items: center;
font-size: 12px;
font-weight: 700;
color: var(--muted);
transition: all 0.25s;
}
.tab-btn.active .tab-num {
background: linear-gradient(135deg, var(--accent), var(--accent2));
color: #fff;
}
/* Tab Panes */
.tab-pane {
display: none;
}
@keyframes fadeIn {
from { opacity: 0; transform: translateY(8px); }
to { opacity: 1; transform: translateY(0); }
}
.tab-pane.active {
display: block;
animation: fadeIn 0.3s ease-out forwards;
}
</style>
</head>
<body>
<div class="wrap">
<h1>鎶栭煶鍑�璇侀噰闆嗗伐鍏� <span class="pill">鏈�鍦拌繍琛� 路 涓嶄笂浼犱换浣曟暟鎹�</span></h1>
<p class="sub">
鍦ㄤ綘<strong>鏈�鍦扮數鑴�</strong>鐨勬祻瑙堝櫒閲岀櫥褰曟姈闊崇綉椤电増锛屾寜涓嬮潰姝ラ�ら噰闆嗗嚟璇侊紝鐢熸垚鍙�鐩存帴瀵煎叆绯荤粺鐨� JSON銆�
瑙e喅鏈嶅姟鍣�锛圠inux/鏃犵晫闈�锛夋壂鐮佺櫥褰曞脊涓嶅嚭浜岀淮鐮佺殑闂�棰樸��
</p>
<div class="tabs-nav">
<button id="tab-btn-1" class="tab-btn active" onclick="switchTab(1)">
<span class="tab-num">1</span>
<span class="tab-txt">1. 鐧诲綍缃戦〉鐗�</span>
</button>
<button id="tab-btn-2" class="tab-btn" onclick="switchTab(2)">
<span class="tab-num">2</span>
<span class="tab-txt">2. 鐙�绔嬫彃浠堕噰闆� (鎺ㄨ崘)</span>
</button>
<button id="tab-btn-3" class="tab-btn" onclick="switchTab(3)">
<span class="tab-num">3</span>
<span class="tab-txt">3. 鎵嬪姩澶嶅埗 Cookie (澶囩敤)</span>
</button>
<button id="tab-btn-4" class="tab-btn" onclick="switchTab(4)">
<span class="tab-num">4</span>
<span class="tab-txt">4. 閲囬泦涓庣敓鎴�</span>
</button>
</div>
<!-- TAB PANE 1 -->
<div id="tab-pane-1" class="tab-pane active">
<!-- STEP 1 -->
<div class="card">
<div class="step-h"><span class="badge">1</span> 鐧诲綍鎶栭煶缃戦〉鐗�</div>
<p class="tip">
鐢ㄦ湰鍦� Chrome / Edge 鎵撳紑 <code class="k">https://www.douyin.com</code> 骞�<strong>鎵�鐮佺櫥褰�</strong>锛�
鐒跺悗<strong>鎵撳紑涓�娆°�岀�佷俊銆嶉〉闈�</strong>锛堣�╁畨鍏� SDK 鍐欏叆绛惧悕鍑�璇侊級銆備繚鎸佽�ユ爣绛鹃〉鎵撳紑銆�
</p>
</div>
<div class="row" style="margin-top:20px; justify-content: flex-end;">
<button onclick="switchTab(2)">涓嬩竴姝ワ細鐙�绔嬫彃浠堕噰闆�</button>
</div>
</div>
<!-- TAB PANE 2 -->
<div id="tab-pane-2" class="tab-pane">
<div class="card">
<div class="step-h"><span class="badge">2</span> 浣跨敤鐙�绔嬫祻瑙堝櫒鎻掍欢锛堜竴閿�鑾峰彇鎵�鏈夊嚟璇侊紝鏃犱换浣曠��涓夋柟渚濊禆锛�</div>
<p class="tip">
鐢变簬瀹夊叏闄愬埗锛屾姈闊崇綉椤电増鐨� <code class="k">sessionid</code> 鏄� HttpOnly 鐨勶紝鏅�閫氳剼鏈�鏃犳硶鐩存帴璇诲彇銆�
鎴戜滑涓轰綘鎻愪緵涓�涓�鏋佺畝鐨�**鐙�绔嬫祻瑙堝櫒鎻掍欢**锛屽姞杞藉悗浼氬湪鎶栭煶缃戦〉鍙充笅瑙掔敓鎴愪竴閿�閲囬泦鎸夐挳锛岃嚜鍔ㄦ彁鍙栧寘鍚� HttpOnly 鍦ㄥ唴鐨勬墍鏈夊嚟璇佸苟鎵撳寘澶嶅埗銆�
</p>
<div class="hint-box" style="background:#1e1b3a; border-color:#3b2f6b; padding:16px;">
<strong style="color: var(--ok); display: block; margin-bottom: 6px;">💡 系统已自动在本地创建插件目录,无需手动下载!</strong>
<p class="tip" style="margin-top:4px; color: var(--text); font-weight: 500;">
该插件的所有文件已保存在项目根目录的 <code class="k">douyin-extension</code> 文件夹中。你无需点击下方按钮下载,可直接进行下一步。
</p>
<div style="border-top: 1px dashed var(--border); margin: 12px 0 0; padding-top: 12px;">
<strong>备用方案:下载插件文件到本地(非本地运行或远程使用时)</strong>
<p class="tip" style="margin-top:6px;">在本地电脑新建一个文件夹(例如命名为 <code class="k">douyin-plugin</code>),依次点击下方三个按钮下载文件,并全部保存到该文件夹中:</p>
<div class="row" style="margin-top:12px; gap:12px;">
<button onclick="downloadPluginFile('manifest.json')">1. 下载 manifest.json</button>
<button onclick="downloadPluginFile('background.js')">2. 下载 background.js</button>
<button onclick="downloadPluginFile('content.js')">3. 下载 content.js</button>
</div>
</div>
</div>
<ol style="margin-top: 16px;">
<li>鎵撳紑 Chrome 鎴� Edge 娴忚�堝櫒锛屽湪鍦板潃鏍忚緭鍏� <code class="k">chrome://extensions/</code> (鎴� <code class="k">edge://extensions/</code>) 骞跺洖杞︺��</li>
<li>鍦ㄦ墿灞曠▼搴忕�$悊椤甸潰鐨勫彸涓婅�掞紝寮�鍚� **銆屽紑鍙戣�呮ā寮忋��** 鍒囨崲寮�鍏炽��</li>
<li>鐐瑰嚮宸︿笂瑙掔殑 **銆屽姞杞藉凡瑙e帇鐨勬墿灞曠▼搴忋��** (Load unpacked)锛岄�夋嫨浣犲垰鍒氬垱寤哄苟瀛樺叆閭� 3 涓�鏂囦欢鐨� <code class="k">douyin-plugin</code> 鏂囦欢澶广��</li>
<li>鍒锋柊浣�<strong>宸茬櫥褰曟姈闊崇殑缃戦〉鏍囩�鹃〉</strong>锛岄〉闈㈠彸涓嬭�掍細娴�鐜颁竴涓� <code class="k">馃搶 涓�閿�閲囬泦鍑�璇�</code> 鎸夐挳锛岀偣鍑诲畠銆�</li>
<li>鍑�璇佸皢琚�鑷�鍔ㄩ噰闆嗗苟澶嶅埗鍒板壀璐存澘锛岄殢鍚庣洿鎺ョ矘璐村埌涓嬫柟銆屾�ラ�� 4锛氶噰闆嗕笌鐢熸垚銆嶇殑杈撳叆妗嗗嵆鍙�锛�</li>
</ol>
<div class="hint-box" style="background:#1c2b1e; border-color:#2f5b39; margin-top:14px;">
<strong style="color:var(--ok);">鉁� 瀹夊叏涓庣�佸瘑淇濊瘉</strong>锛氳�ユ彃浠跺畬鍏ㄨ繍琛屽湪浣犵殑鏈�鍦版祻瑙堝櫒锛屾棤浠讳綍澶栭儴杩炴帴锛岀函鏈�鍦版彁鍙栵紝鏃犻渶瀹夎�呯��涓夋柟娌圭尨鎻掍欢銆�
</div>
</div>
<div class="row" style="margin-top:20px; justify-content: space-between;">
<button class="ghost" onclick="switchTab(1)">涓婁竴姝�</button>
<button onclick="switchTab(4)">涓嬩竴姝ワ細閲囬泦涓庣敓鎴�</button>
</div>
</div>
<!-- TAB PANE 3 -->
<div id="tab-pane-3" class="tab-pane">
<!-- STEP 2 -->
<div class="card">
<div class="step-h"><span class="badge">3</span> 澶囩敤鏂规�堬細鎵嬪姩浠庣綉缁滆�锋眰澶嶅埗 Cookie</div>
<p class="tip">
鑻ヤ笉鏂逛究瀹夎�呮补鐚存彃浠讹紝鍙�鎵嬪姩浠庡紑鍙戣�呭伐鍏凤紙F12锛夌殑缃戠粶璇锋眰澶村�嶅埗鍖呭惈 sessionid 鐨勫畬鏁� Cookie锛�
</p>
<ol>
<li>鍦ㄦ姈闊抽〉闈㈡寜 <code class="k">F12</code> 鈫� 鍒囧埌 <strong>Network锛堢綉缁滐級</strong> 闈㈡澘</li>
<li>鍦� Filter 杩囨护妗嗕腑杈撳叆 <code class="k">user_detail</code> 鎴� <code class="k">douyin.com</code> 骞跺埛鏂伴〉闈�</li>
<li>鍦ㄨ�锋眰鍒楄〃閲岀偣鍑诲�瑰簲鐨勮�锋眰锛堝�� <code class="k">user_detail/?aid=...</code>锛�</li>
<li>鎵惧埌 鍙充晶 <strong>Request Headers锛堣�锋眰鏍囧ご锛�</strong> 閲岀殑 <code class="k">Cookie:</code> 杩欎竴琛�</li>
<li>鎶� <code class="k">Cookie:</code> 鍚庨潰<strong>鏁存�靛��</strong>澶嶅埗锛堟垨鑰呭彸閿�璇ヨ�锋眰 -> Copy -> Copy request headers锛夛紝绮樿创鍒颁笅闈�锛�</li>
</ol>
<div class="hint-box">
绮樿创鍐呭�瑰簲绫讳技锛�<code class="k">ttwid=1%7C...; sessionid=xxxx; sessionid_ss=xxxx; sid_guard=...; uid_tt=...; ...</code>
鈥斺�� 蹇呴』鍖呭惈 <strong>sessionid</strong>銆�
</div>
<textarea id="cookieInput" placeholder="鍦ㄦ�ょ矘璐村畬鏁� Cookie 璇锋眰澶寸殑鍊硷紝鎴栨暣娈� Request Headers锛堝伐鍏蜂細鑷�鍔ㄤ粠涓�鎻愬彇 Cookie锛�"></textarea>
</div>
<div class="row" style="margin-top:20px; justify-content: space-between;">
<button class="ghost" onclick="switchTab(2)">涓婁竴姝�</button>
<button onclick="switchTab(4)">涓嬩竴姝ワ細閲囬泦涓庣敓鎴�</button>
</div>
</div>
<!-- TAB PANE 4 -->
<div id="tab-pane-4" class="tab-pane">
<!-- STEP 3 -->
<div class="card">
<div class="step-h"><span class="badge">4</span> 閲囬泦涓庣敓鎴�</div>
<p class="tip">
灏�<b>娌圭尨鑴氭湰閲囬泦鐨� `DYCRED1.` 瀵嗘枃</b>锛屾垨鑰�<b>涔︾��/鎺у埗鍙板�煎嚭鐨� localStorage JSON</b> 绮樿创鍒颁笅鏂硅緭鍏ユ�嗭細
</p>
<div class="hint-box" style="background:#1c2b1e;border-color:#2f5b39;">
<strong>鏂规�� B锛氫功绛惧皬宸ュ叿锛堜綔涓烘墜鍔ㄦ柟妗堢殑 localStorage 杈呭姪閲囬泦锛夛細</strong>
鎶婁笅闈㈣繖涓�鎸夐挳<strong>鎷栧埌娴忚�堝櫒涔︾�炬爮</strong>銆�
鍦ㄦ姈闊崇�佷俊椤电偣鍑讳袱娆★紝缁撴灉澶嶅埗鍒板壀璐存澘鍚庣矘璐村埌涓嬫柟鍗冲彲锛圵S 浼氳嚜鍔ㄥ~鍏ユ�ラ�� 鈽咃級銆�
<div class="row" style="margin-top:10px;">
<a id="bookmarklet" class="pill" style="padding:8px 16px;color:#fff;background:linear-gradient(135deg,var(--accent),var(--accent2));text-decoration:none;cursor:grab;">馃搶 鎶栭煶鍑�璇侀噰闆嗭紙鎷栨垜鍒颁功绛炬爮锛�</a>
</div>
</div>
<details style="margin-top: 16px; border: 1px dashed var(--border); border-radius: 10px; padding: 12px 14px; background: rgba(0,0,0,0.15);">
<summary style="font-size: 13.5px; font-weight: 600; cursor: pointer; color: var(--muted); outline: none; user-select: none;">
馃挕 鏂规�� C锛氫娇鐢ㄦ祻瑙堝櫒鎺у埗鍙伴噰闆嗭紙鐐瑰嚮灞曞紑 / 鏀惰捣锛�
</summary>
<p class="tip" style="margin-top:10px;">
鍒囧埌 <strong>Console锛堟帶鍒跺彴锛�</strong>锛岀矘璐翠笅闈㈣繖娈靛苟鍥炶溅锛堜細鑷�鍔ㄥ�嶅埗缁撴灉鍒板壀璐存澘锛夈��
</p>
<div class="snippet" id="snippet" style="max-height: 150px; overflow-y: auto;"></div>
<div class="row">
<button class="ghost" onclick="copyText(document.getElementById('snippet').innerText,'宸插�嶅埗閲囬泦鑴氭湰')">澶嶅埗閲囬泦鑴氭湰</button>
</div>
</details>
<p class="tip">鎶婇噰闆嗗埌鐨勫唴瀹癸紙`DYCRED1.` 瀵嗘枃鎴� JSON锛夌矘璐村埌涓嬮潰锛屽伐鍏蜂細鑷�鍔ㄨ瘑鍒�骞剁敓鎴愶細</p>
<textarea id="lsInput" placeholder='鍦ㄦ�ょ矘璐撮噰闆嗗埌鐨勫唴瀹癸紙DYCRED1.xxx 瀵嗘枃锛屾垨 {"localStorage":{...}} JSON锛�'></textarea>
<div id="wsParseHint" class="ws-missing-hint" style="display:none;">
<strong>鈿狅笍 鏈�璇嗗埆鍒� WebSocket 杩炴帴</strong>
<ul>
<li>涔︾�剧粨鏋滈噷娌℃湁 <code class="k">frontier_ws_url</code>锛岃�峰湪鎶栭煶<strong>绉佷俊椤�</strong>锛欶12 鈫� Network 鈫� 杩囨护 <code class="k">WS</code> 鈫� 鎵惧埌 <code class="k">frontier</code> / <code class="k">ws/v2</code> 鈫� 鍙抽敭 Copy URL</li>
<li>灏嗗�嶅埗鐨� <code class="k">wss://...</code> 绮樿创鍒�<strong>鏈�妗嗘湯灏炬崲琛�</strong>锛屾垨鐩存帴绮樿创鍒颁笅鏂广�屾�ラ�� 鈽呫��</li>
<li>鎴栧湪绉佷俊椤�<strong>鍏堢偣涔︾��</strong> 鈫� 鍒锋柊绉佷俊椤� 鈫� WS 杩炴帴鍚庡啀<strong>鐐逛竴娆′功绛�</strong>閲嶆柊閲囬泦</li>
</ul>
</div>
</div>
<!-- STEP 3.5 -->
<div class="card">
<div class="step-h"><span class="badge" style="background:linear-gradient(135deg,#e08c2f,#e0492f);">鈽�</span> 绉佷俊 WebSocket 杩炴帴 <span class="ws-tag">鑴氭湰鍙�鑷�鍔ㄦ姄鍙�</span></div>
<div class="hint-box" style="background:#2b231c;border-color:#5b462f;">
涓嬫柟鍐呭�圭敱<strong>姝ラ�� 4 绮樿创鍐呭�规椂鑷�鍔ㄨ瘑鍒�濉�鍏�</strong>銆�
濡傛灉鏈�璇嗗埆鍒帮紝鍙�鎵嬪姩锛欶12 鈫� Network 鈫� WS 鈫� 鎵惧埌 <code class="k">frontier</code> 鈫� Copy URL 骞剁矘璐淬��
</div>
<textarea id="wsInput" placeholder="绮樿创 wss://frontier-im.douyin.com/ws/v2?token=...&sdk_cert=...&ts_sign=... 瀹屾暣 URL" style="min-height:90px; margin-bottom:12px;"></textarea>
<p class="tip">
<strong>鎶栭煶鏁板瓧 UID锛�</strong>锛堝缓璁�濉�鍐欙紝浠ヨ�勯伩浜戠�� IP 鏌ヨ�㈤�庢帶锛�
</p>
<input type="text" id="uidInput" placeholder="渚嬪�傦細869032150442612锛堜竴閿�閲囬泦浼氳嚜鍔ㄥ~鍏咃級" style="width:100%;height:38px;background:#0c0d1a;color:var(--text);border:1px solid var(--border);border-radius:10px;padding:0 12px;outline:none;font-size:13.5px;transition:border-color .15s;" />
</div>
<!-- STEP 4 -->
<div id="step4Card" class="card">
<div class="step-h"><span class="badge">4</span> 鐢熸垚骞跺�煎叆</div>
<div class="row">
<button onclick="generate()">鐢熸垚鍑�璇� JSON</button>
<button class="ghost" onclick="copyText(document.getElementById('output').value,'宸插�嶅埗鍑�璇� JSON')">澶嶅埗缁撴灉</button>
</div>
<div class="checks" id="checks"></div>
<textarea id="output" class="out" placeholder="鐢熸垚鐨� storage_state JSON 浼氬嚭鐜板湪杩欓噷" style="margin-top:12px;"></textarea>
<div class="hint-box" style="margin-top:14px;">
<strong>瀵煎叆姝ラ�わ細</strong>鍥炲埌绯荤粺 鈫� 璐﹀彿绠$悊 鈫� 瀵瑰簲璐﹀彿鐐广�岀紪杈戙�嶁啋 鎶婁笂闈㈢殑 JSON 绮樿创杩� <strong>Cookie</strong> 妗� 鈫� 淇濆瓨 鈫�
鍐嶇偣銆屽惎鍔ㄦ墭绠°�嶃�傜郴缁熶細鐩存帴 IM 鐩磋繛锛屼笉鍐嶉渶瑕佸湪鏈嶅姟鍣ㄤ笂鎵�鐮併��
</div>
</div>
<div class="row" style="margin-top:20px;">
<button class="ghost" onclick="switchTab(3)">涓婁竴姝�</button>
</div>
</div>
</div>
<div class="toast" id="toast"></div>
<script>
function switchTab(idx) {
document.querySelectorAll('.tab-btn').forEach(btn => btn.classList.remove('active'));
document.querySelectorAll('.tab-pane').forEach(pane => pane.classList.remove('active'));
document.getElementById('tab-btn-' + idx)?.classList.add('active');
document.getElementById('tab-pane-' + idx)?.classList.add('active');
}
const LS_KEYS = [
"security-sdk/s_sdk_crypt_sdk",
"security-sdk/s_sdk_sign_data_key/web_protect",
"tea_cache_tokens",
"web_runtime_security_uid"
];
// 鍦ㄦ姈闊抽〉闈㈠畨瑁� WebSocket 閽╁瓙锛屾姄鍙� IM /ws/v2 杩炴帴 URL
const WS_HOOK_CODE = `(function(){
function __dyIsImWs(u){
u=String(u||'');
return u&&/\\/ws\\/v2/i.test(u)&&/[?&]token=/.test(u)&&(/frontier/i.test(u)||/zijieapi\\.com/i.test(u));
}
function __dySaveWs(u){
if(!__dyIsImWs(u))return;
var a=[];try{a=JSON.parse(sessionStorage.getItem('__dy_captured_ws')||'[]');}catch(e){}
if(a.indexOf(u)<0)a.unshift(u);
sessionStorage.setItem('__dy_captured_ws',JSON.stringify(a.slice(0,5)));
}
if(window.__dyWsHookInstalled)return;
window.__dyWsHookInstalled=1;
var O=window.WebSocket;
if(!O)return;
try {
var origSend = O.prototype.send;
O.prototype.send = function() {
try{__dySaveWs(this.url);}catch(e){}
return origSend.apply(this, arguments);
};
} catch(e){}
try {
var desc = Object.getOwnPropertyDescriptor(O.prototype, 'readyState');
if (desc && desc.get) {
var origGet = desc.get;
Object.defineProperty(O.prototype, 'readyState', {
get: function() {
try{__dySaveWs(this.url);}catch(e){}
return origGet.apply(this, arguments);
},
configurable: true,
enumerable: true
});
}
} catch(e){}
function W(u,p){
try{__dySaveWs(String(u||''));}catch(e){}
return arguments.length>1?new O(u,p):new O(u);
}
W.prototype=O.prototype;
Object.keys(O).forEach(function(k){try{W[k]=O[k];}catch(e){}});
['CONNECTING','OPEN','CLOSING','CLOSED'].forEach(function(k){if(k in O)W[k]=O[k];});
window.WebSocket=W;
try{
new PerformanceObserver(function(list){
list.getEntries().forEach(function(e){__dySaveWs(e.name);});
}).observe({entryTypes:['resource']});
}catch(e){}
})();`;
// 鍥炴函 performance / sessionStorage 閲屽凡寤虹珛鐨� IM WS
const WS_COLLECT_CODE = `(function(){
function __dyIsImWs(u){
u=String(u||'');
return u&&/\\/ws\\/v2/i.test(u)&&/[?&]token=/.test(u)&&(/frontier/i.test(u)||/zijieapi\\.com/i.test(u));
}
function __dyCollectWsUrls(){
var found=[];
function add(u){
u=String(u||'');
if(!__dyIsImWs(u))return;
if(found.indexOf(u)<0)found.unshift(u);
}
try{(performance.getEntries()||[]).forEach(function(e){add(e.name);});}catch(e){}
try{
var stored=JSON.parse(sessionStorage.getItem('__dy_captured_ws')||'[]');
if(Array.isArray(stored))stored.forEach(add);
}catch(e){}
try{sessionStorage.setItem('__dy_captured_ws',JSON.stringify(found.slice(0,5)));}catch(e){}
return found;
}
window.__dyCollectWsUrls=__dyCollectWsUrls;
window.__dyIsImWs=__dyIsImWs;
return __dyCollectWsUrls();
})();`;
function showWsMissingHint(show) {
const el = document.getElementById('wsParseHint');
if (el) el.style.display = show ? 'block' : 'none';
}
function looksLikeBookmarkDump(raw) {
const text = (raw || '').trim();
if (text.length < 80) return false;
return text.includes('"localStorage"') || text.includes("'localStorage'") || (text.startsWith('{') && text.includes('cookie'));
}
function applyDumpToForm(d, raw, forceOverwrite) {
const ws = resolveWsFromDump(d, raw);
const wsEl = document.getElementById('wsInput');
const uidEl = document.getElementById('uidInput');
const cookieEl = document.getElementById('cookieInput');
const existingWs = (wsEl.value || '').trim();
if (d && d.cookie) {
if (forceOverwrite || !cookieEl.value.trim()) {
cookieEl.value = d.cookie;
}
}
if (d && d.my_uid) {
if (forceOverwrite || !uidEl.value.trim()) {
uidEl.value = d.my_uid;
}
}
if (ws) {
showWsMissingHint(false);
const uidFromWs = uidFromWsUrl(ws);
if (uidFromWs) {
if (forceOverwrite || !uidEl.value.trim()) {
uidEl.value = uidFromWs;
}
}
if (forceOverwrite || wsEl.value !== ws) {
const isChanged = wsEl.value !== ws;
wsEl.value = ws;
if (isChanged) {
wsEl.classList.add('auto-filled');
setTimeout(() => wsEl.classList.remove('auto-filled'), 1800);
toast('宸茶嚜鍔ㄨ瘑鍒� WebSocket 骞跺~鍏ユ�ラ�� 鈽�');
}
}
return true;
}
if (existingWs && isImWsUrl(existingWs)) {
showWsMissingHint(false);
return true;
}
if (looksLikeBookmarkDump(raw)) {
showWsMissingHint(true);
toastWarn('鏈�璇嗗埆鍒� WebSocket锛氳剼鏈�缁撴灉涓嶅惈 WS 杩炴帴锛岃�锋煡鐪嬫�ラ�� 4 涓嬫柟鎻愮ず');
} else {
showWsMissingHint(false);
}
return false;
}
function syncDumpFieldsFromLsInput() {
const raw = document.getElementById('lsInput').value;
applyDumpToForm(normalizeDump(raw), raw, true);
generate();
}
// 鎺у埗鍙伴噰闆嗚剼鏈�锛氬叏閲� dump localStorage + 瀹夎�� WS 鐩戝惉 + 寮圭獥杈呭姪澶嶅埗
const SNIPPET_CODE = `(function(){
${WS_HOOK_CODE}
${WS_COLLECT_CODE}
var o = { localStorage: Object.assign({}, localStorage), cookie: document.cookie, my_uid: '' };
try{
var wsArr=JSON.parse(sessionStorage.getItem('__dy_captured_ws')||'[]');
if(wsArr&&wsArr.length){ o.ws_urls=wsArr; o.frontier_ws_url=wsArr[0]; }
}catch(e){}
var wsTip = o.frontier_ws_url ? ('\\n宸叉姄鍙� WebSocket: ' + o.frontier_ws_url.slice(0,72) + '...') : '\\n鏈�鎶撳埌 WS锛氳�峰埛鏂扮�佷俊椤靛悗閲嶆柊杩愯�岋紱鎴栨妸 Network 閲屽�嶅埗鐨� wss URL 杩藉姞绮樿创鍒伴噰闆嗗伐鍏锋�ラ�� 3';
var s = JSON.stringify(o);
try { copy(s); console.log("宸插�嶅埗鍒板壀璐存澘" + wsTip); } catch(e){ console.log(s); }
var div = document.createElement('div');
div.style = "position:fixed;top:15%;left:50%;transform:translateX(-50%);z-index:999999;background:#1a1b2e;color:#e6e6f0;padding:20px;border:2px solid #8b5cf6;border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,0.8);width:90%;max-width:550px;font-family:sans-serif;text-align:left;line-height:1.5;";
div.innerHTML = "<h3 style='margin:0 0 8px;color:#a855f7;font-size:16px;'>馃搶 鎺у埗鍙板嚟璇侀噰闆嗘垚鍔�</h3>" +
"<p style='margin:0 0 12px;font-size:13px;color:#9aa0c0;'>宸插畨瑁� WS 鐩戝惉骞跺皾璇曡嚜鍔ㄥ�嶅埗銆�" + wsTip.replace(/\\n/g,'<br/>') + "</p>" +
"<textarea id='dy_copy_area' style='width:100%;height:140px;background:#0c0d1a;color:#cbd5e1;border:1px solid #2c2e4a;border-radius:8px;padding:8px;font-family:monospace;font-size:12px;outline:none;resize:none;' readonly>" + s + "</textarea>" +
"<div style='margin-top:14px;display:flex;justify-content:flex-end;gap:10px;'>" +
"<button id='dy_btn_copy' style='background:#8b5cf6;color:#fff;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font-weight:600;font-size:13px;'>澶嶅埗鏂囨湰</button>" +
"<button id='dy_btn_close' style='background:transparent;color:#9aa0c0;border:1px solid #2c2e4a;border-radius:8px;padding:8px 16px;cursor:pointer;font-size:13px;'>鍏抽棴</button>" +
"</div>";
document.body.appendChild(div);
var txt = document.getElementById('dy_copy_area');
document.getElementById('dy_btn_copy').onclick = function(){
txt.select();
document.execCommand('copy');
this.innerText = '宸插�嶅埗锛�';
setTimeout(function(){ document.getElementById('dy_btn_copy').innerText = '澶嶅埗鏂囨湰'; }, 1000);
};
document.getElementById('dy_btn_close').onclick = function(){
div.remove();
};
})()`;
const SNIPPET = SNIPPET_CODE;
document.getElementById('snippet').innerText = SNIPPET;
// 涔︾�惧皬宸ュ叿锛氬叏閲� dump localStorage + cookie + WS 鐩戝惉 + 寮傛�� fetch UID + 寤惰繜杞�璇� + 寮圭獥杈呭姪澶嶅埗
const BOOKMARKLET_CODE = `(function(){
${WS_HOOK_CODE}
${WS_COLLECT_CODE}
var o = { localStorage: Object.assign({}, localStorage), cookie: document.cookie, my_uid: '' };
try{
var wsArr=JSON.parse(sessionStorage.getItem('__dy_captured_ws')||'[]');
if(wsArr&&wsArr.length){ o.ws_urls=wsArr; o.frontier_ws_url=wsArr[0]; }
}catch(e){}
function refreshWs(){
try{
if(window.__dyCollectWsUrls)window.__dyCollectWsUrls();
var wsArr=JSON.parse(sessionStorage.getItem('__dy_captured_ws')||'[]');
if(wsArr&&wsArr.length){ o.ws_urls=wsArr; o.frontier_ws_url=wsArr[0]; }
else { delete o.ws_urls; o.frontier_ws_url=''; }
}catch(e){}
}
function done(){
refreshWs();
var s = JSON.stringify(o);
var tip = o.my_uid ? ('\\n(已自动提取到抖音数字 UID: ' + o.my_uid + ')') : '\\n(未获取到 UID,可在采集工具中手动输入)';
var wsTip = o.frontier_ws_url ? ('\\n(已抓取 WebSocket 私信连接)') : '\\n(未抓到 WS:Network→WS→Copy URL,粘贴到采集工具步骤3末尾或步骤★)';
tip += wsTip;
try {
navigator.clipboard.writeText(s);
} catch(e){}
var div = document.createElement('div');
div.style = "position:fixed;top:15%;left:50%;transform:translateX(-50%);z-index:999999;background:#1a1b2e;color:#e6e6f0;padding:20px;border:2px solid #8b5cf6;border-radius:14px;box-shadow:0 10px 30px rgba(0,0,0,0.8);width:90%;max-width:550px;font-family:sans-serif;text-align:left;line-height:1.5;";
div.innerHTML = "<h3 style='margin:0 0 8px;color:#a855f7;font-size:16px;'>📌 控制台凭证采集成功</h3>" +
"<p style='margin:0 0 12px;font-size:13px;color:#9aa0c0;'>已尝试自动复制" + tip.replace(/\\n/g,'<br/>') + "。若粘贴失败,请点击下方按钮复制或手动复制框内文本:</p>" +
"<textarea id='dy_copy_area' style='width:100%;height:140px;background:#0c0d1a;color:#cbd5e1;border:1px solid #2c2e4a;border-radius:8px;padding:8px;font-family:monospace;font-size:12px;outline:none;resize:none;' readonly>" + s + "</textarea>" +
"<div style='margin-top:14px;display:flex;justify-content:flex-end;gap:10px;'>" +
"<button id='dy_btn_copy' style='background:#8b5cf6;color:#fff;border:none;border-radius:8px;padding:8px 16px;cursor:pointer;font-weight:600;font-size:13px;'>复制文本</button>" +
"<button id='dy_btn_close' style='background:transparent;color:#9aa0c0;border:1px solid #2c2e4a;border-radius:8px;padding:8px 16px;cursor:pointer;font-size:13px;'>关闭</button>" +
"</div>";
document.body.appendChild(div);
var txt = document.getElementById('dy_copy_area');
document.getElementById('dy_btn_copy').onclick = function(){
txt.select();
document.execCommand('copy');
this.innerText = '已复制!';
setTimeout(function(){ document.getElementById('dy_btn_copy').innerText = '复制文本'; }, 1000);
};
document.getElementById('dy_btn_close').onclick = function(){
div.remove();
};
}
var fetchedUid = false;
var elapsed = 0;
var maxWait = 3500;
var interval = 200;
function tryFinish() {
refreshWs();
if ((o.frontier_ws_url && fetchedUid) || elapsed >= maxWait) {
done();
} else {
elapsed += interval;
setTimeout(tryFinish, interval);
}
}
fetch('/aweme/v1/web/query/user/')
.then(function(r){ return r.json(); })
.then(function(data){
if(data && data.user_uid){ o.my_uid = String(data.user_uid); }
fetchedUid = true;
})
.catch(function(){
fetch('/aweme/v1/creator/user/info/')
.then(function(r){ return r.json(); })
.then(function(data){
if(data && data.creator_user_id){ o.my_uid = String(data.creator_user_id); }
fetchedUid = true;
})
.catch(function(){ fetchedUid = true; });
});
setTimeout(tryFinish, 0);
})();`;
const BOOKMARKLET = "javascript:" + encodeURIComponent(BOOKMARKLET_CODE.replace(/\s+/g, ' '));
document.getElementById('bookmarklet').setAttribute('href', BOOKMARKLET);
const onst MANIFEST_JSON = `{
"manifest_version": 3,
"name": "鎶栭煶鍑�璇佸姪鎵�",
"version": "1.0",
"description": "鐙�绔嬭幏鍙栨姈闊冲厤鎵�鐮佸嚟璇侊紙鍚� HttpOnly sessionid銆亀eb_protect銆乲eys 鍜� WebSocket 杩炴帴锛�",
"permissions": [
"cookies"
],
"host_permissions": [
"https://*.douyin.com/*"
],
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["https://*.douyin.com/*"],
"js": ["content.js"],
"run_at": "document_start"
}
]
}`;
const BACKGROUND_JS = [
'chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {',
' if (message.action === "get_cookies") {',
' chrome.cookies.getAll({ url: sender.tab.url }, (cookies) => {',
' if (chrome.runtime.lastError) {',
' sendResponse({ error: chrome.runtime.lastError.message });',
' } else {',
' const cookieStr = cookies.map(c => c.name + "=" + c.value).join("; ");',
' sendResponse({ cookie: cookieStr });',
' }',
' });',
' return true;',
' }',
'});'
].join('\\n');
const CONTENT_JS = [
'(function() {',
' \\'use strict\\';',
'',
' // 1. WebSocket Hook 閫昏緫锛堟敞鍏ュ埌 main world锛�',
' const WS_HOOK_CODE = `(function(){',
' function __dyIsImWs(u){',
' u=String(u||"");',
' return u&&/\\\\\\\\/ws\\\\\\\\/v2/i.test(u)&&/[?&]token=/.test(u)&&(/frontier/i.test(u)||/zijieapi\\\\\\\\.com/i.test(u));',
' }',
' function __dySaveWs(u){',
' if(!__dyIsImWs(u))return;',
' var a=[];try{a=JSON.parse(sessionStorage.getItem("__dy_captured_ws")||"[]");}catch(e){}',
' if(a.indexOf(u)<0)a.unshift(u);',
' sessionStorage.setItem("__dy_captured_ws",JSON.stringify(a.slice(0,5)));',
' }',
' if(window.__dyWsHookInstalled)return;',
' window.__dyWsHookInstalled=1;',
' var O=window.WebSocket;',
' if(!O)return;',
' ',
' try {',
' var origSend = O.prototype.send;',
' O.prototype.send = function() {',
' try{__dySaveWs(this.url);}catch(e){}',
' return origSend.apply(this, arguments);',
' };',
' } catch(e){}',
' ',
' try {',
' var desc = Object.getOwnPropertyDescriptor(O.prototype, "readyState");',
' if (desc && desc.get) {',
' var origGet = desc.get;',
' Object.defineProperty(O.prototype, "readyState", {',
' get: function() {',
' try{__dySaveWs(this.url);}catch(e){}',
' return origGet.apply(this, arguments);',
' },',
' configurable: true,',
' enumerable: true',
' });',
' }',
' } catch(e){}',
'',
' function W(u,p){',
' try{__dySaveWs(String(u||""));}catch(e){}',
' return arguments.length>1?new O(u,p):new O(u);',
' }',
' W.prototype=O.prototype;',
' Object.keys(O).forEach(function(k){try{W[k]=O[k];}catch(e){}});',
' ["CONNECTING","OPEN","CLOSING","CLOSED"].forEach(function(k){if(k in O)W[k]=O[k];});',
' window.WebSocket=W;',
' try{',
' new PerformanceObserver(function(list){',
' list.getEntries().forEach(function(e){__dySaveWs(e.name);});',
' }).observe({entryTypes:["resource"]});',
' }catch(e){}',
' })();`;',
'',
' const script = document.createElement("script");',
' script.textContent = WS_HOOK_CODE;',
' (document.head || document.documentElement).appendChild(script);',
' script.remove();',
'',
' // 2. 椤甸潰鍔犺浇瀹屾垚鍚庢坊鍔犳偓娴�鎸夐挳',
' if (document.readyState === "loading") {',
' window.addEventListener("load", initButton);',
' } else {',
' initButton();',
' }',
'',
' function initButton() {',
' if (document.getElementById("dy-credential-grabber-btn")) return;',
' var btn = document.createElement("div");',
' btn.id = "dy-credential-grabber-btn";',
' btn.innerHTML = "馃搶 涓�閿�閲囬泦鍑�璇�";',
' btn.style = "position:fixed;bottom:80px;right:24px;z-index:999999;background:linear-gradient(135deg,#8b5cf6,#a855f7);color:#fff;padding:12px 18px;border-radius:10px;font-weight:600;font-size:14px;cursor:pointer;box-shadow:0 4px 15px rgba(139,92,246,0.4);transition:all 0.2s;user-select:none;font-family:sans-serif;";',
' ',
' btn.addEventListener("mouseenter", function() {',
' btn.style.transform = "scale(1.05)";',
' btn.style.boxShadow = "0 6px 20px rgba(139,92,246,0.6)";',
' });',
' btn.addEventListener("mouseleave", function() {',
' btn.style.transform = "scale(1)";',
' btn.style.boxShadow = "0 4px 15px rgba(139,92,246,0.4)";',
' });',
'',
' btn.addEventListener("click", grabCredentials);',
' document.body.appendChild(btn);',
' }',
'',
' // 3. 閲囬泦涓婚�昏緫',
' function grabCredentials() {',
' var btn = document.getElementById("dy-credential-grabber-btn");',
' if (btn) {',
' btn.innerHTML = "鈴� 姝e湪閲囬泦...";',
' btn.style.background = "#4b5563";',
' }',
'',
' var ls = {};',
' for (var i = 0; i < localStorage.length; i++) {',
' var k = localStorage.key(i);',
' ls[k] = localStorage.getItem(k);',
' }',
'',
' var keys = "";',
' var web_protect = "";',
' var sec_uid = "";',
' var uid = "";',
'',
' for (var key in ls) {',
' var lk = key.toLowerCase();',
' if (lk.indexOf("crypt_sdk") >= 0) keys = ls[key];',
' if (lk.indexOf("web_protect") >= 0) web_protect = ls[key];',
' if (lk.indexOf("security_uid") >= 0 || lk.indexOf("runtime_security_uid") >= 0) sec_uid = ls[key];',
' if (lk.indexOf("tea_cache_tokens") >= 0) {',
' try {',
' var parsed = JSON.parse(ls[key]);',
' uid = String(parsed.user_unique_id || parsed.web_id || "");',
' } catch(e) {}',
' }',
' }',
' if (!sec_uid && uid) sec_uid = uid;',
'',
' var capturedWs = [];',
' try {',
' capturedWs = JSON.parse(sessionStorage.getItem("__dy_captured_ws") || "[]");',
' } catch(e) {}',
' var wsUrl = capturedWs[0] || "";',
'',
' var my_uid = uid;',
' var fetchPromises = [];',
'',
' if (!my_uid) {',
' fetchPromises.push(',
' fetch("/aweme/v1/web/query/user/")',
' .then(function(r) { return r.json(); })',
' .then(function(d) { if(d && d.user_uid) my_uid = String(d.user_uid); })',
' .catch(function() {})',
' );',
' fetchPromises.push(',
' fetch("/aweme/v1/creator/user/info/")',
' .then(function(r) { return r.json(); })',
' .then(function(d) { if(d && d.creator_user_id) my_uid = String(d.creator_user_id); })',
' .catch(function() {})',
' );',
' }',
'',
' Promise.all(fetchPromises).then(function() {',
' chrome.runtime.sendMessage({ action: "get_cookies" }, function(response) {',
' if (!response || response.error) {',
' showToast("鉂� 鑾峰彇 Cookie 澶辫触: " + ((response && response.error) || "鏈�鍝嶅簲"), "err");',
' resetBtn();',
' return;',
' }',
'',
' var cookieStr = response.cookie;',
'',
' var data = {',
' cookie: cookieStr,',
' web_protect: web_protect,',
' keys: keys,',
' ua: navigator.userAgent,',
' sec_uid: sec_uid,',
' nickname: "",',
' unique_id: my_uid || "",',
' frontier_ws_url: wsUrl,',
' ws_urls: capturedWs',
' };',
'',
' var payloadStr = JSON.stringify(data);',
' var base64Payload = btoa(unescape(encodeURIComponent(payloadStr)));',
' var finalResult = "DYCRED1." + base64Payload;',
'',
' try {',
' navigator.clipboard.writeText(finalResult).then(function() {',
' showToast("馃帀 鍑�璇侀噰闆嗘垚鍔燂紒宸茶嚜鍔ㄥ�嶅埗鍒板壀璐存澘銆�", "ok");',
' }).catch(function() {',
' fallbackCopy(finalResult);',
' });',
' } catch(e) {',
' fallbackCopy(finalResult);',
' }',
' resetBtn();',
' });',
' });',
' }',
'',
' function fallbackCopy(text) {',
' var ta = document.createElement("textarea");',
' ta.value = text;',
' document.body.appendChild(ta);',
' ta.select();',
' document.execCommand("copy");',
' ta.remove();',
' showToast("馃帀 鍑�璇侀噰闆嗘垚鍔燂紒宸叉墜鍔ㄥ�嶅埗鍒板壀璐存澘銆�", "ok");',
' }',
'',
' function resetBtn() {',
' var btn = document.getElementById("dy-credential-grabber-btn");',
' if (btn) {',
' btn.innerHTML = "馃搶 涓�閿�閲囬泦鍑�璇�";',
' btn.style.background = "linear-gradient(135deg,#8b5cf6,#a855f7)";',
' }',
' }',
'',
' function showToast(msg, type) {',
' var toast = document.createElement("div");',
' toast.style = "position:fixed;top:10%;left:50%;transform:translateX(-50%);z-index:9999999;padding:14px 24px;border-radius:12px;color:#fff;font-weight:600;font-size:14px;box-shadow:0 8px 30px rgba(0,0,0,0.5);font-family:sans-serif;line-height:1.4;";',
' if (type === "ok") {',
' toast.style.background = "#059669";',
' toast.style.border = "1px solid #34d399";',
' } else {',
' toast.style.background = "#dc2626";',
' toast.style.border = "1px solid #f87171";',
' }',
' toast.innerHTML = msg;',
' document.body.appendChild(toast);',
' setTimeout(function() {',
' toast.style.opacity = "0";',
' toast.style.transition = "opacity 0.4s";',
' setTimeout(function() { toast.remove(); }, 400);',
' }, 3000);',
' }',
'})();'
].join('\\n');
function downloadPluginFile(filename) {
let content = "";
if (filename === 'manifest.json') content = MANIFEST_JSON;
else if (filename === 'background.js') content = BACKGROUND_JS;
else if (filename === 'content.js') content = CONTENT_JS;
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
toast('宸蹭笅杞� ' + filename + '锛岃�蜂繚瀛樺埌浣犵殑鎻掍欢鐩�褰�');
}me/v1/creator/user/info/')
.then(function(r) { return r.json(); })
.then(function(d) { if(d && d.creator_user_id) my_uid = String(d.creator_user_id); })
.catch(function() {})
);
}
Promise.all(fetchPromises).then(function() {
if (typeof GM_cookie === 'undefined' || !GM_cookie.list) {
showToast('鉂� 娌圭尨鐜�澧冩湭鎺堜簣 GM_cookie 鏉冮檺锛岃�峰湪鑴氭湰涓�纭�璁� @grant GM_cookie', 'err');
resetBtn();
return;
}
GM_cookie.list({ url: location.href }, function(cookies, error) {
if (error) {
showToast('鉂� 鑾峰彇 Cookie 澶辫触: ' + error, 'err');
resetBtn();
return;
}
var cookieStr = cookies.map(function(c) {
return c.name + '=' + c.value;
}).join('; ');
var data = {
cookie: cookieStr,
web_protect: web_protect,
keys: keys,
ua: navigator.userAgent,
sec_uid: sec_uid,
nickname: '',
unique_id: my_uid || '',
frontier_ws_url: wsUrl,
ws_urls: capturedWs
};
var payloadStr = JSON.stringify(data);
var base64Payload = btoa(unescape(encodeURIComponent(payloadStr)));
var finalResult = 'DYCRED1.' + base64Payload;
try {
GM_setClipboard(finalResult);
showToast('馃帀 鍑�璇侀噰闆嗘垚鍔燂紒宸茶嚜鍔ㄥ�嶅埗鍒板壀璐存澘銆�', 'ok');
} catch(e) {
var ta = document.createElement('textarea');
ta.value = finalResult;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
ta.remove();
showToast('馃帀 鍑�璇侀噰闆嗘垚鍔燂紒宸叉墜鍔ㄥ�嶅埗鍒板壀璐存澘銆�', 'ok');
}
resetBtn();
});
});
}
function resetBtn() {
var btn = document.getElementById('dy-credential-grabber-btn');
if (btn) {
btn.innerHTML = '馃搶 涓�閿�閲囬泦鍑�璇�';
btn.style.background = 'linear-gradient(135deg,#8b5cf6,#a855f7)';
}
}
function showToast(msg, type) {
var toast = document.createElement('div');
toast.style = 'position:fixed;top:10%;left:50%;transform:translateX(-50%);z-index:9999999;padding:14px 24px;border-radius:12px;color:#fff;font-weight:600;font-size:14px;box-shadow:0 8px 30px rgba(0,0,0,0.5);font-family:sans-serif;line-height:1.4;';
if (type === 'ok') {
toast.style.background = '#059669';
toast.style.border = '1px solid #34d399';
} else {
toast.style.background = '#dc2626';
toast.style.border = '1px solid #f87171';
}
toast.innerHTML = msg;
document.body.appendChild(toast);
setTimeout(function() {
toast.style.opacity = '0';
toast.style.transition = 'opacity 0.4s';
setTimeout(function() { toast.remove(); }, 400);
}, 3000);
}
})();\`;
function toast(msg) {
const t = document.getElementById('toast');
t.classList.remove('warn');
t.innerText = msg; t.classList.add('show');
clearTimeout(t._t); t._t = setTimeout(() => t.classList.remove('show'), 1600);
}
function toastWarn(msg) {
const t = document.getElementById('toast');
t.innerText = msg;
t.classList.add('show', 'warn');
clearTimeout(t._t);
t._t = setTimeout(() => t.classList.remove('show', 'warn'), 5000);
}
function copyText(text, msg) {
if (!text) { toast('娌℃湁鍙�澶嶅埗鐨勫唴瀹�'); return; }
navigator.clipboard.writeText(text).then(() => toast(msg || '宸插�嶅埗'))
.catch(() => {
const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta);
ta.select(); document.execCommand('copy'); ta.remove(); toast(msg || '宸插�嶅埗');
});
}
// 鎶婇噰闆嗗埌鐨勫師濮� JSON 瑙勬暣涓� {ls:{...}, cookie:"", my_uid:""}
// 鍏煎�逛笁绉嶆牸寮忥細鍏ㄩ噺 dump {localStorage,cookie,my_uid} / 鏃х増鍥涢敭鎵佸钩瀵硅薄 / 鐩存帴鐨� localStorage 瀵硅薄 / DYCRED1.xxx 瀵嗘枃
function normalizeDump(raw) {
let cleanRaw = (raw || '').trim();
if (cleanRaw.startsWith('DYCRED1.')) {
try {
const b64 = cleanRaw.split('.', 2)[1];
const decoded = decodeURIComponent(escape(atob(b64)));
const data = JSON.parse(decoded);
const ls = {};
if (data.keys) ls["security-sdk/s_sdk_crypt_sdk"] = data.keys;
if (data.web_protect) ls["security-sdk/s_sdk_sign_data_key/web_protect"] = data.web_protect;
if (data.sec_uid) ls["web_runtime_security_uid"] = data.sec_uid;
if (data.unique_id) {
ls["tea_cache_tokens"] = JSON.stringify({
user_unique_id: data.unique_id,
web_id: data.unique_id
});
}
return {
ls: ls,
cookie: data.cookie || '',
my_uid: data.unique_id || data.my_uid || '',
frontier_ws_url: data.frontier_ws_url || (data.ws_urls && data.ws_urls[0]) || '',
ws_urls: Array.isArray(data.ws_urls) ? data.ws_urls : []
};
} catch (e) {
console.error("Failed to parse DYCRED1 format:", e);
}
}
const obj = parseLocalStorage(cleanRaw);
if (obj === null) return null;
if (obj.localStorage && typeof obj.localStorage === 'object') {
return {
ls: obj.localStorage,
cookie: obj.cookie || '',
my_uid: obj.my_uid || '',
frontier_ws_url: obj.frontier_ws_url || '',
ws_urls: Array.isArray(obj.ws_urls) ? obj.ws_urls : []
};
}
return {
ls: obj,
cookie: obj.cookie || '',
my_uid: obj.my_uid || '',
frontier_ws_url: obj.frontier_ws_url || '',
ws_urls: Array.isArray(obj.ws_urls) ? obj.ws_urls : []
};
}
// 浠� localStorage 閲屾櫤鑳芥彁鍙栨煇涓�瀛楁�碉細鍏堢簿纭�閿�鍚嶏紝鍐嶆ā绯婂寘鍚�鍖归厤
function pickLs(ls, exactKey, containsList) {
if (ls[exactKey]) return ls[exactKey];
for (const k in ls) {
const lk = k.toLowerCase();
if (containsList.some(c => lk.includes(c)) && ls[k]) return ls[k];
}
return '';
}
// 浠庝换鎰� localStorage 鍊奸噷灏藉姏鎸栧嚭 web_id锛坲ser_unique_id锛�
function extractWebId(ls) {
const tea = pickLs(ls, 'tea_cache_tokens', ['tea_cache_tokens']);
if (tea) {
try {
const p = JSON.parse(tea);
const id = p.user_unique_id || p.web_id || '';
if (id) return String(id);
} catch (e) {}
}
// 鍏滃簳锛氭壂鎻忔墍鏈夊�硷紝姝e垯鎵� user_unique_id
for (const k in ls) {
const v = ls[k];
if (typeof v !== 'string') continue;
const m = v.match(/"user_unique_id"\s*:\s*"?(\d{6,})"?/);
if (m && m[1]) return m[1];
}
return '';
}
// 浠庣矘璐村唴瀹归噷鎶藉嚭 wss://frontier-im.douyin.com/ws/v2?... 杩欐潯 URL
// 鍏煎�圭洿鎺ョ矘 URL锛屾垨绮樹簡 fetch("...", {...}) 杩欑�� devtools 澶嶅埗缁撴灉
function isImWsUrl(u) {
u = String(u || '');
if (!u || !/\/ws\/v2/i.test(u) || !/[?&]token=/.test(u)) return false;
return /frontier/i.test(u) || /zijieapi\.com/i.test(u);
}
function extractWsUrl(raw) {
if (!raw) return '';
const text = String(raw).replace(/\\\//g, '/');
const re = /wss?:\/\/[^\s"'`,)\]}]+/gi;
let m;
while ((m = re.exec(text)) !== null) {
const url = m[0];
if (isImWsUrl(url)) return url;
}
return '';
}
function uidFromWsUrl(ws) {
if (!ws) return '';
const m = String(ws).match(/[?&]device_id=(\d{6,})/);
return m ? m[1] : '';
}
function resolveWsFromDump(d, raw) {
const candidates = [];
if (d) {
if (d.frontier_ws_url && String(d.frontier_ws_url).trim()) {
candidates.push(String(d.frontier_ws_url).trim());
}
if (Array.isArray(d.ws_urls)) {
d.ws_urls.forEach((u) => { if (u && String(u).trim()) candidates.push(String(u).trim()); });
}
}
if (raw) {
candidates.push(String(raw));
candidates.push(String(raw).replace(/\\\//g, '/'));
}
for (const item of candidates) {
const url = extractWsUrl(item) || (isImWsUrl(item) ? item : '');
if (url) return url;
}
return '';
}
// 浠� URL 鍙栨煇涓� query 鍙傛暟骞惰В鐮侊紙decodeURIComponent 涓嶄細鎶� + 鍙樼┖鏍硷紝base64 瀹夊叏锛�
function qparam(url, key) {
const m = url.match(new RegExp('[?&]' + key.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '=([^&\\s]+)'));
if (!m) return '';
try { return decodeURIComponent(m[1]); } catch (e) { return m[1]; }
}
// 瑙f瀽 web_protect JSON锛堝吋瀹� {"data":"{...}"} 鍙屽眰缁撴瀯锛�
function parseWebProtect(raw) {
if (!raw) return null;
try {
let o = JSON.parse(String(raw));
if (typeof o === 'string') o = JSON.parse(o);
if (o && typeof o.data === 'string') {
try { o = JSON.parse(o.data); } catch (e) {}
} else if (o && typeof o.data === 'object' && o.data) {
o = o.data;
}
return (o && typeof o === 'object') ? o : null;
} catch (e) {
return null;
}
}
// 鐩戝惉鍚勪釜杈撳叆妗嗙殑鍙樺姩锛岀洿鎺ヨ嚜鍔ㄧ敓鎴愬苟鏍¢獙绗�鍥涙��
const cookieInputEl = document.getElementById('cookieInput');
cookieInputEl.addEventListener('input', () => generate(false));
cookieInputEl.addEventListener('change', () => generate(false));
cookieInputEl.addEventListener('paste', () => setTimeout(() => generate(true), 0));
const lsInputEl = document.getElementById('lsInput');
lsInputEl.addEventListener('input', () => {
const raw = lsInputEl.value;
applyDumpToForm(normalizeDump(raw), raw, false);
generate(false);
});
lsInputEl.addEventListener('change', () => {
const raw = lsInputEl.value;
applyDumpToForm(normalizeDump(raw), raw, false);
generate(false);
});
lsInputEl.addEventListener('paste', () => setTimeout(() => {
const raw = lsInputEl.value;
applyDumpToForm(normalizeDump(raw), raw, true);
generate(true);
}, 0));
const wsInputEl = document.getElementById('wsInput');
const handleWsInput = (isPaste) => {
const ws = extractWsUrl(wsInputEl.value);
if (ws) showWsMissingHint(false);
if (!ws) {
generate(isPaste);
return;
}
const uid = uidFromWsUrl(ws);
if (uid && !document.getElementById('uidInput').value.trim()) {
document.getElementById('uidInput').value = uid;
}
generate(isPaste);
};
wsInputEl.addEventListener('input', () => handleWsInput(false));
wsInputEl.addEventListener('change', () => handleWsInput(false));
wsInputEl.addEventListener('paste', () => setTimeout(() => handleWsInput(true), 0));
const uidInputEl = document.getElementById('uidInput');
uidInputEl.addEventListener('input', () => generate(false));
uidInputEl.addEventListener('change', () => generate(false));
function extractCookieFromRaw(raw) {
raw = (raw || "").trim();
// 1. 灏濊瘯鍖归厤 JSON 缁撴瀯锛歿"cookie": "..."} 鎴� 'cookie': '...'
const m1 = raw.match(/['"]cookie['"]\s*:\s*['"]([^'"]+)['"]/i);
if (m1 && m1[1]) {
return m1[1];
}
// 2. 灏濊瘯鍖归厤 cURL / Header 琛岋細-H 'cookie: ...' 鎴� cookie: '...'
const m2 = raw.match(/cookie:\s*['"]?([^'\r\n"]+)['"]?/i);
if (m2 && m2[1]) {
return m2[1].trim();
}
// 3. 鍏滃簳鎻愬彇锛氬尮閰嶄互 cookie: 寮�澶寸殑浠绘剰琛岋紝骞跺墧闄ゅ�栧眰寮曞彿/杞�涔夌��
const m3 = raw.match(/cookie:\s*([^\r\n]+)/i);
if (m3 && m3[1]) {
let val = m3[1].trim();
val = val.replace(/^['"]|['"]$/g, ''); // 鍓ョ�绘渶澶栧眰鐨勫崟寮曞彿鎴栧弻寮曞彿
return val;
}
return raw;
}
// 鎶� "a=b; c=d" 瑙f瀽涓� cookie 瀵硅薄鏁扮粍
function parseCookieHeader(raw) {
const out = [];
const farFuture = Math.floor(Date.now() / 1000) + 60 * 60 * 24 * 180; // 180 澶�
raw.split(/;\s*/).forEach(pair => {
if (!pair) return;
const idx = pair.indexOf('=');
if (idx <= 0) return;
const name = pair.slice(0, idx).trim();
let value = pair.slice(idx + 1).trim();
// 杩囨护鎺夊洜涓虹矘璐翠簡 fetch 璇�鍙ョ瓑瀵艰嚧瑙f瀽鍑虹殑闈炴硶鐨� Cookie 鍚�
if (!name || /[\(\)\[\]\{\}'"\n \t\\]/.test(name)) return;
out.push({
name,
value,
domain: ".douyin.com",
path: "/",
expires: farFuture,
httpOnly: false,
secure: true,
sameSite: "None"
});
});
return out;
}
function parseLocalStorage(raw) {
raw = (raw || '').trim();
if (!raw) return {};
try { return JSON.parse(raw); } catch (e) {}
const ws = extractWsUrl(raw);
if (ws) {
const idx = raw.indexOf(ws);
if (idx > 0) {
const head = raw.slice(0, idx).trim();
try { return JSON.parse(head); } catch (e2) {}
const s = head.indexOf('{'), e = head.lastIndexOf('}');
if (s >= 0 && e > s) {
try { return JSON.parse(head.slice(s, e + 1)); } catch (e3) {}
}
}
}
const s = raw.indexOf('{'), e = raw.lastIndexOf('}');
if (s >= 0 && e > s) {
try { return JSON.parse(raw.slice(s, e + 1)); } catch (e2) {}
}
return extractWsUrl(raw) ? {} : null;
}
function generate(autoFocus) {
const cookieRaw = document.getElementById('cookieInput').value.trim();
const lsRaw = document.getElementById('lsInput').value.trim();
const myUidVal = document.getElementById('uidInput').value.trim();
const checks = [];
if (!cookieRaw) {
renderChecks([{ s:'err', t:'璇峰厛绮樿创瀹屾暣 Cookie锛堟�ラ�� 2锛�' }]);
if (autoFocus) {
switchTab(2);
toastWarn('璇峰厛瀹屾垚姝ラ�� 2锛氫粠鎺у埗鍙板�嶅埗骞剁矘璐村畬鏁� Cookie');
}
return;
}
// 鎻愬彇鐪熸�g殑 Cookie 鍊煎苟鍘绘帀 Cookie: 鍓嶇紑
const cleanCookie = extractCookieFromRaw(cookieRaw).replace(/^cookie\s*:/i, '').trim();
const cookies = parseCookieHeader(cleanCookie);
const names = new Set(cookies.map(c => c.name.toLowerCase()));
const dump = normalizeDump(lsRaw);
if (dump === null) {
renderChecks([{ s:'err', t:'姝ラ�� 3 鐨� JSON 瑙f瀽澶辫触锛岃�烽噸鏂板�嶅埗閲囬泦缁撴灉' }]);
return;
}
const ls = dump.ls || {};
// 鑷�鍔ㄥ弽濉� UID / WS
applyDumpToForm(dump, lsRaw, false);
// 鏅鸿兘鎻愬彇鍥涢」鍏抽敭鍑�璇侊紙绮剧‘閿�鍚� + 妯$硦鍖归厤鍏滃簳锛�
const valKeys = pickLs(ls, 'security-sdk/s_sdk_crypt_sdk', ['crypt_sdk']);
const valProtect = pickLs(ls, 'security-sdk/s_sdk_sign_data_key/web_protect', ['web_protect']);
const webId = extractWebId(ls);
const deviceId = pickLs(ls, 'web_runtime_security_uid', ['runtime_security_uid', 'security_uid']) || webId;
// 鐢ㄥ悗绔�鏈熸湜鐨勨�滆�勮寖閿�鍚嶁�濋噸寤� localStorage 鏉$洰锛屽�肩敤鏅鸿兘鎻愬彇鍒扮殑缁撴灉
const localStorageEntries = [];
if (valKeys) localStorageEntries.push({ name: 'security-sdk/s_sdk_crypt_sdk', value: String(valKeys) });
if (valProtect) localStorageEntries.push({ name: 'security-sdk/s_sdk_sign_data_key/web_protect', value: String(valProtect) });
// 鍚庣��浠� tea_cache_tokens 瑙f瀽 web_id锛涜嫢鍘熷�嬪�肩己澶变絾鎸栧埌浜� web_id锛屽垯鍚堟垚涓�涓�鏈�灏� structure
const teaRaw = pickLs(ls, 'tea_cache_tokens', ['tea_cache_tokens']);
if (teaRaw) {
localStorageEntries.push({ name: 'tea_cache_tokens', value: String(teaRaw) });
} else if (webId) {
localStorageEntries.push({ name: 'tea_cache_tokens', value: JSON.stringify({ user_unique_id: webId, web_id: webId }) });
}
if (deviceId) localStorageEntries.push({ name: 'web_runtime_security_uid', value: String(deviceId) });
const storageState = {
cookies,
origins: [
{ origin: "https://www.douyin.com", localStorage: localStorageEntries }
],
user_agent: navigator.userAgent
};
const finalUid = document.getElementById('uidInput').value.trim() || dump.my_uid;
if (finalUid) {
storageState.my_uid = parseInt(finalUid, 10);
}
// 鏂规�� A锛氳В鏋� frontier WS URL锛堟�ラ�� 鈽� 鎵嬪~鎴栦功绛捐嚜鍔ㄩ噰闆嗭級
let wsRaw = (document.getElementById('wsInput').value || '').trim();
if (!wsRaw && dump.frontier_ws_url) wsRaw = dump.frontier_ws_url;
if (!wsRaw && dump.ws_urls && dump.ws_urls[0]) wsRaw = dump.ws_urls[0];
let hasWs = false, wsSdkCert = '', wsTsSign = '';
if (wsRaw) {
const wsUrl = extractWsUrl(wsRaw);
const isFrontier = wsUrl && /[?&]token=/.test(wsUrl) &&
(wsUrl.indexOf('frontier-im.douyin.com') !== -1 || (wsUrl.indexOf('frontier') !== -1 && wsUrl.indexOf('zijieapi.com') !== -1));
if (isFrontier) {
hasWs = true;
storageState.frontier_ws_url = wsUrl;
wsSdkCert = qparam(wsUrl, 'sdk_cert');
wsTsSign = qparam(wsUrl, 'ts_sign');
if (wsSdkCert) storageState.sdk_cert = wsSdkCert;
if (wsTsSign) storageState.ts_sign = wsTsSign;
}
}
document.getElementById('output').value = JSON.stringify(storageState, null, 2);
// 鏍¢獙
const hasSession = names.has('sessionid') || names.has('sessionid_ss');
const hasUid = !!finalUid || names.has('uid_tt') || names.has('uid_tt_ss');
const hasProtect = !!valProtect;
const hasKeys = !!valKeys;
const hasWebId = !!webId;
const hasDevice = !!deviceId;
const wp = parseWebProtect(valProtect);
const hasWpTicket = !!(wp && wp.ticket);
const hasWpTs = !!(wp && wp.ts_sign);
const hasWpCert = !!(wp && (wp.client_cert || wp.sdk_cert));
checks.push(chk(hasSession, 'sessionid锛堢櫥褰曟�侊紝蹇呴渶锛�', '缂� sessionid锛佸繀椤讳粠銆屾�ラ�� 2銆峃etwork 璇锋眰澶撮噷鐨� Cookie 澶嶅埗锛堝惈 sessionid=...锛夛紝document.cookie 璇讳笉鍒板畠'));
checks.push(chk(hasUid, '鎶栭煶鏁板瓧 UID', '鏈�鎻愬彇鍒版暟瀛� UID锛堜功绛句細鑷�鍔ㄦ姄鍙栵級锛涘彲鎵嬪姩濉�鍐欙紝缂哄け鏃跺悗鍙颁細鑱旂綉鏌ヨ��'));
checks.push(chk(hasProtect, 'web_protect锛堢�惧悕鍑�璇侊紝蹇呴渶锛�', '缂� web_protect锛佽�峰湪鎶栭煶銆岀�佷俊椤点�嶅仠鐣欏嚑绉掕�╁畨鍏� SDK 鍐欏叆鍚庯紝閲嶆柊鐢ㄤ功绛�/鑴氭湰閲囬泦'));
if (hasProtect) {
checks.push(chk(hasWpTicket && hasWpTs && hasWpCert, 'web_protect 鍐呭惈 ticket + ts_sign + client_cert锛堝彂閫佸繀闇�锛�',
'web_protect 涓嶅畬鏁达紒璇风‘淇濆湪銆岀�佷俊椤点�嶅仠鐣� 5~10 绉掑悗閲嶆柊閲囬泦锛涚己 client_cert/ts_sign 浼氬�艰嚧鍙戦�� 7911'));
}
checks.push(chk(hasKeys, 'keys / crypt_sdk锛堢�惧悕瀵嗛挜锛屽繀闇�锛�', '缂� crypt_sdk锛佽�烽噸鏂伴噰闆� localStorage锛堢‘璁ゅ湪宸茬櫥褰曠殑鎶栭煶鏍囩�鹃〉鎵ц�岋級'));
checks.push(chk(hasWebId, 'web_id', '缂� web_id锛歭ocalStorage 閲屾病鎵惧埌 tea_cache_tokens锛涘埛鏂版姈闊抽〉闈㈠悗閲嶉噰闆�'));
checks.push(chk(hasDevice, 'device_id', '缂� device_id锛氭病鎵惧埌 web_runtime_security_uid锛涘埛鏂版姈闊抽〉闈㈠悗閲嶉噰闆嗭紙宸插皾璇曠敤 web_id 鍏滃簳锛�'));
if (hasWs) {
checks.push({ s: wsSdkCert ? 'ok' : 'warn', t: wsSdkCert ? '宸插寘鍚� frontier WebSocket锛堜功绛�/鑴氭湰鍙�鑷�鍔ㄦ姄鍙栵紝鐢ㄤ簬瀹炴椂鏀舵秷鎭�锛�' : '宸插寘鍚� frontier WebSocket锛屼絾鏈�瑙f瀽鍒� sdk_cert锛岃�风‘璁� URL 瀹屾暣' });
} else {
checks.push({ s: 'warn', t: '鏈�鎶撳埌 frontier WebSocket锛氳�风敤涔︾�炬墦寮�绉佷俊椤靛悗鍐嶉噰闆嗭紝鎴栨墜鍔ㄧ矘璐� WS URL锛堟�ラ�� 鈽咃級' });
}
renderChecks(checks);
const okCore = hasSession && hasProtect && hasKeys && hasWpTicket && hasWpTs && hasWpCert;
if (okCore) {
if (autoFocus) {
// 妫�鏌ュ苟鑷�鍔ㄥ垏鍥� Tab 4
const tab4Btn = document.getElementById('tab-btn-4');
if (tab4Btn && !tab4Btn.classList.contains('active')) {
switchTab(4);
}
// 鑷�鍔ㄥ�嶅埗
const outputText = JSON.stringify(storageState, null, 2);
navigator.clipboard.writeText(outputText).then(() => {
toast('鍑�璇佺敓鎴愭垚鍔燂紝宸茶嚜鍔ㄥ�嶅埗鍒板壀璐存澘锛�');
}).catch(() => {
const ta = document.createElement('textarea');
ta.value = outputText;
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
ta.remove();
toast('鍑�璇佺敓鎴愭垚鍔燂紝宸茶嚜鍔ㄥ�嶅埗鍒板壀璐存澘锛�');
});
// 鑷�鍔ㄦ粴鍔ㄥ埌姝ラ�� 4 Card
setTimeout(() => {
document.getElementById('step4Card')?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}, 300);
} else {
toast(okCore ? (hasWebId && hasDevice ? '鐢熸垚鎴愬姛锛屽嚟璇侀綈鍏�锛屽彲澶嶅埗瀵煎叆' : '鏍稿績鍑�璇侀綈鍏ㄥ彲鐢�锛泈eb_id/device_id 寤鸿��琛ュ叏') : '鍏抽敭鍑�璇佺己澶憋紝璇风湅绾㈣壊鎻愮ず');
}
} else {
if (autoFocus) {
document.getElementById('step4Card')?.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
toastWarn('鍏抽敭鍑�璇佺己澶憋紝璇风湅绾㈣壊鎻愮ず杩涜�岃ˉ閲�');
} else {
toast('鍏抽敭鍑�璇佺己澶憋紝璇风湅绾㈣壊鎻愮ず');
}
}
}
function chk(ok, okText, failText) {
if (ok) return { s: 'ok', t: '宸插寘鍚� ' + okText };
const critical = /蹇呴渶/.test(okText);
return { s: critical ? 'err' : 'warn', t: failText };
}
function renderChecks(list) {
document.getElementById('checks').innerHTML = list.map(c =>
`<div class="chk ${c.s}"><span class="dot"></span>${c.t}</div>`
).join('');
}
</script>
</body>
</html>