tt-help-cli-ycl 1.4.45 → 1.4.46
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/lib/shop-scraper.mjs +93 -39
package/package.json
CHANGED
package/src/lib/shop-scraper.mjs
CHANGED
|
@@ -83,58 +83,112 @@ function extractJsonByKey(html, keyPattern) {
|
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* Windows: 后台服务的 Playwright 浏览器在 Session 0(隔离桌面),
|
|
86
|
-
* RDP
|
|
86
|
+
* RDP 看不到。找到交互桌面 Session,用计划任务推到那个 Session。
|
|
87
87
|
*/
|
|
88
|
+
let winRecoveryRunning = false;
|
|
89
|
+
|
|
88
90
|
async function resolveCaptchaWindows(url, proxyUrl) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
execSync(`start "" "microsoft-edge:${edgeUrl}"`, {
|
|
93
|
-
shell: "cmd.exe",
|
|
94
|
-
timeout: 5000,
|
|
95
|
-
});
|
|
96
|
-
console.error(
|
|
97
|
-
"[shop-scraper] Windows: 已用 start 命令在交互桌面打开 Edge,请在 RDP 中完成验证码",
|
|
98
|
-
);
|
|
99
|
-
} catch (e) {
|
|
100
|
-
console.error("[shop-scraper] Windows: start 命令失败:", e.message);
|
|
91
|
+
if (winRecoveryRunning) {
|
|
92
|
+
console.error("[shop-scraper] Windows: 验证码恢复已在运行,跳过");
|
|
93
|
+
return false;
|
|
101
94
|
}
|
|
95
|
+
winRecoveryRunning = true;
|
|
102
96
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
const POLL_MS = 5000;
|
|
107
|
-
const MAX_WAIT_MS = 3600_000; // 最长等 1 小时
|
|
108
|
-
const start = Date.now();
|
|
97
|
+
try {
|
|
98
|
+
const edgeUrl = url.replace("shop.tiktok.com", "www.tiktok.com/shop/pdp");
|
|
99
|
+
const taskName = "TikTokCaptchaRecovery";
|
|
109
100
|
|
|
110
|
-
|
|
111
|
-
|
|
101
|
+
// 1. 找到交互桌面 Session
|
|
102
|
+
let sessionId = null;
|
|
112
103
|
try {
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
104
|
+
const sessions = execSync(
|
|
105
|
+
`powershell -NoProfile -Command "query session 2>$null | Select-String 'Active' | ForEach-Object { \\$_.Line }"`,
|
|
106
|
+
{ shell: "cmd.exe", timeout: 5000, encoding: "utf-8" },
|
|
107
|
+
);
|
|
108
|
+
const lines = sessions.trim().split("\n");
|
|
109
|
+
for (const line of lines) {
|
|
110
|
+
const match = line.match(/>\s*(\d+)\s/);
|
|
111
|
+
if (match && match[1] !== "0") {
|
|
112
|
+
sessionId = parseInt(match[1], 10);
|
|
113
|
+
break;
|
|
114
|
+
}
|
|
124
115
|
}
|
|
125
116
|
} catch {
|
|
126
|
-
|
|
117
|
+
console.error("[shop-scraper] Windows: 无法获取交互 Session");
|
|
127
118
|
}
|
|
128
|
-
|
|
129
|
-
if (
|
|
119
|
+
|
|
120
|
+
if (sessionId) {
|
|
121
|
+
console.error(
|
|
122
|
+
`[shop-scraper] Windows: Session ${sessionId},用计划任务启动 Edge`,
|
|
123
|
+
);
|
|
124
|
+
try {
|
|
125
|
+
execSync(`schtasks /delete /tn "${taskName}" /f 2>$null`, {
|
|
126
|
+
shell: "cmd.exe",
|
|
127
|
+
});
|
|
128
|
+
} catch {}
|
|
129
|
+
execSync(
|
|
130
|
+
`schtasks /create /tn "${taskName}" /tr "msedge ${edgeUrl}" /sc once /st 00:00 /it /f`,
|
|
131
|
+
{ shell: "cmd.exe", timeout: 10000 },
|
|
132
|
+
);
|
|
133
|
+
execSync(`schtasks /run /tn "${taskName}"`, {
|
|
134
|
+
shell: "cmd.exe",
|
|
135
|
+
timeout: 10000,
|
|
136
|
+
});
|
|
137
|
+
setTimeout(() => {
|
|
138
|
+
try {
|
|
139
|
+
execSync(`schtasks /delete /tn "${taskName}" /f 2>$null`, {
|
|
140
|
+
shell: "cmd.exe",
|
|
141
|
+
});
|
|
142
|
+
} catch {}
|
|
143
|
+
}, 10000);
|
|
144
|
+
} else {
|
|
130
145
|
console.error(
|
|
131
|
-
|
|
146
|
+
"[shop-scraper] Windows: 未找到交互 Session,尝试直接 start",
|
|
132
147
|
);
|
|
148
|
+
try {
|
|
149
|
+
execSync(`start "" "microsoft-edge:${edgeUrl}"`, {
|
|
150
|
+
shell: "cmd.exe",
|
|
151
|
+
timeout: 5000,
|
|
152
|
+
});
|
|
153
|
+
} catch {}
|
|
133
154
|
}
|
|
134
|
-
}
|
|
135
155
|
|
|
136
|
-
|
|
137
|
-
|
|
156
|
+
// 2. 轮询 undici 等待 IP 信用恢复
|
|
157
|
+
const proxy = proxyUrl || DEFAULT_PROXY;
|
|
158
|
+
const agent = proxy ? new ProxyAgent(proxy) : null;
|
|
159
|
+
const POLL_MS = 5000;
|
|
160
|
+
const MAX_WAIT_MS = 3600_000;
|
|
161
|
+
const startTime = Date.now();
|
|
162
|
+
|
|
163
|
+
while (Date.now() - startTime < MAX_WAIT_MS) {
|
|
164
|
+
await new Promise((r) => setTimeout(r, POLL_MS));
|
|
165
|
+
try {
|
|
166
|
+
const { body } = await request(url, {
|
|
167
|
+
headers: PDP_HEADERS,
|
|
168
|
+
...(agent ? { dispatcher: agent } : {}),
|
|
169
|
+
});
|
|
170
|
+
const html = await body.text();
|
|
171
|
+
if (
|
|
172
|
+
html.includes('"product_model"') ||
|
|
173
|
+
html.includes('"review_ratings"')
|
|
174
|
+
) {
|
|
175
|
+
console.error("[shop-scraper] Captcha solved, IP credit restored");
|
|
176
|
+
return true;
|
|
177
|
+
}
|
|
178
|
+
} catch {}
|
|
179
|
+
const waited = Math.round((Date.now() - startTime) / 60000);
|
|
180
|
+
if (waited > 0 && waited % 5 === 0) {
|
|
181
|
+
console.error(
|
|
182
|
+
`[shop-scraper] 等待验证码解决中... 已等待 ${waited} 分钟`,
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
console.error("[shop-scraper] Windows: 等待验证码超时");
|
|
188
|
+
return false;
|
|
189
|
+
} finally {
|
|
190
|
+
winRecoveryRunning = false;
|
|
191
|
+
}
|
|
138
192
|
}
|
|
139
193
|
|
|
140
194
|
/**
|