publishport-opencli 1.0.10 → 1.0.11
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/clis/xiaohongshu/publish.js +30 -34
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionError as
|
|
1
|
+
import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionError as G,ArgumentError as U}from"@jackwener/opencli/errors";import{cli as Wz,Strategy as Fz}from"@jackwener/opencli/registry";import{assertLiteralContent as Oz}from"../_shared/content-guard.js";import{setInputFiles as Uz}from"../_shared/file-inject.js";import{humanType as x,humanWait as N,humanMoveTo as Bz,elementViewportCenter as Dz}from"./human.js";const vz="https://creator.xiaohongshu.com/publish/publish?from=menu_left&target=image",n=9,l=20,i=3000,o="|||",I="基础",s=[["基础","默认兜底,万能"],["边框","金句/要点卡"],["备忘","提醒/随手记"],["清新","日常/清单贴士"],["涂写","随笔/碎碎念"],["便签","笔记/提醒"],["光影","情绪/文艺"],["涂鸦","趣味/童话"],["简约","干货/观点"],["手写","日记/情感"],["插图","生活方式/轻松话题"],["美漫","活力/趣味/故事感"],["弥散","弥散光氛围"],["柔和","柔和/温柔金句"],["印刷","印刷海报/排版"],["科技","科技/产品"],["贺卡","节日祝福"],["札记","艺术/水彩氛围"],["书摘","书摘/引用"],["手帐","手帐拼贴"],["几何","醒目/有力主张"]],tz=s.map(([z])=>z),E="文字配图",g="再写一张",r="生成图片",p="下一步",u=".tiptap.ProseMirror",Pz=["_onPublish","onPublish","_onSubmit","_handlePublish"],kz=["_onSave","_onSaveDraft","_onDraft"],m=['[contenteditable="true"][placeholder*="标题"]','[contenteditable="true"][placeholder*="赞"]','input[placeholder*="标题"]','input[placeholder*="title" i]','[contenteditable="true"][class*="title"]','input[maxlength="20"]','input[class*="title"]',".title-input input",".note-title input","input[maxlength]"],t=['[contenteditable="true"][class*="content"]','[contenteditable="true"][class*="editor"]','[contenteditable="true"][placeholder*="描述"]','[contenteditable="true"][placeholder*="正文"]','[contenteditable="true"][placeholder*="内容"]','.note-content [contenteditable="true"]','.editor-content [contenteditable="true"]','[contenteditable="true"]:not([placeholder*="标题"]):not([placeholder*="赞"]):not([placeholder*="title" i])'],Nz={".jpg":"image/jpeg",".jpeg":"image/jpeg",".png":"image/png",".gif":"image/gif",".webp":"image/webp"};function W(z){if(z&&typeof z==="object"&&typeof z.session==="string"&&Object.prototype.hasOwnProperty.call(z,"data"))return z.data;return z}function Iz(z){return z.map((J)=>{const K=R.resolve(J);if(!S.existsSync(K))throw new U(`Image file not found: ${K}`);const Q=R.extname(K).toLowerCase();if(!Nz[Q])throw new U(`Unsupported image format "${Q}". Supported: jpg, png, gif, webp`);return K})}const a='input[type="file"][accept*="image"],input[type="file"][accept*=".jpg"],input[type="file"][accept*=".jpeg"],input[type="file"][accept*=".png"],input[type="file"][accept*=".gif"],input[type="file"][accept*=".webp"]';async function hz(z,J=15000){const K=500,Q=Math.max(1,Math.ceil(J/K));for(let Z=0;Z<Q;Z++){if(await z.evaluate(`
|
|
2
2
|
(() => {
|
|
3
3
|
const sels = ${JSON.stringify(a)};
|
|
4
4
|
return !!document.querySelector(sels);
|
|
@@ -80,33 +80,29 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
80
80
|
return { ok: actual === normalize(expectedText), actual };
|
|
81
81
|
})(${JSON.stringify(Z.sel)}, ${JSON.stringify(K)})
|
|
82
82
|
`);let q;if(Z.kind==="contenteditable"&&z.insertText){if(!(await z.evaluate(`
|
|
83
|
-
((selector
|
|
83
|
+
((selector) => {
|
|
84
84
|
const __opencli_xhs_fill_phase = "prepare";
|
|
85
|
-
const fireBeforeInput = (el, value) => {
|
|
86
|
-
try {
|
|
87
|
-
el.dispatchEvent(new InputEvent('beforeinput', {
|
|
88
|
-
bubbles: true,
|
|
89
|
-
data: value,
|
|
90
|
-
inputType: 'insertText',
|
|
91
|
-
}));
|
|
92
|
-
} catch {
|
|
93
|
-
el.dispatchEvent(new Event('beforeinput', { bubbles: true }));
|
|
94
|
-
}
|
|
95
|
-
};
|
|
96
85
|
const el = Array.from(document.querySelectorAll(selector)).find(node => node && node.offsetParent !== null);
|
|
97
86
|
if (!el) return { ok: false };
|
|
98
87
|
el.focus();
|
|
99
|
-
el.textContent = '';
|
|
100
88
|
const selection = window.getSelection();
|
|
101
89
|
const range = document.createRange();
|
|
102
90
|
range.selectNodeContents(el);
|
|
91
|
+
selection?.removeAllRanges();
|
|
92
|
+
selection?.addRange(range);
|
|
93
|
+
if ((el.innerText || el.textContent || '').trim()) {
|
|
94
|
+
const deleted = document.execCommand('delete', false);
|
|
95
|
+
if (!deleted) el.textContent = '';
|
|
96
|
+
}
|
|
97
|
+
range.selectNodeContents(el);
|
|
103
98
|
range.collapse(false);
|
|
104
99
|
selection?.removeAllRanges();
|
|
105
100
|
selection?.addRange(range);
|
|
106
|
-
fireBeforeInput(el, nextText);
|
|
107
101
|
return { ok: true };
|
|
108
|
-
})(${JSON.stringify(Z.sel)}
|
|
109
|
-
`))?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});throw Error(`Could not prepare ${Q} input. Debug screenshot: /tmp/xhs_publish_${Q}_debug.png`)}try{
|
|
102
|
+
})(${JSON.stringify(Z.sel)})
|
|
103
|
+
`))?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});throw Error(`Could not prepare ${Q} input. Debug screenshot: /tmp/xhs_publish_${Q}_debug.png`)}await z.wait({time:0.35});try{const X=String(K).split(`
|
|
104
|
+
`);for(let j=0;j<X.length;j++){if(j>0)if(typeof z.pressKey==="function"){await z.pressKey("Enter");await z.wait({time:0.15})}else await z.insertText(`
|
|
105
|
+
`);if(X[j])await x(z,X[j])}q=await z.evaluate(`
|
|
110
106
|
((selector, expectedText) => {
|
|
111
107
|
const __opencli_xhs_fill_phase = "verify";
|
|
112
108
|
const normalize = (value) => (value || '').replace(/\\s+/g, ' ').trim();
|
|
@@ -129,7 +125,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
129
125
|
const actual = normalize(el.innerText || el.textContent || '');
|
|
130
126
|
return { ok: actual === normalize(expectedText), actual };
|
|
131
127
|
})(${JSON.stringify(Z.sel)}, ${JSON.stringify(K)})
|
|
132
|
-
`)}catch{q=await $()}}else q=await $();if(!q?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});const V=typeof q?.actual==="string"?q.actual:"";throw Error(`Failed to set ${Q}. Expected "${K}", got "${V}". Debug screenshot: /tmp/xhs_publish_${Q}_debug.png`)}}async function Mz(z,J){return
|
|
128
|
+
`)}catch{q=await $()}}else q=await $();if(!q?.ok){await z.screenshot({path:`/tmp/xhs_publish_${Q}_debug.png`});const V=typeof q?.actual==="string"?q.actual:"";throw Error(`Failed to set ${Q}. Expected "${K}", got "${V}". Debug screenshot: /tmp/xhs_publish_${Q}_debug.png`)}}async function Mz(z,J){return W(await z.evaluate(`
|
|
133
129
|
(selectors => {
|
|
134
130
|
const el = selectors
|
|
135
131
|
.map(sel => Array.from(document.querySelectorAll(sel)))
|
|
@@ -234,7 +230,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
234
230
|
}
|
|
235
231
|
return count;
|
|
236
232
|
})(${JSON.stringify(z)}, ${JSON.stringify(J)})
|
|
237
|
-
`}async function zJ(z,J){const K=`#${J}`;if(typeof z.insertText==="function")try{await z.insertText(K);return!0}catch{}return
|
|
233
|
+
`}async function zJ(z,J){const K=`#${J}`;if(typeof z.insertText==="function")try{await z.insertText(K);return!0}catch{}return W(await z.evaluate(`
|
|
238
234
|
(text => {
|
|
239
235
|
const ok = document.execCommand('insertText', false, text);
|
|
240
236
|
const active = document.activeElement;
|
|
@@ -251,7 +247,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
251
247
|
if (!editor) return -1;
|
|
252
248
|
return (editor.innerText || editor.textContent || '').length;
|
|
253
249
|
})(${JSON.stringify(z)})
|
|
254
|
-
`}async function Lz(z,J,K){if(!await Mz(z,J))throw new
|
|
250
|
+
`}async function Lz(z,J,K){if(!await Mz(z,J))throw new G(`Could not attach topic "${K}": body editor not found`);const Z=Number(W(await z.evaluate(Kz(K,J))))||0;if(typeof z.pressKey==="function")try{await z.pressKey("Enter")}catch{}if(typeof z.insertText!=="function")throw new G(`Could not attach topic "${K}": page.insertText is unavailable`);try{await x(z,`#${K}`)}catch{throw new G(`Could not attach topic "${K}": failed to type inline topic query`)}await N(z,1.2);if(typeof z.pressKey!=="function")throw new G(`Could not attach topic "${K}": page.pressKey is unavailable`);try{await z.pressKey("Enter")}catch(q){throw new G(`Could not attach topic "${K}": failed to accept suggestion (${q&&q.message||q})`)}await z.wait({time:0.8});if((Number(W(await z.evaluate(Kz(K,J))))||0)<=Z)throw new G(`Could not attach topic "${K}": no real topic entity appeared after selection`)}async function _z(z,J,K){const Q=[],Z=[];for(const $ of K){let q=-1;try{const V=Number(W(await z.evaluate(Qz(J))));q=Number.isFinite(V)?V:-1}catch{}try{await Lz(z,J,$);Q.push($);await z.wait({time:0.4})}catch(V){Z.push({topic:$,reason:V&&V.message||String(V)});if(q>=0&&typeof z.pressKey==="function")try{const X=$.length+6;for(let j=0;j<X;j++){const O=Number(W(await z.evaluate(Qz(J))));if(!Number.isFinite(O)||O<0||O<=q)break;await z.pressKey("Backspace");await z.wait({time:0.15})}}catch{}}}return{added:Q,failed:Z}}async function fz(z){const J=await z.evaluate(`
|
|
255
251
|
() => {
|
|
256
252
|
const isVisible = (el) => {
|
|
257
253
|
if (!el || el.offsetParent === null) return false;
|
|
@@ -300,7 +296,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
300
296
|
}
|
|
301
297
|
return { ok: false, visibleTexts };
|
|
302
298
|
}
|
|
303
|
-
`);if(J?.ok)await z.wait({time:1});return J}async function h(z,J,K=7000){const Q=500,Z=Math.max(1,Math.ceil(K/Q));let $={ok:!1};for(let q=0;q<Z;q++){$=
|
|
299
|
+
`);if(J?.ok)await z.wait({time:1});return J}async function h(z,J,K=7000){const Q=500,Z=Math.max(1,Math.ceil(K/Q));let $={ok:!1};for(let q=0;q<Z;q++){$=W(await z.evaluate(`
|
|
304
300
|
((cfg) => {
|
|
305
301
|
const __opencli_xhs_click_label = { wantLabel: ${JSON.stringify(J)} };
|
|
306
302
|
const wantLabel = ${JSON.stringify(J)};
|
|
@@ -352,7 +348,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
352
348
|
selection?.addRange(range);
|
|
353
349
|
return { ok: true };
|
|
354
350
|
})()
|
|
355
|
-
`);return
|
|
351
|
+
`);return W(J)}async function Rz(z){const J=await z.evaluate(`
|
|
356
352
|
(() => {
|
|
357
353
|
const __opencli_xhs_card_text = true;
|
|
358
354
|
const sel = ${JSON.stringify(u)};
|
|
@@ -361,7 +357,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
361
357
|
const text = active ? (active.innerText || active.textContent || '').trim() : '';
|
|
362
358
|
return { ok: !!text, text };
|
|
363
359
|
})()
|
|
364
|
-
`);return
|
|
360
|
+
`);return W(J)}async function Zz(z){const J=await z.evaluate(`
|
|
365
361
|
(() => {
|
|
366
362
|
const __opencli_xhs_card_count = true;
|
|
367
363
|
const sel = ${JSON.stringify(u)};
|
|
@@ -370,16 +366,16 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
370
366
|
const activeText = active ? (active.innerText || active.textContent || '').trim() : '';
|
|
371
367
|
return { ok: true, count: editors.length, activeEmpty: !activeText };
|
|
372
368
|
})()
|
|
373
|
-
`);return
|
|
369
|
+
`);return W(J)}async function yz(z,J=8000){const K=300,Q=Math.ceil(J/K);for(let Z=0;Z<Q;Z++){if((await Zz(z))?.count>=1)return!0;await z.wait({time:K/1000})}return!1}async function bz(z,J,K=6000){const Q=300,Z=Math.ceil(K/Q);for(let $=0;$<Z;$++){const q=await Zz(z);if(q?.count>=J&&q?.activeEmpty)return!0;await z.wait({time:Q/1000})}return!1}async function wz(z,J,K=4){for(let Q=0;Q<K;Q++){if(!(await h(z,g))?.ok)return!1;if(await bz(z,J,2500))return!0}return!1}async function Tz(z){const J=await z.evaluate(`
|
|
374
370
|
(() => {
|
|
375
371
|
const __opencli_xhs_preview_ready = true;
|
|
376
372
|
const ready = Array.from(document.querySelectorAll('button'))
|
|
377
373
|
.some((b) => b.offsetParent !== null && (b.innerText || '').replace(/\\s+/g, '') === '下一步');
|
|
378
374
|
return { ok: ready };
|
|
379
375
|
})()
|
|
380
|
-
`);return
|
|
381
|
-
`);if(!(await Az(z))?.ok)throw new
|
|
382
|
-
`);for(let q=0;q<$.length;q++){if(q>0&&typeof z.pressKey==="function")await z.pressKey("Enter");if($[q])await x(z,$[q])}}else await z.evaluate(`(t => document.execCommand('insertText', false, t))(${JSON.stringify(J)})`);await z.wait({time:0.4});if(!(await Rz(z))?.ok)throw new
|
|
376
|
+
`);return W(J)}async function Cz(z,J=6){const K=400,Q=Math.ceil(3000/K);for(let Z=0;Z<J;Z++){if(!(await h(z,r))?.ok&&Z===0)return!1;for(let q=0;q<Q;q++){if((await Tz(z))?.ok)return!0;await z.wait({time:K/1000})}}return!1}async function Sz(z,J,K){J=String(J).replace(/\\n/g,`
|
|
377
|
+
`);if(!(await Az(z))?.ok)throw new G(`文字配图: could not focus card editor #${K+1}`);if(typeof z.insertText==="function"){const $=J.split(`
|
|
378
|
+
`);for(let q=0;q<$.length;q++){if(q>0&&typeof z.pressKey==="function")await z.pressKey("Enter");if($[q])await x(z,$[q])}}else await z.evaluate(`(t => document.execCommand('insertText', false, t))(${JSON.stringify(J)})`);await z.wait({time:0.4});if(!(await Rz(z))?.ok)throw new G(`文字配图: card editor #${K+1} is empty after typing`)}async function xz(z,J){if(!J||J===I)return I;const K=W(await z.evaluate(`
|
|
383
379
|
(async () => {
|
|
384
380
|
const __opencli_xhs_card_styles = true;
|
|
385
381
|
const want = ${JSON.stringify(J)};
|
|
@@ -408,7 +404,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
408
404
|
}
|
|
409
405
|
return { ok: seen.length > 0, styles: seen, found: false };
|
|
410
406
|
})()
|
|
411
|
-
`));if(!K?.found)throw new
|
|
407
|
+
`));if(!K?.found)throw new G(`文字配图: requested style "${J}" is not available for this content (options: ${(K?.styles||[]).join(" / ")||"none"}). Choose an available style or omit --card-style to use ${I}.`);if(!(await h(z,J))?.ok)throw new G(`文字配图: could not click requested style "${J}".`);await z.wait({time:0.6});return J}async function $z(z){const J=await z.evaluate(`
|
|
412
408
|
(() => {
|
|
413
409
|
const __opencli_xhs_composer_media_count = true;
|
|
414
410
|
const visibleBox = (el) => {
|
|
@@ -440,7 +436,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
440
436
|
}
|
|
441
437
|
return { ok: true, count };
|
|
442
438
|
})()
|
|
443
|
-
`);return
|
|
439
|
+
`);return W(J)}async function qz(z,J,K){const Q=await $z(z);if(!Q||typeof Q.count!=="number")throw new G(`${K}: could not verify current composer media count`);if(Q.count<J){await z.screenshot({path:"/tmp/xhs_publish_media_debug.png"});throw new G(`${K}: expected at least ${J} visible media item(s), got ${Q.count}. Debug screenshot: /tmp/xhs_publish_media_debug.png`)}}async function Ez(z,J,K){if(!(await h(z,E))?.ok){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new G(`文字配图: could not click "${E}" entry. Debug: /tmp/xhs_publish_textimage_debug.png`)}if(!await yz(z)){await z.screenshot({path:"/tmp/xhs_publish_textimage_debug.png"});throw new G(`文字配图: 写文字 card editor did not appear after clicking "${E}". Debug: /tmp/xhs_publish_textimage_debug.png`)}for(let V=0;V<J.length;V++){if(V>0){if(!await wz(z,V+1)){await z.screenshot({path:"/tmp/xhs_publish_addcard_debug.png"});throw new G(`文字配图: new card editor #${V+1} did not render after "${g}". Debug: /tmp/xhs_publish_addcard_debug.png`)}}await Sz(z,J[V],V)}if(!await Cz(z)){await z.screenshot({path:"/tmp/xhs_publish_generate_debug.png"});throw new G(`文字配图: "${r}" did not advance to the 预览图片 step. `+"Debug: /tmp/xhs_publish_generate_debug.png")}const $=await xz(z,K);if(!(await h(z,p))?.ok){await z.screenshot({path:"/tmp/xhs_publish_next_debug.png"});throw new G(`文字配图: could not click "${p}". Debug: /tmp/xhs_publish_next_debug.png`)}await z.wait({time:2});return $}async function Vz(z){return z.evaluate(`
|
|
444
440
|
() => {
|
|
445
441
|
const text = (document.body?.innerText || '').replace(/s+/g, ' ').trim();
|
|
446
442
|
const hasTitleInput = !!Array.from(document.querySelectorAll('input, textarea')).find((el) => {
|
|
@@ -478,14 +474,14 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
478
474
|
if (el && el.offsetParent !== null) return true;
|
|
479
475
|
}
|
|
480
476
|
return false;
|
|
481
|
-
})()`))return!0;if(Z<Q-1)await z.wait({time:K/1000})}return!1}async function cz(z){const J=
|
|
477
|
+
})()`))return!0;if(Z<Q-1)await z.wait({time:K/1000})}return!1}async function cz(z){const J=W(await z.evaluate(`
|
|
482
478
|
(() => {
|
|
483
479
|
const __opencli_xhs_restore_srcs = true;
|
|
484
480
|
return Array.from(document.querySelectorAll('.img-container img'))
|
|
485
481
|
.map((img) => img.currentSrc || img.src || '')
|
|
486
482
|
.filter(Boolean);
|
|
487
483
|
})()
|
|
488
|
-
`));return Array.isArray(J)?J:[]}async function Hz(z,J){if(!Array.isArray(J)||J.length===0)return 0;const K=
|
|
484
|
+
`));return Array.isArray(J)?J:[]}async function Hz(z,J){if(!Array.isArray(J)||J.length===0)return 0;const K=W(await z.evaluate(`
|
|
489
485
|
(async (wanted) => {
|
|
490
486
|
const __opencli_xhs_delete_by_src = true;
|
|
491
487
|
const want = new Set(wanted);
|
|
@@ -516,7 +512,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
516
512
|
}
|
|
517
513
|
return removed;
|
|
518
514
|
})(${JSON.stringify(J)})
|
|
519
|
-
`));return Number(K)||0}function dz(z){const J=String(z&&(z.url||z.key)||""),K=String(z&&z.method||"").toUpperCase(),Q=Number(z&&z.status||0);if(K!=="PUT")return null;if(!/ros-upload[^/]*\/spectrum\//.test(J))return null;if(Q<200||Q>=300)return null;const Z=J.match(/\/spectrum\/([^?#]+)/);return Z?Z[1]:J}function jz(z){return Math.min(45,6+z*4)}async function Gz(z,J,K,Q=90000){if(J<=0)return{ok:!0,completed:0};if(!K||typeof z.readNetworkCapture!=="function"){await z.wait({time:jz(J)});return{ok:!0,completed:null,viaFallback:!0}}const Z=1500,q=Math.ceil(9000/Z),V=Math.ceil(Q/Z),
|
|
515
|
+
`));return Number(K)||0}function dz(z){const J=String(z&&(z.url||z.key)||""),K=String(z&&z.method||"").toUpperCase(),Q=Number(z&&z.status||0);if(K!=="PUT")return null;if(!/ros-upload[^/]*\/spectrum\//.test(J))return null;if(Q<200||Q>=300)return null;const Z=J.match(/\/spectrum\/([^?#]+)/);return Z?Z[1]:J}function jz(z){return Math.min(45,6+z*4)}async function Gz(z,J,K,Q=90000){if(J<=0)return{ok:!0,completed:0};if(!K||typeof z.readNetworkCapture!=="function"){await z.wait({time:jz(J)});return{ok:!0,completed:null,viaFallback:!0}}const Z=1500,q=Math.ceil(9000/Z),V=Math.ceil(Q/Z),X=new Set;for(let j=0;j<V;j++){const O=await z.readNetworkCapture().catch(()=>[]);for(const D of Array.isArray(O)?O:[]){const Y=dz(D);if(Y)X.add(Y)}if(X.size>=J)return{ok:!0,completed:X.size};if(j>=q&&X.size===0){await z.wait({time:jz(J)});return{ok:!0,completed:null,viaFallback:!0}}await z.wait({time:Z/1000})}return{ok:!1,completed:X.size}}async function nz(z,J,K){try{await z.goto("https://creator.xiaohongshu.com/new/note-manager")}catch{return null}const Q=J.trim();for(let Z=0;Z<K;Z++){await z.wait({time:1.5});let $;try{$=W(await z.evaluate(`(() => {
|
|
520
516
|
const noteIdRe = /"noteId":"([0-9a-f]{24})"/;
|
|
521
517
|
return Array.from(document.querySelectorAll('div.note[data-impression], div.note')).slice(0, 3).map((card) => {
|
|
522
518
|
const impression = card.getAttribute('data-impression') || '';
|
|
@@ -525,7 +521,7 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
525
521
|
const dateText = (card.querySelector('.time_status, .time')?.innerText || '').trim();
|
|
526
522
|
return { id, title, dateText };
|
|
527
523
|
}).filter((entry) => entry.title);
|
|
528
|
-
})()`))}catch{return null}if(Array.isArray($)){const q=$.find((V)=>{const
|
|
524
|
+
})()`))}catch{return null}if(Array.isArray($)){const q=$.find((V)=>{const X=String(V.title||"").trim();if(!(X===Q||X.startsWith(Q)||Q.startsWith(X)))return!1;const j=String(V.dateText||"").match(/(\d{4})年(\d{2})月(\d{2})日 (\d{2}):(\d{2})/);if(!j)return!0;const O=Date.UTC(Number(j[1]),Number(j[2])-1,Number(j[3]),Number(j[4]),Number(j[5]))-28800000;return Math.abs(Date.now()-O)<=93600000});if(q)return q}}return null}Wz({site:"xiaohongshu",name:"publish",access:"write",description:"小红书发布图文笔记 (creator center UI automation)",domain:"creator.xiaohongshu.com",strategy:Fz.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"content",required:!1,positional:!0,help:"笔记正文(字面文本,不会展开 @文件 引用;长正文建议用 --file)"},{name:"file",required:!1,help:"从本机文件读取笔记正文(UTF-8),与位置参数 <content> 二选一"},{name:"title",required:!0,help:"笔记标题 (最多20字)"},{name:"images",required:!1,help:"图片路径,逗号分隔,最多9张 (jpg/png/gif/webp)"},{name:"card-text",required:!1,help:`文字配图卡片文字,多张卡片用 ${o} 分隔,卡内换行用 \\n`},{name:"card-style",required:!1,help:`文字配图卡片样式,运行时按页面实际选项匹配;找不到会失败。省略时使用${I}。可选: ${s.map(([z,J])=>`${z}(${J})`).join(" ")}`},{name:"topics",required:!1,help:"话题标签,逗号分隔,不含 # 号。平台话题库匹配不上的会自动跳过并在结果中提示,不影响发布"},{name:"draft",type:"bool",default:!1,help:"保存为草稿,不直接发布"}],columns:["status","detail","url"],func:async(z,J)=>{if(!z)throw Error("Browser page required");const K=String(J.title??"").trim(),Q=J.file?String(J.file).trim():"";let Z=String(J.content??"").trim();if(Q){if(Z)throw new U("正文位置参数和 --file 只能二选一,不能同时给");const H=R.resolve(Q);if(!S.existsSync(H))throw new U(`--file 指向的文件不存在: ${H}`);Z=S.readFileSync(H,"utf-8").trim();if(!Z)throw new U(`--file 指向的文件内容为空: ${H}`)}const $=J.images?String(J.images).split(",").map((H)=>H.trim()).filter(Boolean):[],q=J.topics?String(J.topics).split(",").map((H)=>H.trim()).filter(Boolean):[],V=Boolean(J.draft),X=J["card-text"]?String(J["card-text"]):"",j=X?X.split(o).map((H)=>H.trim()).filter(Boolean):[],O=J["card-style"]?String(J["card-style"]).trim():"",D=j.length>0;if(!K)throw new U("--title is required");if(K.length>l)throw new U(`Title is ${K.length} chars — must be ≤ ${l}`);if(!Z)throw new U("缺少笔记正文:给位置参数 <content>(字面文本),或用 --file <本机文件路径>");Oz(Z,{fileFlag:"--file"});if(!D&&$.length===0)throw new U("Provide --card-text (text-image mode) or --images (upload mode); neither was given.");if($.length>n)throw new U(`Too many images: ${$.length} (max ${n})`);if(D&&$.some((H)=>R.extname(H).toLowerCase()===".gif"))throw new U("文字配图模式追加的图片不支持 .gif(编辑器图片入口只接受 jpg/jpeg/png/webp)");const Y=Iz($);await z.goto(vz);await z.wait({time:1});await z.evaluate("() => { try { window.onbeforeunload = null; } catch (e) {} location.reload(); }").catch(()=>{return});let M="";for(let H=0;H<30;H++){await z.wait({time:0.5});M=await z.evaluate("() => location.href");if(M.includes("creator.xiaohongshu.com"))break}if(!M.includes("creator.xiaohongshu.com")){await z.screenshot({path:"/tmp/xhs_publish_redirect_debug.png"});throw Error(`Redirected away from creator center (landed on ${M}) — session may have expired. `+"Re-capture browser login via: ppcli xiaohongshu creator-profile. Debug screenshot: /tmp/xhs_publish_redirect_debug.png")}const L=await fz(z);if((await uz(z,L?.ok?5000:2000)).state==="video_surface"){await z.screenshot({path:"/tmp/xhs_publish_tab_debug.png"});const H=L?.ok?`clicked "${L.text}"`:`visible candidates: ${(L?.visibleTexts||[]).join(" | ")||"none"}`;throw Error("Still on the video publish page after trying to select 图文. "+`Details: ${H}. Debug screenshot: /tmp/xhs_publish_tab_debug.png`)}const _=await cz(z);let f=O;if(D)f=await Ez(z,j,O);else{const H=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,B=await e(z,Y);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new G(`图片注入失败:${B.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_upload_debug.png")}await z.wait({time:i/1000});const v=await Gz(z,Y.length,H);if(!v.ok){await z.screenshot({path:"/tmp/xhs_publish_upload_debug.png"});throw new G(`图片上传未全部完成:只确认 ${v.completed}/${Y.length} 张传到小红书 CDN,`+"继续发布会丢图(通常只剩封面)。直接重新运行本 publish 命令即可(会自动重置发布页重传);"+"请勿手动修改当前页面。Debug: /tmp/xhs_publish_upload_debug.png")}await zz(z);if(_.length){await Hz(z,_);await z.wait({time:0.5})}const A=await $z(z);if(A&&typeof A.count==="number"&&A.count>Y.length){await z.screenshot({path:"/tmp/xhs_publish_dup_debug.png"});throw new G(`清掉恢复态旧图后编辑器内仍有 ${A.count} 张图,超过本次注入的 ${Y.length} 张。`+"直接重新运行本 publish 命令即可(会自动重置发布页);请勿手动修改当前页面。Debug: /tmp/xhs_publish_dup_debug.png")}}if(!await mz(z)){await z.screenshot({path:"/tmp/xhs_publish_form_debug.png"});throw new G("Editing form did not appear after image acquisition. The page layout may have changed. Debug screenshot: /tmp/xhs_publish_form_debug.png")}if(D)await qz(z,j.length,"文字配图 generated images");if(D&&Y.length>0){const H=typeof z.startNetworkCapture==="function"?await z.startNetworkCapture("").catch(()=>!1):!1,B=await e(z,Y);if(!B.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new G(`追加图片失败:${B.error??"unknown"}。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await z.wait({time:i/1000});const v=await Gz(z,Y.length,H);if(!v.ok){await z.screenshot({path:"/tmp/xhs_publish_append_debug.png"});throw new G(`追加图片上传未全部完成:只确认 ${v.completed}/${Y.length} 张传到小红书 CDN。`+"直接重新运行本 publish 命令即可(会自动重置发布页重传);请勿手动修改当前页面。"+"Debug: /tmp/xhs_publish_append_debug.png")}await zz(z);await qz(z,j.length+Y.length,"文字配图 appended images")}if(D&&_.length){await Hz(z,_);await z.wait({time:0.5})}await Jz(z,m,K,"title");await N(z,0.6);await Jz(z,t,Z,"content");await N(z,0.6);let y=[],b=[];if(q.length){const H=await _z(z,t,q);y=H.added;b=H.failed}await N(z,1,0.6);const w=await Dz(z,["xhs-publish-btn"]);if(w){await Bz(z,w.x,w.y);await N(z,0.4)}const c=V?["暂存离开","存草稿"]:["发布","发布笔记"],F=await z.evaluate(`
|
|
529
525
|
(cfg => {
|
|
530
526
|
const { isDraftMode, publishNames, draftNames, labels } = cfg;
|
|
531
527
|
const isVisible = (el) => {
|
|
@@ -613,4 +609,4 @@ import*as S from"node:fs";import*as R from"node:path";import{CommandExecutionErr
|
|
|
613
609
|
}
|
|
614
610
|
return '';
|
|
615
611
|
})(${JSON.stringify(Xz)})
|
|
616
|
-
`),Yz=!k.includes("/publish/publish");let T=d.length>0||Yz;const C=V?"暂存成功":"发布成功";let P=null;if(!V){P=await nz(z,K,T?4:8);if(P)T=!0}if(!T){if(V)throw new
|
|
612
|
+
`),Yz=!k.includes("/publish/publish");let T=d.length>0||Yz;const C=V?"暂存成功":"发布成功";let P=null;if(!V){P=await nz(z,K,T?4:8);if(P)T=!0}if(!T){if(V)throw new G(`${C}无法确认:未观察到成功提示或提交后跳转。`+(k?`当前 URL: ${k}。`:"当前 URL 为空。")+"先用 `xiaohongshu drafts` 查草稿箱确认是否已存上;确认没有再重新运行本命令,避免堆出重复草稿。");throw new G(`${C}无法确认:未观察到成功提示或提交后跳转,创作者后台也暂未查到同标题笔记。`+(k?`当前 URL: ${k}。`:"")+"注意:发布按钮已被触发,笔记仍可能延迟入库。禁止立即重发——请等 1-2 分钟后用 `xiaohongshu creator-notes` 回查本篇标题是否已出现;确认不存在后才重新运行本 publish 命令,否则会重复发布。")}return[{status:`✅ ${C}`,detail:[`"${K}"`,D?`${j.length}张文字配图${Y.length?` + ${Y.length}张图片`:""}${f&&f!==I?` (${f})`:""}`:`${Y.length}张图片`,y.length?`话题: ${y.join(" ")}`:"",b.length?`⚠️ 话题未附上(已自动跳过,不影响发布): ${b.map((H)=>H.topic).join(" ")}`:"",P?.id?`笔记ID: ${P.id}`:"",d||(P?"已在创作者后台确认":"")||k||""].filter(Boolean).join(" · "),url:P?.id?`https://www.xiaohongshu.com/explore/${P.id}`:""}]}});
|