@jackwener/opencli 1.7.3 → 1.7.5
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/README.md +81 -59
- package/README.zh-CN.md +93 -67
- package/cli-manifest.json +5015 -2975
- package/clis/antigravity/serve.js +71 -25
- package/clis/baidu-scholar/search.js +87 -0
- package/clis/baidu-scholar/search.test.js +23 -0
- package/clis/bilibili/favorite.js +18 -13
- package/clis/binance/depth.js +3 -4
- package/clis/boss/utils.js +2 -3
- package/clis/chatgpt-app/ax.js +6 -3
- package/clis/deepseek/ask.js +74 -0
- package/clis/deepseek/history.js +25 -0
- package/clis/deepseek/new.js +20 -0
- package/clis/deepseek/read.js +22 -0
- package/clis/deepseek/status.js +24 -0
- package/clis/deepseek/utils.js +208 -0
- package/clis/douban/search.js +1 -0
- package/clis/douban/search.test.js +11 -0
- package/clis/douban/subject.js +20 -93
- package/clis/douban/subject.test.js +11 -0
- package/clis/douban/utils.js +250 -8
- package/clis/douban/utils.test.js +179 -4
- package/clis/doubao/utils.js +319 -130
- package/clis/doubao/utils.test.js +241 -2
- package/clis/eastmoney/_secid.js +78 -0
- package/clis/eastmoney/announcement.js +52 -0
- package/clis/eastmoney/convertible.js +73 -0
- package/clis/eastmoney/etf.js +65 -0
- package/clis/eastmoney/holders.js +78 -0
- package/clis/eastmoney/hot-rank.js +50 -0
- package/clis/eastmoney/hot-rank.test.js +59 -0
- package/clis/eastmoney/index-board.js +96 -0
- package/clis/eastmoney/kline.js +87 -0
- package/clis/eastmoney/kuaixun.js +54 -0
- package/clis/eastmoney/longhu.js +67 -0
- package/clis/eastmoney/money-flow.js +78 -0
- package/clis/eastmoney/northbound.js +57 -0
- package/clis/eastmoney/quote.js +107 -0
- package/clis/eastmoney/rank.js +94 -0
- package/clis/eastmoney/sectors.js +76 -0
- package/clis/google-scholar/search.js +58 -0
- package/clis/google-scholar/search.test.js +23 -0
- package/clis/gov-law/commands.test.js +39 -0
- package/clis/gov-law/recent.js +22 -0
- package/clis/gov-law/search.js +41 -0
- package/clis/gov-law/shared.js +51 -0
- package/clis/gov-policy/commands.test.js +27 -0
- package/clis/gov-policy/recent.js +47 -0
- package/clis/gov-policy/search.js +48 -0
- package/clis/grok/image.test.ts +107 -0
- package/clis/grok/image.ts +356 -0
- package/clis/nowcoder/companies.js +23 -0
- package/clis/nowcoder/creators.js +27 -0
- package/clis/nowcoder/detail.js +61 -0
- package/clis/nowcoder/experience.js +36 -0
- package/clis/nowcoder/hot.js +24 -0
- package/clis/nowcoder/jobs.js +21 -0
- package/clis/nowcoder/notifications.js +29 -0
- package/clis/nowcoder/papers.js +40 -0
- package/clis/nowcoder/practice.js +37 -0
- package/clis/nowcoder/recommend.js +30 -0
- package/clis/nowcoder/referral.js +39 -0
- package/clis/nowcoder/salary.js +40 -0
- package/clis/nowcoder/search.js +49 -0
- package/clis/nowcoder/suggest.js +33 -0
- package/clis/nowcoder/topics.js +27 -0
- package/clis/nowcoder/trending.js +25 -0
- package/clis/tdx/hot-rank.js +47 -0
- package/clis/tdx/hot-rank.test.js +59 -0
- package/clis/ths/hot-rank.js +49 -0
- package/clis/ths/hot-rank.test.js +64 -0
- package/clis/twitter/bookmarks.js +2 -1
- package/clis/twitter/list-add.js +337 -0
- package/clis/twitter/list-add.test.js +15 -0
- package/clis/twitter/list-remove.js +297 -0
- package/clis/twitter/list-remove.test.js +14 -0
- package/clis/twitter/list-tweets.js +185 -0
- package/clis/twitter/list-tweets.test.js +108 -0
- package/clis/twitter/lists.js +134 -47
- package/clis/twitter/lists.test.js +105 -38
- package/clis/uiverse/_shared.js +368 -0
- package/clis/uiverse/_shared.test.js +55 -0
- package/clis/uiverse/code.js +47 -0
- package/clis/uiverse/preview.js +71 -0
- package/clis/wanfang/search.js +66 -0
- package/clis/wanfang/search.test.js +23 -0
- package/clis/web/read.js +1 -1
- package/clis/weixin/download.js +3 -2
- package/clis/xiaohongshu/comments.js +2 -2
- package/clis/xiaohongshu/comments.test.js +46 -25
- package/clis/xiaohongshu/download.js +6 -7
- package/clis/xiaohongshu/download.test.js +17 -5
- package/clis/xiaohongshu/note-helpers.js +46 -12
- package/clis/xiaohongshu/note.js +3 -5
- package/clis/xiaohongshu/note.test.js +52 -25
- package/clis/xiaohongshu/publish.js +149 -28
- package/clis/xiaohongshu/publish.test.js +319 -6
- package/clis/xiaoyuzhou/auth.js +303 -0
- package/clis/xiaoyuzhou/auth.test.js +124 -0
- package/clis/xiaoyuzhou/download.js +53 -0
- package/clis/xiaoyuzhou/download.test.js +135 -0
- package/clis/xiaoyuzhou/episode.js +9 -4
- package/clis/xiaoyuzhou/podcast-episodes.js +15 -11
- package/clis/xiaoyuzhou/podcast.js +9 -4
- package/clis/xiaoyuzhou/transcript.js +76 -0
- package/clis/xiaoyuzhou/transcript.test.js +195 -0
- package/clis/xiaoyuzhou/utils.js +0 -40
- package/clis/xiaoyuzhou/utils.test.js +15 -75
- package/clis/youtube/feed.js +120 -0
- package/clis/youtube/history.js +118 -0
- package/clis/youtube/like.js +62 -0
- package/clis/youtube/playlist.js +97 -0
- package/clis/youtube/subscribe.js +71 -0
- package/clis/youtube/subscriptions.js +57 -0
- package/clis/youtube/unlike.js +62 -0
- package/clis/youtube/unsubscribe.js +71 -0
- package/clis/youtube/utils.js +122 -0
- package/clis/youtube/utils.test.js +32 -1
- package/clis/youtube/watch-later.js +76 -0
- package/clis/zsxq/dynamics.js +1 -1
- package/clis/zsxq/utils.js +6 -3
- package/clis/zsxq/utils.test.js +31 -0
- package/dist/src/browser/base-page.d.ts +1 -1
- package/dist/src/browser/base-page.js +25 -5
- package/dist/src/browser/bridge.d.ts +3 -0
- package/dist/src/browser/bridge.js +52 -15
- package/dist/src/browser/cdp.js +2 -1
- package/dist/src/browser/daemon-client.d.ts +7 -4
- package/dist/src/browser/daemon-client.js +6 -1
- package/dist/src/browser/daemon-client.test.js +40 -1
- package/dist/src/browser/dom-snapshot.js +20 -3
- package/dist/src/browser/page.d.ts +18 -5
- package/dist/src/browser/page.js +96 -15
- package/dist/src/browser/page.test.js +158 -1
- package/dist/src/browser/target-errors.d.ts +23 -0
- package/dist/src/browser/target-errors.js +29 -0
- package/dist/src/browser/target-errors.test.js +61 -0
- package/dist/src/browser/target-resolver.d.ts +57 -0
- package/dist/src/browser/target-resolver.js +298 -0
- package/dist/src/browser/target-resolver.test.js +43 -0
- package/dist/src/browser.test.js +38 -1
- package/dist/src/cli.js +272 -187
- package/dist/src/cli.test.js +167 -90
- package/dist/src/commanderAdapter.d.ts +0 -1
- package/dist/src/commanderAdapter.js +2 -16
- package/dist/src/commanderAdapter.test.js +1 -1
- package/dist/src/commands/daemon.d.ts +4 -2
- package/dist/src/commands/daemon.js +22 -2
- package/dist/src/commands/daemon.test.js +65 -2
- package/dist/src/completion-shared.js +2 -5
- package/dist/src/daemon.js +10 -0
- package/dist/src/doctor.d.ts +1 -0
- package/dist/src/doctor.js +32 -9
- package/dist/src/doctor.test.js +28 -12
- package/dist/src/download/article-download.d.ts +1 -0
- package/dist/src/download/article-download.js +3 -0
- package/dist/src/download/article-download.test.js +39 -0
- package/dist/src/external-clis.yaml +2 -2
- package/dist/src/logger.d.ts +2 -2
- package/dist/src/logger.js +3 -3
- package/dist/src/output.js +1 -5
- package/dist/src/output.test.js +0 -21
- package/dist/src/pipeline/steps/transform.js +1 -1
- package/dist/src/pipeline/template.d.ts +1 -0
- package/dist/src/pipeline/template.js +11 -3
- package/dist/src/pipeline/template.test.js +3 -0
- package/dist/src/pipeline/transform.test.js +14 -0
- package/dist/src/plugin.d.ts +8 -9
- package/dist/src/plugin.js +24 -28
- package/dist/src/plugin.test.js +16 -60
- package/dist/src/registry.d.ts +1 -0
- package/dist/src/registry.js +3 -2
- package/dist/src/registry.test.js +22 -0
- package/dist/src/types.d.ts +15 -6
- package/package.json +1 -1
- package/clis/twitter/lists-parser.js +0 -77
- package/clis/twitter/lists.d.ts +0 -5
- package/dist/src/cascade.d.ts +0 -46
- package/dist/src/cascade.js +0 -135
- package/dist/src/explore.d.ts +0 -99
- package/dist/src/explore.js +0 -402
- package/dist/src/generate-verified.d.ts +0 -105
- package/dist/src/generate-verified.js +0 -696
- package/dist/src/generate-verified.test.js +0 -925
- package/dist/src/generate.d.ts +0 -46
- package/dist/src/generate.js +0 -117
- package/dist/src/record.d.ts +0 -96
- package/dist/src/record.js +0 -657
- package/dist/src/record.test.js +0 -293
- package/dist/src/skill-generate.d.ts +0 -30
- package/dist/src/skill-generate.js +0 -75
- package/dist/src/skill-generate.test.js +0 -173
- package/dist/src/synthesize.d.ts +0 -97
- package/dist/src/synthesize.js +0 -208
- /package/dist/src/{generate-verified.test.d.ts → browser/target-errors.test.d.ts} +0 -0
- /package/dist/src/{record.test.d.ts → browser/target-resolver.test.d.ts} +0 -0
- /package/dist/src/{skill-generate.test.d.ts → download/article-download.test.d.ts} +0 -0
package/clis/doubao/utils.js
CHANGED
|
@@ -1,6 +1,44 @@
|
|
|
1
|
+
import { CommandExecutionError } from '@jackwener/opencli/errors';
|
|
2
|
+
|
|
1
3
|
export const DOUBAO_DOMAIN = 'www.doubao.com';
|
|
2
4
|
export const DOUBAO_CHAT_URL = 'https://www.doubao.com/chat';
|
|
3
5
|
export const DOUBAO_NEW_CHAT_URL = 'https://www.doubao.com/chat/new-thread/create-by-msg';
|
|
6
|
+
const DOUBAO_COMPOSER_SELECTORS = [
|
|
7
|
+
'textarea[data-testid="chat_input_input"]',
|
|
8
|
+
'[data-testid="chat_input"] textarea',
|
|
9
|
+
'.chat-input textarea',
|
|
10
|
+
'.chat-input [contenteditable="true"]',
|
|
11
|
+
'.chat-editor textarea',
|
|
12
|
+
'.chat-editor [contenteditable="true"]',
|
|
13
|
+
'textarea[placeholder*="发消息"]',
|
|
14
|
+
'textarea[placeholder*="Message"]',
|
|
15
|
+
'[contenteditable="true"][placeholder*="发消息"]',
|
|
16
|
+
'[contenteditable="true"][placeholder*="Message"]',
|
|
17
|
+
'[contenteditable="true"][aria-label*="发消息"]',
|
|
18
|
+
'[contenteditable="true"][aria-label*="Message"]',
|
|
19
|
+
'textarea',
|
|
20
|
+
'[contenteditable="true"]',
|
|
21
|
+
];
|
|
22
|
+
function buildDoubaoComposerLocatorScript() {
|
|
23
|
+
return `
|
|
24
|
+
const isVisible = (el) => {
|
|
25
|
+
if (!(el instanceof HTMLElement)) return false;
|
|
26
|
+
const style = window.getComputedStyle(el);
|
|
27
|
+
if (style.display === 'none' || style.visibility === 'hidden') return false;
|
|
28
|
+
const rect = el.getBoundingClientRect();
|
|
29
|
+
return rect.width > 0 && rect.height > 0;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const composerSelectors = ${JSON.stringify(DOUBAO_COMPOSER_SELECTORS)};
|
|
33
|
+
const findComposer = () => {
|
|
34
|
+
for (const selector of composerSelectors) {
|
|
35
|
+
const node = Array.from(document.querySelectorAll(selector)).find(isVisible);
|
|
36
|
+
if (node) return node;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
};
|
|
40
|
+
`;
|
|
41
|
+
}
|
|
4
42
|
function getTranscriptLinesScript() {
|
|
5
43
|
return `
|
|
6
44
|
(() => {
|
|
@@ -205,42 +243,98 @@ function getTurnsScript() {
|
|
|
205
243
|
})()
|
|
206
244
|
`;
|
|
207
245
|
}
|
|
208
|
-
function
|
|
246
|
+
function prepareDoubaoComposerScript() {
|
|
209
247
|
return `
|
|
210
|
-
((
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
const style = window.getComputedStyle(el);
|
|
214
|
-
if (style.display === 'none' || style.visibility === 'hidden') return false;
|
|
215
|
-
const rect = el.getBoundingClientRect();
|
|
216
|
-
return rect.width > 0 && rect.height > 0;
|
|
217
|
-
};
|
|
248
|
+
(() => {
|
|
249
|
+
${buildDoubaoComposerLocatorScript()}
|
|
250
|
+
const composer = findComposer();
|
|
218
251
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
'
|
|
225
|
-
|
|
226
|
-
'textarea[placeholder*="Message"]',
|
|
227
|
-
'[contenteditable="true"][placeholder*="发消息"]',
|
|
228
|
-
'[contenteditable="true"][placeholder*="Message"]',
|
|
229
|
-
'[contenteditable="true"][aria-label*="发消息"]',
|
|
230
|
-
'[contenteditable="true"][aria-label*="Message"]',
|
|
231
|
-
'textarea',
|
|
232
|
-
'[contenteditable="true"]',
|
|
233
|
-
];
|
|
252
|
+
if (
|
|
253
|
+
!(composer instanceof HTMLTextAreaElement)
|
|
254
|
+
&& !(composer instanceof HTMLInputElement)
|
|
255
|
+
&& !(composer instanceof HTMLElement)
|
|
256
|
+
) {
|
|
257
|
+
return { ok: false, reason: 'Could not find Doubao input element' };
|
|
258
|
+
}
|
|
234
259
|
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
|
|
240
|
-
|
|
260
|
+
try {
|
|
261
|
+
composer.focus();
|
|
262
|
+
|
|
263
|
+
if (composer instanceof HTMLTextAreaElement || composer instanceof HTMLInputElement) {
|
|
264
|
+
const length = composer.value.length;
|
|
265
|
+
composer.setSelectionRange(0, length);
|
|
266
|
+
} else {
|
|
267
|
+
const selection = window.getSelection();
|
|
268
|
+
const range = document.createRange();
|
|
269
|
+
range.selectNodeContents(composer);
|
|
270
|
+
selection?.removeAllRanges();
|
|
271
|
+
selection?.addRange(range);
|
|
241
272
|
}
|
|
273
|
+
} catch (error) {
|
|
274
|
+
return {
|
|
275
|
+
ok: false,
|
|
276
|
+
reason: error instanceof Error ? error.message : String(error),
|
|
277
|
+
};
|
|
242
278
|
}
|
|
243
279
|
|
|
280
|
+
return { ok: true };
|
|
281
|
+
})()
|
|
282
|
+
`;
|
|
283
|
+
}
|
|
284
|
+
function composerStateScript() {
|
|
285
|
+
return `
|
|
286
|
+
(() => {
|
|
287
|
+
${buildDoubaoComposerLocatorScript()}
|
|
288
|
+
const composer = findComposer();
|
|
289
|
+
|
|
290
|
+
if (composer instanceof HTMLTextAreaElement || composer instanceof HTMLInputElement) {
|
|
291
|
+
return { hasText: !!composer.value.trim(), text: composer.value };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
if (composer instanceof HTMLElement) {
|
|
295
|
+
const text = (composer.innerText || '').trim() || (composer.textContent || '').trim();
|
|
296
|
+
return {
|
|
297
|
+
hasText: !!text,
|
|
298
|
+
text,
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return { hasText: false, text: '' };
|
|
303
|
+
})()
|
|
304
|
+
`;
|
|
305
|
+
}
|
|
306
|
+
function syncComposerAfterNativeTypeScript() {
|
|
307
|
+
return `
|
|
308
|
+
(() => {
|
|
309
|
+
${buildDoubaoComposerLocatorScript()}
|
|
310
|
+
const composer = findComposer();
|
|
311
|
+
|
|
312
|
+
if (composer instanceof HTMLTextAreaElement || composer instanceof HTMLInputElement) {
|
|
313
|
+
const value = composer.value;
|
|
314
|
+
composer.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, data: value, inputType: 'insertText' }));
|
|
315
|
+
composer.dispatchEvent(new InputEvent('input', { bubbles: true, data: value, inputType: 'insertText' }));
|
|
316
|
+
composer.dispatchEvent(new Event('change', { bubbles: true }));
|
|
317
|
+
return { hasText: !!value.trim(), text: value };
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (composer instanceof HTMLElement) {
|
|
321
|
+
const text = (composer.innerText || '').trim() || (composer.textContent || '').trim();
|
|
322
|
+
composer.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, data: text, inputType: 'insertText' }));
|
|
323
|
+
composer.dispatchEvent(new InputEvent('input', { bubbles: true, data: text, inputType: 'insertText' }));
|
|
324
|
+
composer.dispatchEvent(new Event('change', { bubbles: true }));
|
|
325
|
+
return { hasText: !!text, text };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
return { hasText: false, text: '' };
|
|
329
|
+
})()
|
|
330
|
+
`;
|
|
331
|
+
}
|
|
332
|
+
function fillComposerScript(text) {
|
|
333
|
+
return `
|
|
334
|
+
((inputText) => {
|
|
335
|
+
${buildDoubaoComposerLocatorScript()}
|
|
336
|
+
const composer = findComposer();
|
|
337
|
+
|
|
244
338
|
if (!composer) throw new Error('Could not find Doubao input element');
|
|
245
339
|
|
|
246
340
|
composer.focus();
|
|
@@ -251,9 +345,10 @@ function fillComposerScript(text) {
|
|
|
251
345
|
: window.HTMLInputElement.prototype;
|
|
252
346
|
const setter = Object.getOwnPropertyDescriptor(proto, 'value')?.set;
|
|
253
347
|
setter?.call(composer, inputText);
|
|
254
|
-
composer.dispatchEvent(new
|
|
348
|
+
composer.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, data: inputText, inputType: 'insertText' }));
|
|
349
|
+
composer.dispatchEvent(new InputEvent('input', { bubbles: true, data: inputText, inputType: 'insertText' }));
|
|
255
350
|
composer.dispatchEvent(new Event('change', { bubbles: true }));
|
|
256
|
-
return 'text-input';
|
|
351
|
+
return { hasText: !!composer.value.trim(), mode: 'text-input', text: composer.value };
|
|
257
352
|
}
|
|
258
353
|
|
|
259
354
|
if (composer instanceof HTMLElement) {
|
|
@@ -265,18 +360,23 @@ function fillComposerScript(text) {
|
|
|
265
360
|
selection?.removeAllRanges();
|
|
266
361
|
selection?.addRange(range);
|
|
267
362
|
document.execCommand('insertText', false, inputText);
|
|
268
|
-
composer.dispatchEvent(new
|
|
363
|
+
composer.dispatchEvent(new InputEvent('beforeinput', { bubbles: true, data: inputText, inputType: 'insertText' }));
|
|
364
|
+
composer.dispatchEvent(new InputEvent('input', { bubbles: true, data: inputText, inputType: 'insertText' }));
|
|
269
365
|
composer.dispatchEvent(new Event('change', { bubbles: true }));
|
|
270
|
-
return
|
|
366
|
+
return {
|
|
367
|
+
hasText: !!((composer.innerText || '').trim() || (composer.textContent || '').trim()),
|
|
368
|
+
mode: 'contenteditable',
|
|
369
|
+
text: (composer.innerText || '').trim() || (composer.textContent || '').trim(),
|
|
370
|
+
};
|
|
271
371
|
}
|
|
272
372
|
|
|
273
373
|
throw new Error('Unsupported Doubao input element');
|
|
274
374
|
})(${JSON.stringify(text)})
|
|
275
375
|
`;
|
|
276
376
|
}
|
|
277
|
-
function
|
|
377
|
+
function detectDoubaoVerificationScript() {
|
|
278
378
|
return `
|
|
279
|
-
((
|
|
379
|
+
(() => {
|
|
280
380
|
const isVisible = (el) => {
|
|
281
381
|
if (!(el instanceof HTMLElement)) return false;
|
|
282
382
|
const style = window.getComputedStyle(el);
|
|
@@ -285,119 +385,115 @@ function fillAndSubmitComposerScript(text) {
|
|
|
285
385
|
return rect.width > 0 && rect.height > 0;
|
|
286
386
|
};
|
|
287
387
|
|
|
288
|
-
const
|
|
289
|
-
'
|
|
290
|
-
'[
|
|
291
|
-
'
|
|
292
|
-
'
|
|
293
|
-
'textarea[placeholder*="Message"]',
|
|
294
|
-
'textarea',
|
|
388
|
+
const challengeSelectors = [
|
|
389
|
+
'iframe[src*="captcha"]',
|
|
390
|
+
'iframe[src*="verify"]',
|
|
391
|
+
'input[placeholder*="验证码"]',
|
|
392
|
+
'input[aria-label*="验证码"]',
|
|
295
393
|
];
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
composer = node;
|
|
302
|
-
break;
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (!(composer instanceof HTMLTextAreaElement || composer instanceof HTMLInputElement)) {
|
|
307
|
-
throw new Error('Could not find Doubao textarea input element');
|
|
394
|
+
const selectorMatch = challengeSelectors.find((selector) => {
|
|
395
|
+
return Array.from(document.querySelectorAll(selector)).some((node) => isVisible(node));
|
|
396
|
+
});
|
|
397
|
+
if (selectorMatch) {
|
|
398
|
+
return { detected: true, reason: selectorMatch };
|
|
308
399
|
}
|
|
309
400
|
|
|
310
|
-
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
const
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const target = buttons[buttons.length - 1];
|
|
322
|
-
|
|
323
|
-
if (target) {
|
|
324
|
-
target.click();
|
|
325
|
-
return 'button';
|
|
326
|
-
}
|
|
401
|
+
const phrasePattern = /人机验证|完成安全验证|异常访问|滑动验证|拖动滑块/i;
|
|
402
|
+
const candidateRoots = Array.from(
|
|
403
|
+
document.querySelectorAll('[role="dialog"], [aria-modal="true"], .semi-modal, .modal')
|
|
404
|
+
);
|
|
405
|
+
const match = candidateRoots.find((node) => {
|
|
406
|
+
if (!(node instanceof HTMLElement)) return false;
|
|
407
|
+
if (!isVisible(node)) return false;
|
|
408
|
+
const text = (node.innerText || node.textContent || '').trim();
|
|
409
|
+
if (!text || text.length > 400) return false;
|
|
410
|
+
return phrasePattern.test(text);
|
|
411
|
+
});
|
|
327
412
|
|
|
328
|
-
return
|
|
329
|
-
|
|
413
|
+
return {
|
|
414
|
+
detected: !!match,
|
|
415
|
+
reason: match ? ((match.innerText || match.textContent || '').trim().slice(0, 80) || 'challenge-ui') : '',
|
|
416
|
+
};
|
|
417
|
+
})()
|
|
330
418
|
`;
|
|
331
419
|
}
|
|
332
420
|
function clickSendButtonScript() {
|
|
333
421
|
return `
|
|
334
422
|
(() => {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
423
|
+
${buildDoubaoComposerLocatorScript()}
|
|
424
|
+
const composer = findComposer();
|
|
425
|
+
if (!(composer instanceof HTMLElement)) return false;
|
|
426
|
+
|
|
427
|
+
const composerRect = composer.getBoundingClientRect();
|
|
428
|
+
const rootCandidates = [
|
|
429
|
+
composer.closest('form'),
|
|
430
|
+
composer.closest('[role="form"]'),
|
|
431
|
+
composer.closest('[data-testid="chat_input"]'),
|
|
432
|
+
composer.closest('.chat-input'),
|
|
433
|
+
composer.parentElement,
|
|
434
|
+
composer.parentElement?.parentElement,
|
|
435
|
+
].filter(Boolean);
|
|
436
|
+
|
|
437
|
+
const seen = new Set();
|
|
438
|
+
const buttons = [];
|
|
439
|
+
for (const root of rootCandidates) {
|
|
440
|
+
root.querySelectorAll('button, [role="button"]').forEach((node) => {
|
|
441
|
+
if (!(node instanceof HTMLElement)) return;
|
|
442
|
+
if (seen.has(node)) return;
|
|
443
|
+
seen.add(node);
|
|
444
|
+
buttons.push(node);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
342
447
|
|
|
343
|
-
const
|
|
344
|
-
const
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
));
|
|
448
|
+
const submitPattern = /send|发送|提交|发消息/i;
|
|
449
|
+
const excludedPattern = /新对话|new chat|快速|视频生成|深入研究|图像生成|帮我写作|音乐生成|更多|上传|upload|麦克风|microphone|模式|mode|工具|tools|设置|settings|云盘|history|历史/i;
|
|
450
|
+
let bestButton = null;
|
|
451
|
+
let bestScore = -Infinity;
|
|
348
452
|
|
|
349
453
|
for (const button of buttons) {
|
|
350
454
|
if (!isVisible(button)) continue;
|
|
351
455
|
const disabled = button.getAttribute('disabled') !== null
|
|
352
456
|
|| button.getAttribute('aria-disabled') === 'true';
|
|
353
457
|
if (disabled) continue;
|
|
458
|
+
|
|
354
459
|
const text = (button.innerText || button.textContent || '').trim();
|
|
355
460
|
const aria = (button.getAttribute('aria-label') || '').trim();
|
|
356
461
|
const title = (button.getAttribute('title') || '').trim();
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
return true;
|
|
361
|
-
}
|
|
362
|
-
}
|
|
462
|
+
const className = String(button.className || '');
|
|
463
|
+
const haystack = [text, aria, title].join(' ').trim();
|
|
464
|
+
if (excludedPattern.test(haystack)) continue;
|
|
363
465
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
466
|
+
const rect = button.getBoundingClientRect();
|
|
467
|
+
const dx = rect.left - composerRect.right;
|
|
468
|
+
const dy = Math.abs((rect.top + rect.height / 2) - (composerRect.top + composerRect.height / 2));
|
|
469
|
+
const distancePenalty = Math.abs(dx) + dy;
|
|
470
|
+
const isSubmitLike = submitPattern.test(haystack)
|
|
471
|
+
|| button.getAttribute('type') === 'submit'
|
|
472
|
+
|| className.includes('bg-dbx-text-highlight')
|
|
371
473
|
|| className.includes('bg-dbx-fill-highlight')
|
|
372
474
|
|| className.includes('text-dbx-text-static-white-primary');
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
styledCandidate.click();
|
|
377
|
-
return true;
|
|
378
|
-
}
|
|
475
|
+
if (!isSubmitLike) continue;
|
|
476
|
+
if (dx < -80 || dx > 280) continue;
|
|
477
|
+
if (dy > 140) continue;
|
|
379
478
|
|
|
380
|
-
|
|
381
|
-
if (
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (
|
|
385
|
-
|
|
386
|
-
|
|
479
|
+
let score = -distancePenalty;
|
|
480
|
+
if (submitPattern.test(haystack)) score += 5000;
|
|
481
|
+
if (button.getAttribute('type') === 'submit') score += 1200;
|
|
482
|
+
if (button.closest('.chat-input-button')) score += 1200;
|
|
483
|
+
if (className.includes('bg-dbx-text-highlight')) score += 600;
|
|
484
|
+
if (className.includes('bg-dbx-fill-highlight')) score += 600;
|
|
485
|
+
if (className.includes('text-dbx-text-static-white-primary')) score += 400;
|
|
486
|
+
if (dx >= -40 && dx <= 240) score += 120;
|
|
487
|
+
if (rect.left >= composerRect.left - 40) score += 40;
|
|
387
488
|
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
489
|
+
if (score > bestScore) {
|
|
490
|
+
bestScore = score;
|
|
491
|
+
bestButton = button;
|
|
492
|
+
}
|
|
391
493
|
}
|
|
392
494
|
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
return button.getAttribute('disabled') === null
|
|
396
|
-
&& button.getAttribute('aria-disabled') !== 'true';
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
if (lastEnabledButton) {
|
|
400
|
-
lastEnabledButton.click();
|
|
495
|
+
if (bestButton && bestScore >= 200) {
|
|
496
|
+
bestButton.click();
|
|
401
497
|
return true;
|
|
402
498
|
}
|
|
403
499
|
|
|
@@ -513,15 +609,56 @@ export async function getDoubaoTranscriptLines(page) {
|
|
|
513
609
|
}
|
|
514
610
|
export async function sendDoubaoMessage(page, text) {
|
|
515
611
|
await ensureDoubaoChatPage(page);
|
|
516
|
-
const
|
|
517
|
-
|
|
612
|
+
const normalizeComposerText = (value) => value.replace(/\r\n/g, '\n').trim();
|
|
613
|
+
const expectedText = normalizeComposerText(text);
|
|
614
|
+
const prepared = await page.evaluate(prepareDoubaoComposerScript());
|
|
615
|
+
if (!prepared?.ok) {
|
|
616
|
+
throw new CommandExecutionError(prepared?.reason || 'Could not find Doubao input element');
|
|
617
|
+
}
|
|
618
|
+
let hasText = false;
|
|
619
|
+
if (page.nativeType) {
|
|
620
|
+
try {
|
|
621
|
+
await page.nativeType(text);
|
|
622
|
+
await page.wait(0.2);
|
|
623
|
+
await page.evaluate(syncComposerAfterNativeTypeScript());
|
|
624
|
+
const nativeState = await page.evaluate(composerStateScript());
|
|
625
|
+
hasText = !!nativeState?.hasText && normalizeComposerText(nativeState?.text || '') === expectedText;
|
|
626
|
+
}
|
|
627
|
+
catch { }
|
|
628
|
+
}
|
|
629
|
+
if (!hasText) {
|
|
630
|
+
const fallbackState = await page.evaluate(fillComposerScript(text));
|
|
631
|
+
hasText = !!fallbackState?.hasText && normalizeComposerText(fallbackState?.text || '') === expectedText;
|
|
632
|
+
}
|
|
633
|
+
if (!hasText) {
|
|
634
|
+
throw new CommandExecutionError('Failed to insert text into Doubao composer');
|
|
635
|
+
}
|
|
636
|
+
let submittedBy = 'enter';
|
|
637
|
+
const clicked = await page.evaluate(clickSendButtonScript());
|
|
638
|
+
if (clicked) {
|
|
639
|
+
submittedBy = 'button';
|
|
640
|
+
}
|
|
641
|
+
else if (page.nativeKeyPress) {
|
|
642
|
+
try {
|
|
643
|
+
await page.nativeKeyPress('Enter');
|
|
644
|
+
}
|
|
645
|
+
catch {
|
|
646
|
+
await page.pressKey('Enter');
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
518
650
|
await page.pressKey('Enter');
|
|
519
651
|
}
|
|
520
652
|
await page.wait(0.8);
|
|
653
|
+
const verification = await page.evaluate(detectDoubaoVerificationScript());
|
|
654
|
+
if (verification?.detected) {
|
|
655
|
+
throw new CommandExecutionError('Doubao blocked the request with a verification challenge', verification.reason
|
|
656
|
+
? `Detected challenge signal: ${verification.reason}`
|
|
657
|
+
: 'Please complete the challenge in the browser and try again.');
|
|
658
|
+
}
|
|
521
659
|
return submittedBy;
|
|
522
660
|
}
|
|
523
661
|
export async function waitForDoubaoResponse(page, beforeLines, beforeTurns, promptText, timeoutSeconds) {
|
|
524
|
-
const beforeSet = new Set(beforeLines);
|
|
525
662
|
const beforeTurnSet = new Set(beforeTurns
|
|
526
663
|
.filter((turn) => turn.Role === 'Assistant')
|
|
527
664
|
.map((turn) => `${turn.Role}::${turn.Text}`));
|
|
@@ -534,6 +671,12 @@ export async function waitForDoubaoResponse(page, beforeLines, beforeTurns, prom
|
|
|
534
671
|
.replace(/window\\._SSR_DATA.*$/g, '')
|
|
535
672
|
.trim();
|
|
536
673
|
const getCandidate = async () => {
|
|
674
|
+
const verification = await page.evaluate(detectDoubaoVerificationScript());
|
|
675
|
+
if (verification?.detected) {
|
|
676
|
+
throw new CommandExecutionError('Doubao blocked the request with a verification challenge', verification.reason
|
|
677
|
+
? `Detected challenge signal: ${verification.reason}`
|
|
678
|
+
: 'Please complete the challenge in the browser and try again.');
|
|
679
|
+
}
|
|
537
680
|
const turns = await getDoubaoVisibleTurns(page);
|
|
538
681
|
const assistantCandidate = [...turns]
|
|
539
682
|
.reverse()
|
|
@@ -542,10 +685,9 @@ export async function waitForDoubaoResponse(page, beforeLines, beforeTurns, prom
|
|
|
542
685
|
if (visibleCandidate)
|
|
543
686
|
return visibleCandidate;
|
|
544
687
|
const lines = await getDoubaoTranscriptLines(page);
|
|
545
|
-
const additions = lines
|
|
546
|
-
.
|
|
547
|
-
.
|
|
548
|
-
.filter((line) => line && line !== promptText);
|
|
688
|
+
const additions = collectDoubaoTranscriptAdditions(beforeLines, lines, promptText, sanitizeCandidate)
|
|
689
|
+
.split('\n')
|
|
690
|
+
.filter(Boolean);
|
|
549
691
|
const shortCandidate = additions.find((line) => line.length <= 120);
|
|
550
692
|
return shortCandidate || additions[additions.length - 1] || '';
|
|
551
693
|
};
|
|
@@ -571,6 +713,48 @@ export async function waitForDoubaoResponse(page, beforeLines, beforeTurns, prom
|
|
|
571
713
|
}
|
|
572
714
|
return lastCandidate;
|
|
573
715
|
}
|
|
716
|
+
export function isLikelyDoubaoUiNoise(value) {
|
|
717
|
+
const text = value.replace(/\s+/g, '');
|
|
718
|
+
if (!text)
|
|
719
|
+
return false;
|
|
720
|
+
const exactNoise = new Set([
|
|
721
|
+
'快速视频生成深入研究图像生成帮我写作音乐生成更多',
|
|
722
|
+
]);
|
|
723
|
+
return exactNoise.has(text);
|
|
724
|
+
}
|
|
725
|
+
function isAlwaysTranscriptUiNoise(value) {
|
|
726
|
+
const text = value.replace(/\s+/g, '');
|
|
727
|
+
if (!text)
|
|
728
|
+
return false;
|
|
729
|
+
const exactNoise = new Set([
|
|
730
|
+
'AI创作云盘更多历史对话',
|
|
731
|
+
]);
|
|
732
|
+
return exactNoise.has(text);
|
|
733
|
+
}
|
|
734
|
+
function isLikelyTranscriptUiNoise(rawValue, sanitizedValue, promptText) {
|
|
735
|
+
const normalizeWhitespace = (value) => value.replace(/\s+/g, ' ').trim();
|
|
736
|
+
const normalizedRaw = normalizeWhitespace(rawValue);
|
|
737
|
+
const normalizedPrompt = normalizeWhitespace(promptText);
|
|
738
|
+
if (!normalizedPrompt || !normalizedRaw.startsWith(normalizedPrompt))
|
|
739
|
+
return false;
|
|
740
|
+
const remainder = normalizedRaw.slice(normalizedPrompt.length).trim();
|
|
741
|
+
if (!remainder)
|
|
742
|
+
return true;
|
|
743
|
+
return isLikelyDoubaoUiNoise(remainder) || isLikelyDoubaoUiNoise(sanitizedValue);
|
|
744
|
+
}
|
|
745
|
+
export function collectDoubaoTranscriptAdditions(beforeLines, currentLines, promptText, sanitize = (value) => value.trim()) {
|
|
746
|
+
const normalizedBefore = new Set(beforeLines.map((line) => sanitize(line)).filter(Boolean));
|
|
747
|
+
return currentLines
|
|
748
|
+
.filter((line) => !beforeLines.includes(line))
|
|
749
|
+
.map((line) => ({ raw: line, sanitized: sanitize(line) }))
|
|
750
|
+
.filter(({ raw, sanitized }) => sanitized
|
|
751
|
+
&& sanitized !== promptText
|
|
752
|
+
&& !normalizedBefore.has(sanitized)
|
|
753
|
+
&& !isAlwaysTranscriptUiNoise(sanitized)
|
|
754
|
+
&& !isLikelyTranscriptUiNoise(raw, sanitized, promptText))
|
|
755
|
+
.map(({ sanitized }) => sanitized)
|
|
756
|
+
.join('\n');
|
|
757
|
+
}
|
|
574
758
|
function getConversationListScript() {
|
|
575
759
|
return `
|
|
576
760
|
(() => {
|
|
@@ -888,6 +1072,11 @@ export async function triggerTranscriptDownload(page) {
|
|
|
888
1072
|
const btnResult = await page.evaluate(clickTranscriptDownloadBtnScript());
|
|
889
1073
|
return !btnResult.error;
|
|
890
1074
|
}
|
|
1075
|
+
export const __test__ = {
|
|
1076
|
+
clickSendButtonScript,
|
|
1077
|
+
composerStateScript,
|
|
1078
|
+
detectDoubaoVerificationScript,
|
|
1079
|
+
};
|
|
891
1080
|
export async function startNewDoubaoChat(page) {
|
|
892
1081
|
await ensureDoubaoChatPage(page);
|
|
893
1082
|
const clickedLabel = await page.evaluate(clickNewChatScript());
|