@gobing-ai/knowledge-kit 0.1.11 → 0.1.13
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/.claude-plugin/marketplace.json +1 -1
- package/dist/index.js +2372 -1722
- package/package.json +1 -1
- package/plugins/generations/image-gen/README.md +108 -0
- package/plugins/generations/image-gen/assets/sample/cover__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/cover__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/daily-news-cover__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/free__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/illustration__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__clean-webapp-ui.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__cute.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__daily-news.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__editorial.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__fresh.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__minimalist.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__photorealistic.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__sketch.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__technical-diagram.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__vibrant.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__warm.jpg +0 -0
- package/plugins/generations/image-gen/assets/sample/xhs-card__xhs-knowledge.jpg +0 -0
- package/plugins/generations/image-gen/dist/index.js +102 -58
- package/plugins/generations/image-gen/scripts/compose-e2e-artifact.ts +5 -21
- package/plugins/generations/image-gen/scripts/gen-samples.ts +208 -0
- package/plugins/generations/image-gen/src/compose/chromium.ts +2 -3
- package/plugins/generations/image-gen/src/presets.ts +6 -3
- package/plugins/generations/image-gen/src/providers/errors.ts +16 -1
- package/plugins/generations/image-gen/src/providers/minimax.ts +46 -17
- package/plugins/ingestions/web-search/src/index.ts +1 -1
- package/plugins/kk/README.md +18 -0
- package/plugins/kk/commands/image-extract.md +1 -0
- package/plugins/kk/commands/image-generate.md +1 -0
- package/plugins/kk/commands/tell-me.md +1 -0
- package/plugins/kk/commands/workflow-run.md +7 -1
- package/plugins/kk/decisions/extract/it-topics.yaml +24 -0
- package/plugins/kk/decisions/kk-itc.yaml +6 -4
- package/plugins/kk/plugin.json +1 -1
- package/plugins/kk/publish-presets/daily-ai-voice-migration.json +146 -0
- package/plugins/kk/publish-presets/daily-ai-voice.json +15 -7
- package/plugins/kk/schedules/kk-collect.grok-bot.md.tmpl +69 -0
- package/plugins/kk/schedules/kk-daily-ai-voice3.cron.tmpl +18 -0
- package/plugins/kk/schedules/kk-daily-publish3-cn-peak.cron.tmpl +15 -5
- package/plugins/kk/schedules/kk-daily-publish3-overseas.cron.tmpl +9 -4
- package/plugins/kk/scripts/daily-scheduled-run.ts +91 -41
- package/plugins/kk/scripts/daily-stages.ts +394 -53
- package/plugins/kk/scripts/kk-workflow-stages.ts +190 -0
- package/plugins/kk/scripts/safety-artifact-check.ts +70 -28
- package/plugins/kk/scripts/voice3-publish-window.ts +202 -0
- package/plugins/kk/skills/article-adapt/SKILL.md +1 -0
- package/plugins/kk/skills/audio-authoring/SKILL.md +1 -0
- package/plugins/kk/skills/content-judge/SKILL.md +1 -0
- package/plugins/kk/skills/content-safety/SKILL.md +1 -0
- package/plugins/kk/skills/content-safety/references/platforms/podcast-salty.md +41 -0
- package/plugins/kk/skills/content-safety/references/platforms/surfdash.md +36 -0
- package/plugins/kk/skills/content-safety/references/platforms/xhs.md +39 -0
- package/plugins/kk/skills/explain-things/SKILL.md +1 -0
- package/plugins/kk/skills/image-authoring/SKILL.md +1 -0
- package/plugins/kk/skills/itc-generating/SKILL.md +1 -0
- package/plugins/kk/skills/publish/SKILL.md +1 -0
- package/plugins/kk/skills/smart-seo/SKILL.md +1 -0
- package/plugins/kk/skills/storm-research/SKILL.md +1 -0
- package/plugins/kk/skills/taste-unslop/SKILL.md +1 -0
- package/plugins/kk/skills/topic/SKILL.md +1 -0
- package/plugins/kk/skills/web-automation/SKILL.md +1 -0
- package/plugins/kk/skills/xhs-images/SKILL.md +1 -0
- package/plugins/kk/workflows/kk-collect.yaml +3 -3
- package/plugins/kk/workflows/kk-daily-ai-voice2.yaml +21 -19
- package/plugins/kk/workflows/kk-daily-ai-voice3.yaml +163 -63
- package/plugins/kk/workflows/kk-daily-publish3.yaml +82 -26
- package/plugins/kk/workflows/kk-daily-safety.yaml +39 -31
- package/plugins/kk/workflows/kk-itc-author.yaml +75 -0
- package/plugins/kk/workflows/kk-itc-publish-tail.yaml +25 -3
- package/plugins/kk/workflows/kk-itc.yaml +36 -4
- package/plugins/publishings/linkedin-pub/dist/index.js +21333 -0
- package/plugins/publishings/linkedin-pub/dist/profile-check.js +21005 -0
- package/plugins/publishings/linkedin-pub/package.json +19 -0
- package/plugins/publishings/linkedin-pub/plugin.json +12 -0
- package/plugins/publishings/linkedin-pub/src/index.ts +705 -0
- package/plugins/publishings/linkedin-pub/src/profile-check.ts +17 -0
- package/plugins/publishings/linkedin-pub/tsconfig.json +4 -0
- package/plugins/publishings/surfdash-pub/dist/index.js +53 -24
- package/plugins/publishings/surfdash-pub/src/index.ts +72 -25
- package/plugins/publishings/threads-pub/dist/index.js +7 -0
- package/plugins/publishings/threads-pub/src/index.ts +12 -0
- package/plugins/publishings/wechatmp-pub/dist/index.js +176 -32
- package/plugins/publishings/wechatmp-pub/src/flow.ts +298 -52
- package/plugins/publishings/weibo-pub/dist/index.js +100 -7
- package/plugins/publishings/weibo-pub/src/index.ts +111 -11
- package/plugins/publishings/x-pub/dist/index.js +7 -0
- package/plugins/publishings/x-pub/src/index.ts +11 -0
- package/plugins/publishings/xhs-pub/dist/index.js +164 -12
- package/plugins/publishings/xhs-pub/dist/profile-check.js +11 -1
- package/plugins/publishings/xhs-pub/src/flow.ts +194 -23
- package/plugins/publishings/xhs-pub/src/page.ts +26 -0
|
@@ -20991,7 +20991,7 @@ function normalizeText(value) {
|
|
|
20991
20991
|
return value.replace(/\s+/g, "");
|
|
20992
20992
|
}
|
|
20993
20993
|
function excerpt(text) {
|
|
20994
|
-
return normalizeText(text).slice(0, EXCERPT_CHARS);
|
|
20994
|
+
return normalizeText(text.replace(/^[#>*`\-\s]+/gm, "")).slice(0, EXCERPT_CHARS);
|
|
20995
20995
|
}
|
|
20996
20996
|
function resolveOperation(options, content) {
|
|
20997
20997
|
const raw = options?.operation ?? content?.metadata?.operation ?? "post";
|
|
@@ -21118,6 +21118,9 @@ class WeiboBrowserTransport {
|
|
|
21118
21118
|
async runArticleFlow(page, content, publishStatus, coverPath, profileDir) {
|
|
21119
21119
|
const metadata = { operation: "article", driver: this.driver, publishStatus, profileDir };
|
|
21120
21120
|
const title = content.title ?? "Untitled";
|
|
21121
|
+
await page.evaluate(ensureArticleDraftScript());
|
|
21122
|
+
const editorUrl = page.url();
|
|
21123
|
+
const toastsBefore = await this.readToasts(page);
|
|
21121
21124
|
const titleSelector = await resolveSelector(page, WEIBO_ARTICLE_SELECTORS.title, {
|
|
21122
21125
|
timeoutMs: this.stepTimeoutMs,
|
|
21123
21126
|
label: "article title input"
|
|
@@ -21129,12 +21132,19 @@ class WeiboBrowserTransport {
|
|
|
21129
21132
|
});
|
|
21130
21133
|
await pasteHtml(page, bodySelector, markdownToHtml(content.body), { timeoutMs: this.stepTimeoutMs });
|
|
21131
21134
|
if (coverPath !== undefined) {
|
|
21135
|
+
const baseline = await this.countImages(page, bodySelector);
|
|
21136
|
+
const preAttached = (await trySelectors(page, WEIBO_ARTICLE_SELECTORS.fileInput, { visible: false })).found;
|
|
21137
|
+
if (!preAttached) {
|
|
21138
|
+
await page.evaluate(openCoverLibraryScript());
|
|
21139
|
+
}
|
|
21132
21140
|
const fileInputSelector = await resolveSelector(page, WEIBO_ARTICLE_SELECTORS.fileInput, {
|
|
21133
21141
|
timeoutMs: this.stepTimeoutMs,
|
|
21134
21142
|
label: "article cover file input"
|
|
21135
21143
|
});
|
|
21136
|
-
const baseline = await this.countImages(page, bodySelector);
|
|
21137
21144
|
await uploadFile(page, fileInputSelector, coverPath, { timeoutMs: this.stepTimeoutMs });
|
|
21145
|
+
if (!preAttached) {
|
|
21146
|
+
await page.evaluate(confirmCoverModalScript());
|
|
21147
|
+
}
|
|
21138
21148
|
const attached = await expectOutcome(() => this.probeCoverAttached(page, bodySelector, baseline), {
|
|
21139
21149
|
timeoutMs: this.stepTimeoutMs,
|
|
21140
21150
|
label: "weibo article cover attachment"
|
|
@@ -21148,8 +21158,6 @@ class WeiboBrowserTransport {
|
|
|
21148
21158
|
const bodyReadback = await this.requireReadback(page, bodySelector, content.body, "article body readback");
|
|
21149
21159
|
if (!bodyReadback.ok)
|
|
21150
21160
|
return fail(bodyReadback.error);
|
|
21151
|
-
const editorUrl = page.url();
|
|
21152
|
-
const toastsBefore = await this.readToasts(page);
|
|
21153
21161
|
if (publishStatus === "draft") {
|
|
21154
21162
|
const saveSelector = await resolveSelector(page, WEIBO_ARTICLE_SELECTORS.saveDraft, {
|
|
21155
21163
|
timeoutMs: this.stepTimeoutMs,
|
|
@@ -21277,7 +21285,10 @@ class WeiboBrowserTransport {
|
|
|
21277
21285
|
async probeFreshToast(page, toastsBefore) {
|
|
21278
21286
|
const before = new Set(toastsBefore);
|
|
21279
21287
|
const now = await this.readToasts(page);
|
|
21280
|
-
|
|
21288
|
+
const hit = now.find((text) => !before.has(text) && /\u4FDD\u5B58|\u8349\u7A3F|\u6210\u529F|\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}/.test(text));
|
|
21289
|
+
if (hit === undefined)
|
|
21290
|
+
return null;
|
|
21291
|
+
return /#\/draft\/\d+/.test(page.url()) ? page.url() : hit;
|
|
21281
21292
|
}
|
|
21282
21293
|
async probeArticleLive(page) {
|
|
21283
21294
|
return liveArticleUrl(page.url());
|
|
@@ -21400,12 +21411,94 @@ function coverCountScript(selector) {
|
|
|
21400
21411
|
return `(() => { // kk-weibo-cover
|
|
21401
21412
|
const anchor = document.querySelector(${JSON.stringify(selector)});
|
|
21402
21413
|
const scope = anchor === null ? document.body : anchor.closest('form, section, article, main') || document.body;
|
|
21403
|
-
return String(scope.querySelectorAll('img[src]').length);
|
|
21414
|
+
return String(Array.from(scope.querySelectorAll('img[src]')).filter((el) => !el.closest('.n-modal')).length);
|
|
21415
|
+
})()`;
|
|
21416
|
+
}
|
|
21417
|
+
function ensureArticleDraftScript() {
|
|
21418
|
+
return `(async () => { // kk-weibo-draft-init
|
|
21419
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
21420
|
+
const spin = () => document.querySelectorAll('.wb-editor-spin').length > 0;
|
|
21421
|
+
const titleEl = document.querySelector('input[placeholder*="\u6807\u9898"], textarea[placeholder*="\u6807\u9898"]');
|
|
21422
|
+
const titleVal = titleEl && titleEl.value ? String(titleEl.value).trim() : '';
|
|
21423
|
+
const hasCover = document.querySelectorAll('.cover-preview, img.cover-img').length > 0;
|
|
21424
|
+
if (spin() || titleVal.length > 0 || hasCover) {
|
|
21425
|
+
for (const btn of document.querySelectorAll('button')) {
|
|
21426
|
+
const t = (btn.textContent || '').trim();
|
|
21427
|
+
if (t === '\u5199\u6587\u7AE0' || t.includes('\u521B\u4F5C\u4E00\u7BC7\u65B0\u6587\u7AE0')) {
|
|
21428
|
+
btn.click();
|
|
21429
|
+
break;
|
|
21430
|
+
}
|
|
21431
|
+
}
|
|
21432
|
+
await sleep(600);
|
|
21433
|
+
for (let i = 0; i < 15 && spin(); i += 1) await sleep(200);
|
|
21434
|
+
}
|
|
21435
|
+
return true;
|
|
21436
|
+
})()`;
|
|
21437
|
+
}
|
|
21438
|
+
function openCoverLibraryScript() {
|
|
21439
|
+
return `(async () => { // kk-weibo-open-cover
|
|
21440
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
21441
|
+
let coverEmpty = null;
|
|
21442
|
+
for (let i = 0; i < 15; i += 1) {
|
|
21443
|
+
coverEmpty = document.querySelector('div.cover-empty, [class*="cover-empty"]');
|
|
21444
|
+
if (coverEmpty) break;
|
|
21445
|
+
await sleep(200);
|
|
21446
|
+
}
|
|
21447
|
+
if (coverEmpty instanceof HTMLElement) coverEmpty.click();
|
|
21448
|
+
for (let i = 0; i < 15 && !document.querySelector('.n-modal'); i += 1) await sleep(200);
|
|
21449
|
+
for (const tab of document.querySelectorAll('.n-modal .n-tabs-tab')) {
|
|
21450
|
+
if ((tab.textContent || '').trim() === '\u56FE\u7247\u5E93' && tab instanceof HTMLElement) {
|
|
21451
|
+
tab.click();
|
|
21452
|
+
}
|
|
21453
|
+
}
|
|
21454
|
+
for (let i = 0; i < 15 && !document.querySelector('input[type="file"]'); i += 1) await sleep(200);
|
|
21455
|
+
return true;
|
|
21456
|
+
})()`;
|
|
21457
|
+
}
|
|
21458
|
+
function confirmCoverModalScript() {
|
|
21459
|
+
return `(async () => { // kk-weibo-confirm-cover
|
|
21460
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
21461
|
+
if (!document.querySelector('.n-modal')) return true;
|
|
21462
|
+
await sleep(1500);
|
|
21463
|
+
let item = null;
|
|
21464
|
+
for (let i = 0; i < 25; i += 1) {
|
|
21465
|
+
item = document.querySelector('.n-modal .image-item, .n-modal img[src]');
|
|
21466
|
+
if (item) break;
|
|
21467
|
+
await sleep(200);
|
|
21468
|
+
}
|
|
21469
|
+
if (item instanceof HTMLElement && !item.classList.contains('is-selected')) {
|
|
21470
|
+
item.click();
|
|
21471
|
+
}
|
|
21472
|
+
for (let i = 0; i < 15; i += 1) {
|
|
21473
|
+
const nextBtn = Array.from(document.querySelectorAll('.n-modal button')).find(
|
|
21474
|
+
(b) => (b.textContent || '').trim() === '\u4E0B\u4E00\u6B65' && !b.disabled,
|
|
21475
|
+
);
|
|
21476
|
+
if (nextBtn) {
|
|
21477
|
+
nextBtn.click();
|
|
21478
|
+
break;
|
|
21479
|
+
}
|
|
21480
|
+
await sleep(200);
|
|
21481
|
+
}
|
|
21482
|
+
await sleep(1800);
|
|
21483
|
+
for (let i = 0; i < 15; i += 1) {
|
|
21484
|
+
const okBtn = Array.from(document.querySelectorAll('.n-modal button')).find(
|
|
21485
|
+
(b) => (b.textContent || '').trim() === '\u786E\u5B9A' && !b.disabled,
|
|
21486
|
+
);
|
|
21487
|
+
if (okBtn) {
|
|
21488
|
+
okBtn.click();
|
|
21489
|
+
break;
|
|
21490
|
+
}
|
|
21491
|
+
await sleep(200);
|
|
21492
|
+
}
|
|
21493
|
+
for (let i = 0; i < 25 && document.querySelector('.n-modal'); i += 1) {
|
|
21494
|
+
await sleep(200);
|
|
21495
|
+
}
|
|
21496
|
+
return true;
|
|
21404
21497
|
})()`;
|
|
21405
21498
|
}
|
|
21406
21499
|
function toastTextsScript() {
|
|
21407
21500
|
return `(() => { // kk-weibo-toasts
|
|
21408
|
-
const nodes = document.querySelectorAll('[class*="toast" i], [class*="Toast"], [class*="message" i], [role="alert"]');
|
|
21501
|
+
const nodes = document.querySelectorAll('[class*="toast" i], [class*="Toast"], [class*="message" i], [role="alert"], .list-item');
|
|
21409
21502
|
const texts = [];
|
|
21410
21503
|
for (const node of nodes) {
|
|
21411
21504
|
const text = (node.textContent || '').trim();
|
|
@@ -143,7 +143,7 @@ function normalizeText(value: string): string {
|
|
|
143
143
|
}
|
|
144
144
|
|
|
145
145
|
function excerpt(text: string): string {
|
|
146
|
-
return normalizeText(text).slice(0, EXCERPT_CHARS);
|
|
146
|
+
return normalizeText(text.replace(/^[#>*`\-\s]+/gm, '')).slice(0, EXCERPT_CHARS);
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/**
|
|
@@ -368,6 +368,10 @@ export class WeiboBrowserTransport implements PublishTransport {
|
|
|
368
368
|
const metadata = { operation: 'article', driver: this.driver, publishStatus, profileDir };
|
|
369
369
|
const title = content.title ?? 'Untitled';
|
|
370
370
|
|
|
371
|
+
await page.evaluate(ensureArticleDraftScript());
|
|
372
|
+
const editorUrl = page.url();
|
|
373
|
+
const toastsBefore = await this.readToasts(page);
|
|
374
|
+
|
|
371
375
|
const titleSelector = await resolveSelector(page, WEIBO_ARTICLE_SELECTORS.title, {
|
|
372
376
|
timeoutMs: this.stepTimeoutMs,
|
|
373
377
|
label: 'article title input',
|
|
@@ -382,12 +386,19 @@ export class WeiboBrowserTransport implements PublishTransport {
|
|
|
382
386
|
await pasteHtml(page, bodySelector, markdownToHtml(content.body), { timeoutMs: this.stepTimeoutMs });
|
|
383
387
|
|
|
384
388
|
if (coverPath !== undefined) {
|
|
389
|
+
const baseline = await this.countImages(page, bodySelector);
|
|
390
|
+
const preAttached = (await trySelectors(page, WEIBO_ARTICLE_SELECTORS.fileInput, { visible: false })).found;
|
|
391
|
+
if (!preAttached) {
|
|
392
|
+
await page.evaluate(openCoverLibraryScript());
|
|
393
|
+
}
|
|
385
394
|
const fileInputSelector = await resolveSelector(page, WEIBO_ARTICLE_SELECTORS.fileInput, {
|
|
386
395
|
timeoutMs: this.stepTimeoutMs,
|
|
387
396
|
label: 'article cover file input',
|
|
388
397
|
});
|
|
389
|
-
const baseline = await this.countImages(page, bodySelector);
|
|
390
398
|
await uploadFile(page, fileInputSelector, coverPath, { timeoutMs: this.stepTimeoutMs });
|
|
399
|
+
if (!preAttached) {
|
|
400
|
+
await page.evaluate(confirmCoverModalScript());
|
|
401
|
+
}
|
|
391
402
|
// A requested cover needs a fresh observed attachment, not a successful upload call.
|
|
392
403
|
const attached = await expectOutcome(() => this.probeCoverAttached(page, bodySelector, baseline), {
|
|
393
404
|
timeoutMs: this.stepTimeoutMs,
|
|
@@ -403,9 +414,6 @@ export class WeiboBrowserTransport implements PublishTransport {
|
|
|
403
414
|
const bodyReadback = await this.requireReadback(page, bodySelector, content.body, 'article body readback');
|
|
404
415
|
if (!bodyReadback.ok) return fail(bodyReadback.error);
|
|
405
416
|
|
|
406
|
-
const editorUrl = page.url();
|
|
407
|
-
const toastsBefore = await this.readToasts(page);
|
|
408
|
-
|
|
409
417
|
if (publishStatus === 'draft') {
|
|
410
418
|
const saveSelector = await resolveSelector(page, WEIBO_ARTICLE_SELECTORS.saveDraft, {
|
|
411
419
|
timeoutMs: this.stepTimeoutMs,
|
|
@@ -590,11 +598,15 @@ export class WeiboBrowserTransport implements PublishTransport {
|
|
|
590
598
|
return parts.join('; ');
|
|
591
599
|
}
|
|
592
600
|
|
|
593
|
-
/** A toast carrying a save/success
|
|
601
|
+
/** A toast or v5 draft-list item carrying a save/success/timestamp signal not on screen before the flow. */
|
|
594
602
|
private async probeFreshToast(page: Page, toastsBefore: readonly string[]): Promise<string | null> {
|
|
595
603
|
const before = new Set(toastsBefore);
|
|
596
604
|
const now = await this.readToasts(page);
|
|
597
|
-
|
|
605
|
+
const hit = now.find(
|
|
606
|
+
(text) => !before.has(text) && /保存|草稿|成功|\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}/.test(text),
|
|
607
|
+
);
|
|
608
|
+
if (hit === undefined) return null;
|
|
609
|
+
return /#\/draft\/\d+/.test(page.url()) ? page.url() : hit;
|
|
598
610
|
}
|
|
599
611
|
|
|
600
612
|
/** Live article evidence: the browser left the editor for a published article URL. */
|
|
@@ -751,19 +763,107 @@ function readbackScript(selector: string): string {
|
|
|
751
763
|
})()`;
|
|
752
764
|
}
|
|
753
765
|
|
|
754
|
-
/** Counts rendered images in the editor's nearest container, for the cover baseline. */
|
|
766
|
+
/** Counts rendered images in the editor's nearest container (excluding modal portals), for the cover baseline. */
|
|
755
767
|
function coverCountScript(selector: string): string {
|
|
756
768
|
return `(() => { // kk-weibo-cover
|
|
757
769
|
const anchor = document.querySelector(${JSON.stringify(selector)});
|
|
758
770
|
const scope = anchor === null ? document.body : anchor.closest('form, section, article, main') || document.body;
|
|
759
|
-
return String(scope.querySelectorAll('img[src]').length);
|
|
771
|
+
return String(Array.from(scope.querySelectorAll('img[src]')).filter((el) => !el.closest('.n-modal')).length);
|
|
772
|
+
})()`;
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/** Ensures the v5 article editor has an active, blank draft pane before filling title/body. */
|
|
776
|
+
function ensureArticleDraftScript(): string {
|
|
777
|
+
return `(async () => { // kk-weibo-draft-init
|
|
778
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
779
|
+
const spin = () => document.querySelectorAll('.wb-editor-spin').length > 0;
|
|
780
|
+
const titleEl = document.querySelector('input[placeholder*="标题"], textarea[placeholder*="标题"]');
|
|
781
|
+
const titleVal = titleEl && titleEl.value ? String(titleEl.value).trim() : '';
|
|
782
|
+
const hasCover = document.querySelectorAll('.cover-preview, img.cover-img').length > 0;
|
|
783
|
+
if (spin() || titleVal.length > 0 || hasCover) {
|
|
784
|
+
for (const btn of document.querySelectorAll('button')) {
|
|
785
|
+
const t = (btn.textContent || '').trim();
|
|
786
|
+
if (t === '写文章' || t.includes('创作一篇新文章')) {
|
|
787
|
+
btn.click();
|
|
788
|
+
break;
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
await sleep(600);
|
|
792
|
+
for (let i = 0; i < 15 && spin(); i += 1) await sleep(200);
|
|
793
|
+
}
|
|
794
|
+
return true;
|
|
795
|
+
})()`;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
/** Opens the v5 cover modal (`div.cover-empty`) and switches to the `图片库` tab so `input[type="file"]` mounts. */
|
|
799
|
+
function openCoverLibraryScript(): string {
|
|
800
|
+
return `(async () => { // kk-weibo-open-cover
|
|
801
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
802
|
+
let coverEmpty = null;
|
|
803
|
+
for (let i = 0; i < 15; i += 1) {
|
|
804
|
+
coverEmpty = document.querySelector('div.cover-empty, [class*="cover-empty"]');
|
|
805
|
+
if (coverEmpty) break;
|
|
806
|
+
await sleep(200);
|
|
807
|
+
}
|
|
808
|
+
if (coverEmpty instanceof HTMLElement) coverEmpty.click();
|
|
809
|
+
for (let i = 0; i < 15 && !document.querySelector('.n-modal'); i += 1) await sleep(200);
|
|
810
|
+
for (const tab of document.querySelectorAll('.n-modal .n-tabs-tab')) {
|
|
811
|
+
if ((tab.textContent || '').trim() === '图片库' && tab instanceof HTMLElement) {
|
|
812
|
+
tab.click();
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
for (let i = 0; i < 15 && !document.querySelector('input[type="file"]'); i += 1) await sleep(200);
|
|
816
|
+
return true;
|
|
817
|
+
})()`;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
/** Selects the uploaded image in the v5 `.n-modal` library, advances through crop (`下一步` → `确定`), and waits for the modal to close. */
|
|
821
|
+
function confirmCoverModalScript(): string {
|
|
822
|
+
return `(async () => { // kk-weibo-confirm-cover
|
|
823
|
+
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
824
|
+
if (!document.querySelector('.n-modal')) return true;
|
|
825
|
+
await sleep(1500);
|
|
826
|
+
let item = null;
|
|
827
|
+
for (let i = 0; i < 25; i += 1) {
|
|
828
|
+
item = document.querySelector('.n-modal .image-item, .n-modal img[src]');
|
|
829
|
+
if (item) break;
|
|
830
|
+
await sleep(200);
|
|
831
|
+
}
|
|
832
|
+
if (item instanceof HTMLElement && !item.classList.contains('is-selected')) {
|
|
833
|
+
item.click();
|
|
834
|
+
}
|
|
835
|
+
for (let i = 0; i < 15; i += 1) {
|
|
836
|
+
const nextBtn = Array.from(document.querySelectorAll('.n-modal button')).find(
|
|
837
|
+
(b) => (b.textContent || '').trim() === '下一步' && !b.disabled,
|
|
838
|
+
);
|
|
839
|
+
if (nextBtn) {
|
|
840
|
+
nextBtn.click();
|
|
841
|
+
break;
|
|
842
|
+
}
|
|
843
|
+
await sleep(200);
|
|
844
|
+
}
|
|
845
|
+
await sleep(1800);
|
|
846
|
+
for (let i = 0; i < 15; i += 1) {
|
|
847
|
+
const okBtn = Array.from(document.querySelectorAll('.n-modal button')).find(
|
|
848
|
+
(b) => (b.textContent || '').trim() === '确定' && !b.disabled,
|
|
849
|
+
);
|
|
850
|
+
if (okBtn) {
|
|
851
|
+
okBtn.click();
|
|
852
|
+
break;
|
|
853
|
+
}
|
|
854
|
+
await sleep(200);
|
|
855
|
+
}
|
|
856
|
+
for (let i = 0; i < 25 && document.querySelector('.n-modal'); i += 1) {
|
|
857
|
+
await sleep(200);
|
|
858
|
+
}
|
|
859
|
+
return true;
|
|
760
860
|
})()`;
|
|
761
861
|
}
|
|
762
862
|
|
|
763
|
-
/** Visible toast texts, used as the pre-save baseline and as the fresh-signal read. */
|
|
863
|
+
/** Visible toast texts and v5 draft sidebar items, used as the pre-save baseline and as the fresh-signal read. */
|
|
764
864
|
function toastTextsScript(): string {
|
|
765
865
|
return `(() => { // kk-weibo-toasts
|
|
766
|
-
const nodes = document.querySelectorAll('[class*="toast" i], [class*="Toast"], [class*="message" i], [role="alert"]');
|
|
866
|
+
const nodes = document.querySelectorAll('[class*="toast" i], [class*="Toast"], [class*="message" i], [role="alert"], .list-item');
|
|
767
867
|
const texts = [];
|
|
768
868
|
for (const node of nodes) {
|
|
769
869
|
const text = (node.textContent || '').trim();
|
|
@@ -21008,6 +21008,13 @@ function resolveOperation(options, content) {
|
|
|
21008
21008
|
class XPostApiTransport {
|
|
21009
21009
|
kind = "file-cli";
|
|
21010
21010
|
async publish(payload) {
|
|
21011
|
+
const requestedStatus = payload.content.metadata?.publishStatus;
|
|
21012
|
+
if (requestedStatus === "draft") {
|
|
21013
|
+
return fail("x post has no draft: the post API publishes immediately \u2014 plan this target dummy/off or launch it live");
|
|
21014
|
+
}
|
|
21015
|
+
if (requestedStatus !== undefined && requestedStatus !== "public") {
|
|
21016
|
+
return fail(`unknown publishStatus '${String(requestedStatus)}' (expected absent, 'draft' or 'public')`);
|
|
21017
|
+
}
|
|
21011
21018
|
const missing = TOKEN_ENVS.filter((key) => {
|
|
21012
21019
|
const value = process.env[key];
|
|
21013
21020
|
return !value || value.trim().length === 0;
|
|
@@ -105,6 +105,17 @@ export class XPostApiTransport implements PublishTransport {
|
|
|
105
105
|
public readonly kind = 'file-cli';
|
|
106
106
|
|
|
107
107
|
public async publish(payload: PublishTransportPayload): Promise<Result> {
|
|
108
|
+
// 0330 R2: the post API publishes immediately — there is no draft. Intercept before the
|
|
109
|
+
// credential lookup so a planned draft never touches X, and fail closed on unknown modes.
|
|
110
|
+
const requestedStatus = payload.content.metadata?.publishStatus;
|
|
111
|
+
if (requestedStatus === 'draft') {
|
|
112
|
+
return fail(
|
|
113
|
+
'x post has no draft: the post API publishes immediately — plan this target dummy/off or launch it live',
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
if (requestedStatus !== undefined && requestedStatus !== 'public') {
|
|
117
|
+
return fail(`unknown publishStatus '${String(requestedStatus)}' (expected absent, 'draft' or 'public')`);
|
|
118
|
+
}
|
|
108
119
|
const missing = TOKEN_ENVS.filter((key) => {
|
|
109
120
|
const value = process.env[key];
|
|
110
121
|
return !value || value.trim().length === 0;
|
|
@@ -21342,6 +21342,7 @@ function wrapPlaywrightPage(raw, shadow) {
|
|
|
21342
21342
|
isVisible: (options) => loc.isVisible(options),
|
|
21343
21343
|
fill: (text, options) => loc.fill(text, options),
|
|
21344
21344
|
click: (options) => loc.click(options),
|
|
21345
|
+
boundingBox: async () => (await onScreen(selector)).at(-1)?.box ?? await loc.boundingBox?.() ?? null,
|
|
21345
21346
|
type: (text, options) => loc.pressSequentially(text, options),
|
|
21346
21347
|
textContent: () => loc.textContent()
|
|
21347
21348
|
};
|
|
@@ -21360,7 +21361,16 @@ function wrapPlaywrightPage(raw, shadow) {
|
|
|
21360
21361
|
return true;
|
|
21361
21362
|
},
|
|
21362
21363
|
findShadowControl: async (labels) => shadow ? shadow.findControl(labels) : null,
|
|
21363
|
-
clickAt: (x, y) => pw.mouse.click(x, y)
|
|
21364
|
+
clickAt: (x, y) => pw.mouse.click(x, y),
|
|
21365
|
+
waitForEvent: (event, options) => {
|
|
21366
|
+
if (typeof pw.waitForEvent !== "function") {
|
|
21367
|
+
return Promise.reject(new Error(`the page seam does not expose a '${event}' event source`));
|
|
21368
|
+
}
|
|
21369
|
+
return pw.waitForEvent(event, options);
|
|
21370
|
+
},
|
|
21371
|
+
mouse: {
|
|
21372
|
+
click: (x, y) => pw.mouse.click(x, y)
|
|
21373
|
+
}
|
|
21364
21374
|
};
|
|
21365
21375
|
}
|
|
21366
21376
|
async function findSelector(page, selectors, what, timeoutMs = STEP_TIMEOUT_MS) {
|
|
@@ -21412,8 +21422,9 @@ var ARTICLE_LAYOUT = ['button:has-text("\u4E00\u952E\u6392\u7248")', "div.ailayo
|
|
|
21412
21422
|
var PODCAST_OPEN = ["text=\u4E0A\u4F20\u97F3\u9891", 'button:has-text("\u4E0A\u4F20\u97F3\u9891")'];
|
|
21413
21423
|
var PODCAST_MODAL = ["div.creator-modal-sty", ".d-modal.d-modal-centered", ".d-modal"];
|
|
21414
21424
|
var PODCAST_AUDIO_INPUT = ['input[type="file"][accept*=".mp3"]', 'input[type="file"][accept*=".wav"]'];
|
|
21415
|
-
var PODCAST_COVER_INPUT = ['input[type="file"][accept*=".jpg"]', 'input[type="file"][accept*=".png"]'];
|
|
21416
21425
|
var PODCAST_MODAL_ADVANCE = ["\u53BB\u53D1\u5E03"];
|
|
21426
|
+
var PODCAST_COVER_ZONE = '.d-modal div:has-text("\u8BBE\u7F6E\u5C01\u9762")';
|
|
21427
|
+
var PODCAST_UPLOAD_ACK = "\u4E0A\u4F20\u6210\u529F";
|
|
21417
21428
|
var DRAFT_LABELS = ["\u6682\u5B58\u79BB\u5F00", "\u5B58\u8349\u7A3F", "\u4FDD\u5B58\u8349\u7A3F"];
|
|
21418
21429
|
var PUBLISH_LABELS = ["\u53D1\u5E03"];
|
|
21419
21430
|
var PUBLISH_CONFIRM = ["\u53D1\u5E03\u6210\u529F", "\u5DF2\u53D1\u5E03"];
|
|
@@ -21589,18 +21600,67 @@ async function composeArticle(deps) {
|
|
|
21589
21600
|
async function isPodcastModalOpen(page) {
|
|
21590
21601
|
return trySelectors(page, PODCAST_MODAL).then((found) => found.found).catch(() => false);
|
|
21591
21602
|
}
|
|
21603
|
+
async function clickPodcastModalDomFallback(page) {
|
|
21604
|
+
const res = await page.evaluate(`(() => {
|
|
21605
|
+
const modals = Array.from(document.querySelectorAll('.d-modal, div.creator-modal-sty'));
|
|
21606
|
+
const buttons = Array.from(document.querySelectorAll('.d-modal button, div.creator-modal-sty button, .d-modal-mask button'));
|
|
21607
|
+
const confirmLabels = ['\u786E\u5B9A', '\u5B8C\u6210', '\u786E\u8BA4', '\u4FDD\u5B58'];
|
|
21608
|
+
const cropBtn = buttons.find((b) => {
|
|
21609
|
+
const t = (b.textContent || '').trim();
|
|
21610
|
+
return confirmLabels.includes(t) && !b.classList.contains('cancel-btn') && !b.dataset.kkClicked;
|
|
21611
|
+
}) || (modals.length > 1 ? Array.from(modals[modals.length - 1].querySelectorAll('button')).find((b) => !b.classList.contains('cancel-btn') && b.classList.contains('bg-red') && !(b.textContent || '').includes('\u53BB\u53D1\u5E03') && !b.dataset.kkClicked) : null);
|
|
21612
|
+
if (cropBtn && typeof cropBtn.click === 'function') {
|
|
21613
|
+
cropBtn.dataset.kkClicked = '1';
|
|
21614
|
+
cropBtn.click();
|
|
21615
|
+
return JSON.stringify({ modals: modals.length, clickedCrop: true, clickedAdvance: false });
|
|
21616
|
+
}
|
|
21617
|
+
const advBtns = buttons.filter((b) => (b.textContent || '').includes('\u53BB\u53D1\u5E03') && !b.hasAttribute('disabled') && b.getAttribute('aria-disabled') !== 'true');
|
|
21618
|
+
const adv = advBtns[advBtns.length - 1];
|
|
21619
|
+
let clickedAdvance = false;
|
|
21620
|
+
if (adv && !adv.dataset.kkClicked && typeof adv.click === 'function') {
|
|
21621
|
+
adv.dataset.kkClicked = '1';
|
|
21622
|
+
adv.click();
|
|
21623
|
+
clickedAdvance = true;
|
|
21624
|
+
}
|
|
21625
|
+
return JSON.stringify({
|
|
21626
|
+
modals: modals.length,
|
|
21627
|
+
clickedCrop: false,
|
|
21628
|
+
clickedAdvance,
|
|
21629
|
+
buttons: buttons.map((b) => ({ text: (b.textContent || '').trim(), cls: b.className, disabled: b.hasAttribute('disabled') })),
|
|
21630
|
+
});
|
|
21631
|
+
})()`).catch(() => "");
|
|
21632
|
+
return typeof res === "string" ? res : "";
|
|
21633
|
+
}
|
|
21592
21634
|
async function leavePodcastModal(deps) {
|
|
21593
21635
|
const closed = () => isPodcastModalOpen(deps.page).then((open) => !open);
|
|
21594
|
-
if (await
|
|
21636
|
+
if (!await isPodcastModalOpen(deps.page))
|
|
21637
|
+
return;
|
|
21638
|
+
let usedFallback = false;
|
|
21639
|
+
let lastDiag = "";
|
|
21640
|
+
try {
|
|
21641
|
+
const advance = await clickLightControl(deps.page, PODCAST_MODAL_ADVANCE, Math.min(deps.stepTimeoutMs, 2500), "podcast modal advance");
|
|
21642
|
+
if (!advance.ok) {
|
|
21643
|
+
lastDiag = await clickPodcastModalDomFallback(deps.page);
|
|
21644
|
+
if (!lastDiag.startsWith("{"))
|
|
21645
|
+
throw new Error(advance.error);
|
|
21646
|
+
usedFallback = true;
|
|
21647
|
+
}
|
|
21648
|
+
} catch {
|
|
21649
|
+
lastDiag = await clickPodcastModalDomFallback(deps.page);
|
|
21650
|
+
usedFallback = true;
|
|
21651
|
+
}
|
|
21652
|
+
if (await waitUntil(async () => {
|
|
21653
|
+
if (await closed())
|
|
21654
|
+
return true;
|
|
21655
|
+
if (usedFallback) {
|
|
21656
|
+
lastDiag = await clickPodcastModalDomFallback(deps.page);
|
|
21657
|
+
}
|
|
21658
|
+
return false;
|
|
21659
|
+
}, deps.stepTimeoutMs)) {
|
|
21595
21660
|
return;
|
|
21596
|
-
if (deps.publishStatus === "public") {
|
|
21597
|
-
const advance = await clickLightControl(deps.page, PODCAST_MODAL_ADVANCE, deps.stepTimeoutMs, "podcast modal advance");
|
|
21598
|
-
if (!advance.ok)
|
|
21599
|
-
throw new Error(advance.error);
|
|
21600
|
-
if (await waitUntil(closed, deps.stepTimeoutMs))
|
|
21601
|
-
return;
|
|
21602
21661
|
}
|
|
21603
|
-
|
|
21662
|
+
const diagSuffix = lastDiag ? ` [modal=${lastDiag}]` : "";
|
|
21663
|
+
throw new Error(`podcast modal: the \u53D1\u64AD\u5BA2 modal stayed open ${deps.stepTimeoutMs}ms after the advance click; the measured 2026-11-06 flow moves to the podcast editor page with the closed-shadow '${DRAFT_LABELS[0]}' bar, so a fast re-measure with a fresh probe is the next step, not a retry${diagSuffix}`);
|
|
21604
21664
|
}
|
|
21605
21665
|
async function composePodcast(input, deps) {
|
|
21606
21666
|
await enterMode(deps, input.operation);
|
|
@@ -21609,10 +21669,102 @@ async function composePodcast(input, deps) {
|
|
|
21609
21669
|
await findSelector(deps.page, PODCAST_MODAL, "podcast modal", deps.stepTimeoutMs);
|
|
21610
21670
|
const audioInput = await findAttachedSelector(deps.page, PODCAST_AUDIO_INPUT, "podcast audio input", deps.stepTimeoutMs);
|
|
21611
21671
|
await uploadFile(asPlaywrightPage(deps.page), audioInput, input.audio, { timeoutMs: deps.stepTimeoutMs });
|
|
21612
|
-
const
|
|
21613
|
-
|
|
21672
|
+
const ack = await waitUntil(async () => {
|
|
21673
|
+
try {
|
|
21674
|
+
return await deps.page.locator(`text=${PODCAST_UPLOAD_ACK}`).count() > 0;
|
|
21675
|
+
} catch {
|
|
21676
|
+
return false;
|
|
21677
|
+
}
|
|
21678
|
+
}, deps.stepTimeoutMs);
|
|
21679
|
+
if (!ack) {
|
|
21680
|
+
throw new Error(`podcast modal: the audio card never showed '${PODCAST_UPLOAD_ACK}' within ${deps.stepTimeoutMs}ms of the file assignment; mimic-file upload of ${input.audio.split("/").pop()} did not register`);
|
|
21681
|
+
}
|
|
21682
|
+
await uploadPodcastCover(deps, input.cover);
|
|
21683
|
+
await podcastAdvanceReady(deps);
|
|
21614
21684
|
await leavePodcastModal(deps);
|
|
21615
21685
|
}
|
|
21686
|
+
async function uploadPodcastCover(deps, cover) {
|
|
21687
|
+
const zone = deps.page.locator(PODCAST_COVER_ZONE).first();
|
|
21688
|
+
const box = await zone.boundingBox().catch(() => null);
|
|
21689
|
+
if (!box) {
|
|
21690
|
+
throw new Error(`podcast modal: the cover card ('${PODCAST_COVER_ZONE}') has no bounding box to click`);
|
|
21691
|
+
}
|
|
21692
|
+
if (!deps.page.mouse) {
|
|
21693
|
+
throw new Error("podcast modal: the page seam does not expose a raw mouse for the cover-card coordinate click");
|
|
21694
|
+
}
|
|
21695
|
+
const chooser = deps.page.waitForEvent?.("filechooser", { timeout: 8000 }).catch(() => null);
|
|
21696
|
+
await deps.page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
|
|
21697
|
+
const pending = Symbol("pending");
|
|
21698
|
+
const quick = await Promise.race([
|
|
21699
|
+
chooser,
|
|
21700
|
+
new Promise((resolve) => setTimeout(() => resolve(pending), 400))
|
|
21701
|
+
]);
|
|
21702
|
+
let diag = "";
|
|
21703
|
+
if (quick === pending) {
|
|
21704
|
+
const fallback = await deps.page.evaluate(`(() => {
|
|
21705
|
+
const modal = document.querySelector('.d-modal') || document.body;
|
|
21706
|
+
const inputs = Array.from(modal.querySelectorAll('input[type="file"]'));
|
|
21707
|
+
const coverInput = inputs.find((i) => !String(i.getAttribute('accept') || '').includes('.mp3'));
|
|
21708
|
+
const all = Array.from(modal.querySelectorAll('*')).filter((el) => (el.textContent || '').includes('\u8BBE\u7F6E\u5C01\u9762'));
|
|
21709
|
+
const leaf = all[all.length - 1];
|
|
21710
|
+
let card = leaf;
|
|
21711
|
+
while (card && card.parentElement && card.parentElement !== modal) {
|
|
21712
|
+
const pt = card.parentElement.textContent || '';
|
|
21713
|
+
if (pt.includes('\u4E0A\u4F20\u6210\u529F') || pt.includes('\u70B9\u51FB\u4E0A\u4F20') || pt.includes('\u53BB\u53D1\u5E03')) break;
|
|
21714
|
+
card = card.parentElement;
|
|
21715
|
+
}
|
|
21716
|
+
let clickedInput = false;
|
|
21717
|
+
if (coverInput && typeof coverInput.click === 'function') {
|
|
21718
|
+
coverInput.click();
|
|
21719
|
+
clickedInput = true;
|
|
21720
|
+
} else {
|
|
21721
|
+
if (card && typeof card.click === 'function') card.click();
|
|
21722
|
+
if (leaf && typeof leaf.click === 'function') leaf.click();
|
|
21723
|
+
}
|
|
21724
|
+
const rect = card ? card.getBoundingClientRect() : null;
|
|
21725
|
+
return JSON.stringify({
|
|
21726
|
+
clickedInput,
|
|
21727
|
+
x: rect ? Math.round(rect.left + rect.width / 2) : null,
|
|
21728
|
+
y: rect ? Math.round(rect.top + rect.height * 0.35) : null,
|
|
21729
|
+
inputs: inputs.map((i) => ({ accept: i.getAttribute('accept'), cls: i.className })),
|
|
21730
|
+
cardTag: card ? card.tagName + '.' + String(card.className) : null,
|
|
21731
|
+
cardRect: rect ? [Math.round(rect.left), Math.round(rect.top), Math.round(rect.width), Math.round(rect.height)] : null,
|
|
21732
|
+
});
|
|
21733
|
+
})()`).catch(() => "");
|
|
21734
|
+
if (typeof fallback === "string" && fallback.startsWith("{")) {
|
|
21735
|
+
diag = ` [modal=${fallback}]`;
|
|
21736
|
+
try {
|
|
21737
|
+
const parsed = JSON.parse(fallback);
|
|
21738
|
+
if (!parsed.clickedInput && typeof parsed.x === "number" && typeof parsed.y === "number") {
|
|
21739
|
+
await deps.page.mouse.click(parsed.x, parsed.y);
|
|
21740
|
+
}
|
|
21741
|
+
} catch {}
|
|
21742
|
+
}
|
|
21743
|
+
}
|
|
21744
|
+
const opened = quick === pending ? await chooser : quick;
|
|
21745
|
+
if (!opened) {
|
|
21746
|
+
throw new Error(`podcast modal: clicking the cover card did not open a native file chooser within 8000ms${diag}`);
|
|
21747
|
+
}
|
|
21748
|
+
await opened.setFiles(cover);
|
|
21749
|
+
}
|
|
21750
|
+
async function podcastAdvanceReady(deps) {
|
|
21751
|
+
if (deps.publishStatus !== "draft") {
|
|
21752
|
+
return;
|
|
21753
|
+
}
|
|
21754
|
+
const enabled = await waitUntil(async () => {
|
|
21755
|
+
try {
|
|
21756
|
+
if (await deps.page.locator('button:has-text("\u53BB\u53D1\u5E03")').count() === 0)
|
|
21757
|
+
return false;
|
|
21758
|
+
const disabled = await deps.page.locator('button:has-text("\u53BB\u53D1\u5E03")[disabled], button:has-text("\u53BB\u53D1\u5E03")[aria-disabled="true"]').count();
|
|
21759
|
+
return disabled === 0;
|
|
21760
|
+
} catch {
|
|
21761
|
+
return false;
|
|
21762
|
+
}
|
|
21763
|
+
}, deps.stepTimeoutMs);
|
|
21764
|
+
if (!enabled) {
|
|
21765
|
+
throw new Error(`podcast modal: '${PODCAST_MODAL_ADVANCE[0]}' stayed disabled \u2014 the cards were not both accepted; check the audio ack '${PODCAST_UPLOAD_ACK}' and retry with the cover chooser path`);
|
|
21766
|
+
}
|
|
21767
|
+
}
|
|
21616
21768
|
async function compose(input, deps) {
|
|
21617
21769
|
switch (input.operation) {
|
|
21618
21770
|
case "\u4E0A\u4F20\u56FE\u6587":
|
|
@@ -20996,6 +20996,7 @@ function wrapPlaywrightPage(raw, shadow) {
|
|
|
20996
20996
|
isVisible: (options) => loc.isVisible(options),
|
|
20997
20997
|
fill: (text, options) => loc.fill(text, options),
|
|
20998
20998
|
click: (options) => loc.click(options),
|
|
20999
|
+
boundingBox: async () => (await onScreen(selector)).at(-1)?.box ?? await loc.boundingBox?.() ?? null,
|
|
20999
21000
|
type: (text, options) => loc.pressSequentially(text, options),
|
|
21000
21001
|
textContent: () => loc.textContent()
|
|
21001
21002
|
};
|
|
@@ -21014,7 +21015,16 @@ function wrapPlaywrightPage(raw, shadow) {
|
|
|
21014
21015
|
return true;
|
|
21015
21016
|
},
|
|
21016
21017
|
findShadowControl: async (labels) => shadow ? shadow.findControl(labels) : null,
|
|
21017
|
-
clickAt: (x, y) => pw.mouse.click(x, y)
|
|
21018
|
+
clickAt: (x, y) => pw.mouse.click(x, y),
|
|
21019
|
+
waitForEvent: (event, options) => {
|
|
21020
|
+
if (typeof pw.waitForEvent !== "function") {
|
|
21021
|
+
return Promise.reject(new Error(`the page seam does not expose a '${event}' event source`));
|
|
21022
|
+
}
|
|
21023
|
+
return pw.waitForEvent(event, options);
|
|
21024
|
+
},
|
|
21025
|
+
mouse: {
|
|
21026
|
+
click: (x, y) => pw.mouse.click(x, y)
|
|
21027
|
+
}
|
|
21018
21028
|
};
|
|
21019
21029
|
}
|
|
21020
21030
|
|