publishport-opencli 1.0.9 → 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/_shared/file-inject.js +41 -0
- package/clis/_shared/video-publish.js +4 -4
- package/clis/bilibili/upload.js +1 -1
- package/clis/chatgpt/utils.js +5 -5
- package/clis/claude/utils.js +1 -1
- package/clis/deepseek/utils.js +7 -7
- package/clis/douyin/draft.js +6 -6
- package/clis/douyin/publish-image.js +10 -31
- package/clis/facebook/post.js +9 -36
- package/clis/instagram/post.js +19 -66
- package/clis/instagram/reel.js +11 -11
- package/clis/threads/post.js +15 -34
- package/clis/twitter/post.js +15 -42
- package/clis/twitter/utils.js +4 -36
- package/clis/wechat-channels/publish.js +29 -57
- package/clis/weibo/publish.js +8 -8
- package/clis/weixin/create-draft.js +7 -7
- package/clis/xianyu/publish.js +2 -2
- package/clis/xiaohongshu/publish-video.js +9 -27
- package/clis/xiaohongshu/publish.js +42 -86
- package/dist/src/browser/bridge-readiness.d.ts +1 -1
- package/dist/src/browser/daemon-client.d.ts +72 -1
- package/dist/src/browser/daemon-client.js +1 -1
- package/dist/src/browser/daemon-version.d.ts +1 -1
- package/dist/src/browser/errors.d.ts +2 -0
- package/dist/src/browser/errors.js +3 -3
- package/dist/src/browser/network-interceptor.d.ts +11 -0
- package/dist/src/browser/network-interceptor.js +1 -0
- package/dist/src/cli.js +24 -24
- package/dist/src/commands/daemon.js +1 -1
- package/dist/src/daemon-utils.d.ts +1 -0
- package/dist/src/daemon-utils.js +1 -1
- package/dist/src/daemon.js +1 -1
- package/dist/src/doctor.d.ts +1 -1
- package/dist/src/doctor.js +7 -7
- package/dist/src/errors.d.ts +8 -0
- package/dist/src/errors.js +1 -1
- package/dist/src/execution.js +2 -2
- package/dist/src/session-lease.d.ts +135 -0
- package/dist/src/session-lease.js +1 -0
- package/dist/src/session-lease.test.d.ts +1 -0
- package/package.json +1 -1
package/clis/facebook/post.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as y from"node:fs";import*as f from"node:path";import{ArgumentError as q,AuthRequiredError as L,CommandExecutionError as W}from"@jackwener/opencli/errors";import{cli as N,Strategy as A}from"@jackwener/opencli/registry";import{assertLiteralContent as j}from"../_shared/content-guard.js";import{setInputFiles as _}from"../_shared/file-inject.js";const D='[data-lexical-editor="true"][contenteditable="true"]',$='[data-pp-fb-composer="1"]',M='[data-pp-fb-trigger="1"]',U='[data-pp-fb-post="1"]',T="https://www.facebook.com/",I=new Set([".jpg",".jpeg",".png",".gif",".webp"]);function k(z){const K=String(z??"").trim();if(!K)return T;if(/^https?:\/\//i.test(K)){let J;try{J=new URL(K)}catch{throw new q(`Invalid target URL: ${K}`)}if(J.hostname!=="facebook.com"&&!J.hostname.endsWith(".facebook.com"))throw new q("target URL must be on facebook.com.");return J.href}const Q=K.replace(/^@/,"").replace(/^\/+|\/+$/g,"");if(!/^[A-Za-z0-9.\-]+$/.test(Q))throw new q(`Invalid target "${z}" — pass a facebook.com URL or a bare profile/Page vanity.`);return`https://www.facebook.com/${Q}`}function w(z){return String(z).split(",").map((Q)=>Q.trim()).filter(Boolean).map((Q)=>{const J=f.resolve(Q),Y=f.extname(J).toLowerCase();if(!I.has(Y))throw new W(`Unsupported image format "${Y}". Supported: jpg, png, gif, webp`);const Z=y.statSync(J,{throwIfNoEntry:!1});if(!Z||!Z.isFile())throw new W(`Not a valid file: ${J}`);return J})}function R(){return`(() => {
|
|
2
2
|
const path = location.pathname || '';
|
|
3
3
|
if (/^\\/(login|checkpoint)(\\/|$|\\.php)/.test(path)) return 'login';
|
|
4
4
|
// Prefer an already-open editor (dialog first, so we don't grab a
|
|
5
5
|
// background feed publisher when the modal is up).
|
|
6
6
|
const dialog = document.querySelector('[role="dialog"]');
|
|
7
7
|
const scope = dialog || document;
|
|
8
|
-
const ed = scope.querySelector('${
|
|
8
|
+
const ed = scope.querySelector('${D}') || document.querySelector('${D}');
|
|
9
9
|
if (ed) {
|
|
10
10
|
ed.setAttribute('data-pp-fb-composer', '1');
|
|
11
11
|
ed.scrollIntoView({ block: 'center' });
|
|
@@ -33,7 +33,7 @@ import*as v from"node:fs";import*as H from"node:path";import{ArgumentError as V,
|
|
|
33
33
|
return 'wait';
|
|
34
34
|
})()`}function S(){return`(() => {
|
|
35
35
|
const dialog = document.querySelector('[role="dialog"]') || document;
|
|
36
|
-
const ed = dialog.querySelector('${
|
|
36
|
+
const ed = dialog.querySelector('${$}');
|
|
37
37
|
const btns = [...dialog.querySelectorAll('[role="button"][tabindex]')].filter((b) => {
|
|
38
38
|
if (ed && (b === ed || b.contains(ed) || ed.contains(b))) return false;
|
|
39
39
|
if (b.closest('[role="toolbar"]')) return false;
|
|
@@ -46,43 +46,16 @@ import*as v from"node:fs";import*as H from"node:path";import{ArgumentError as V,
|
|
|
46
46
|
submit.setAttribute('data-pp-fb-post', '1');
|
|
47
47
|
submit.scrollIntoView({ block: 'center' });
|
|
48
48
|
return { ok: true };
|
|
49
|
-
})()`}function
|
|
49
|
+
})()`}function b(z){return`(() => {
|
|
50
50
|
const collapse = (s) => String(s || '').replace(/\\s+/g, ' ').trim();
|
|
51
|
-
const ed = document.querySelector('${
|
|
51
|
+
const ed = document.querySelector('${$}');
|
|
52
52
|
if (!ed) return { ok: true };
|
|
53
53
|
const remaining = collapse(ed.textContent || '');
|
|
54
54
|
if (remaining.length === 0) return { ok: true };
|
|
55
55
|
if (remaining === collapse(${JSON.stringify(z)})) return { ok: false, remaining };
|
|
56
56
|
return { ok: true };
|
|
57
|
-
})()`}const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const dt = new DataTransfer();
|
|
61
|
-
for (const file of ${JSON.stringify(Q)}) {
|
|
62
|
-
const bin = atob(file.base64);
|
|
63
|
-
const bytes = new Uint8Array(bin.length);
|
|
64
|
-
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
|
|
65
|
-
dt.items.add(new File([bytes], file.name, { type: file.mime }));
|
|
66
|
-
}
|
|
67
|
-
let assigned = false;
|
|
68
|
-
try {
|
|
69
|
-
Object.defineProperty(input, 'files', { value: dt.files, writable: false, configurable: true });
|
|
70
|
-
assigned = input.files && input.files.length >= ${JSON.stringify(J.length)};
|
|
71
|
-
} catch (e) {
|
|
72
|
-
try {
|
|
73
|
-
const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'files');
|
|
74
|
-
if (setter && setter.set) {
|
|
75
|
-
setter.set.call(input, dt.files);
|
|
76
|
-
assigned = input.files && input.files.length >= ${JSON.stringify(J.length)};
|
|
77
|
-
}
|
|
78
|
-
} catch (e2) { /* ignore */ }
|
|
79
|
-
}
|
|
80
|
-
if (!assigned) return { ok: false, error: 'Could not assign files to input' };
|
|
81
|
-
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
82
|
-
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
83
|
-
return { ok: true };
|
|
84
|
-
})()`);if(!K?.ok)throw new $(`Image upload failed: ${K?.error??"unknown error"}`)}async function x(z,J){if(z.setFileInput)try{await z.setFileInput(J,D);return}catch{}await C(z,J)}N({site:"facebook",name:"post",access:"write",description:"Publish a new post to your Facebook feed (or a Page you manage). Pass the post text (multi-line allowed); optionally attach local images and target a Page. Published from your logged-in account — write genuine, on-topic content, not filler.",domain:"facebook.com",strategy:L.UI,browser:!0,args:[{name:"text",type:"string",required:!0,positional:!0,help:"Post text (multi-line allowed)"},{name:"images",type:"string",required:!1,help:"Local image paths, comma-separated (jpg/png/gif/webp)"},{name:"target",type:"string",required:!1,help:"Optional Page/profile to post to: a facebook.com URL or a bare vanity. Omit to post to your own feed."}],columns:["status","target","message"],func:async(z,J)=>{if(!z)throw new $("Browser session required for facebook post");const Q=M(J.text,{label:"帖子正文"});if(!Q)throw new V("Post text cannot be empty.");const K=J.images?w(J.images):[],Y=T(J.target);await z.goto(Y,{waitUntil:"none"});let Z=!1;for(let W=0;W<20;W++){const F=await z.evaluate(R());if(F==="login")throw new A("www.facebook.com","Facebook redirected to login/checkpoint — not signed in");if(F==="ready"){Z=!0;break}if(F==="trigger")await z.click(_);await z.wait(1)}if(!Z)throw new $("Post composer not found — the feed publisher may not have loaded, or you may lack permission to post here.");await z.click(B);await z.wait(1);await z.pressKey("Control+A");await z.pressKey("Delete");if(K.length>0){await x(z,K);await z.wait(2)}const q=Q.split(`
|
|
85
|
-
`);for(let W=0;W<q.length;W++){if(W>0)await z.pressKey("Shift+Enter");if(q[W].length)await z.typeText(B,q[W])}if(await z.evaluate(`(() => !!document.querySelector('[role="listbox"][aria-label], ul[role="listbox"]'))()`))await z.pressKey("Escape");const G=(W)=>String(W||"").replace(/\s+/g," ").trim(),X=await z.evaluate(`(() => {
|
|
86
|
-
const ed = document.querySelector('${B}');
|
|
57
|
+
})()`}const C='[role="dialog"] input[type="file"], input[type="file"][accept*="image"]';async function O(z,K){await _(z,C,K)}N({site:"facebook",name:"post",access:"write",description:"Publish a new post to your Facebook feed (or a Page you manage). Pass the post text (multi-line allowed); optionally attach local images and target a Page. Published from your logged-in account — write genuine, on-topic content, not filler.",domain:"facebook.com",strategy:A.UI,browser:!0,args:[{name:"text",type:"string",required:!0,positional:!0,help:"Post text (multi-line allowed)"},{name:"images",type:"string",required:!1,help:"Local image paths, comma-separated (jpg/png/gif/webp)"},{name:"target",type:"string",required:!1,help:"Optional Page/profile to post to: a facebook.com URL or a bare vanity. Omit to post to your own feed."}],columns:["status","target","message"],func:async(z,K)=>{if(!z)throw new W("Browser session required for facebook post");const Q=j(K.text,{label:"帖子正文"});if(!Q)throw new q("Post text cannot be empty.");const J=K.images?w(K.images):[],Y=k(K.target);await z.goto(Y,{waitUntil:"none"});let Z=!1;for(let V=0;V<20;V++){const X=await z.evaluate(R());if(X==="login")throw new L("www.facebook.com","Facebook redirected to login/checkpoint — not signed in");if(X==="ready"){Z=!0;break}if(X==="trigger")await z.click(M);await z.wait(1)}if(!Z)throw new W("Post composer not found — the feed publisher may not have loaded, or you may lack permission to post here.");await z.click($);await z.wait(1);await z.pressKey("Control+A");await z.pressKey("Delete");if(J.length>0){await O(z,J);await z.wait(2)}const B=Q.split(`
|
|
58
|
+
`);for(let V=0;V<B.length;V++){if(V>0)await z.pressKey("Shift+Enter");if(B[V].length)await z.typeText($,B[V])}if(await z.evaluate(`(() => !!document.querySelector('[role="listbox"][aria-label], ul[role="listbox"]'))()`))await z.pressKey("Escape");const F=(V)=>String(V||"").replace(/\s+/g," ").trim(),H=await z.evaluate(`(() => {
|
|
59
|
+
const ed = document.querySelector('${$}');
|
|
87
60
|
return ed ? (ed.textContent || '') : null;
|
|
88
|
-
})()`);if(
|
|
61
|
+
})()`);if(H===null||!F(H).includes(F(Q)))throw new W(`Composed text did not land before publish (got ${JSON.stringify((H||"").slice(0,60))}).`);const G=await z.evaluate(S());if(G?.notfound)throw new W("Post button not found in the composer.");if(G?.disabled)throw new W("Post button is disabled — composer may be empty or the post was rejected.");await z.click(U);await z.wait(2);if(!(await z.evaluate(b(Q)))?.ok)throw new W("Post still in the composer after clicking Post — publish blocked (rate limit, restricted, or no permission). Not confirmed sent; verify before retrying.");const v=J.length>0?` with ${J.length} image(s)`:"";return[{status:"success",target:Y,message:`Post published${v}.`}]}});
|
package/clis/instagram/post.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as P from"node:fs";import*as S from"node:path";import{cli as r,Strategy as p}from"@jackwener/opencli/registry";import{ArgumentError as H,AuthRequiredError as W,CommandExecutionError as T}from"@jackwener/opencli/errors";import{installInstagramProtocolCapture as a,readInstagramProtocolCapture as t}from"./_shared/protocol-capture.js";import{publishMediaViaPrivateApi as e,publishImagesViaPrivateApi as jj,resolveInstagramPrivatePublishConfig as Gj,isPublishOutcomeUncertain as Kj}from"./_shared/private-publish.js";import{resolveInstagramRuntimeInfo as Nj}from"./_shared/runtime-info.js";import{setInputFiles as Oj}from"../_shared/file-inject.js";const J="https://www.instagram.com/",Qj=new Set([".jpg",".jpeg",".png",".webp"]),Tj=new Set([".mp4"]),x=10,Xj="/tmp/instagram_post_protocol_trace.json";async function k(j,G=!1){if(G){await j.goto(`${J}?__opencli_reset=${Date.now()}`);await j.wait({time:1})}await j.goto(J)}export function buildEnsureComposerOpenJs(){return`
|
|
2
2
|
(() => {
|
|
3
3
|
const path = window.location?.pathname || '';
|
|
4
4
|
const onLoginRoute = /\\/accounts\\/login\\/?/.test(path);
|
|
@@ -61,7 +61,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
61
61
|
const settled = !shared && !composerOpen && !/sharing/.test(visibleText);
|
|
62
62
|
return { ok: shared, failed, settled, url: /\\/p\\//.test(url) ? url : '' };
|
|
63
63
|
})()
|
|
64
|
-
`}function
|
|
64
|
+
`}function Yj(j){if(!j)throw new T("Browser session required for instagram post");return j}function Zj(j){if(!j.length)throw new H('Argument "media" is required.',"Provide --media /path/to/file.jpg or --media /path/a.jpg,/path/b.mp4");if(j.length>x)throw new H(`Too many media items: ${j.length}`,`Instagram carousel posts support at most ${x} items`);return j.map((K)=>{const N=S.resolve(String(K||"").trim());if(!N)throw new H("Media path cannot be empty");if(!P.existsSync(N))throw new H(`Media file not found: ${N}`);const O=S.extname(N).toLowerCase();if(Qj.has(O))return{type:"image",filePath:N};if(Tj.has(O))return{type:"video",filePath:N};throw new H(`Unsupported media format: ${O}`,"Supported formats: images (.jpg, .jpeg, .png, .webp) and videos (.mp4)")})}function _j(j){const G=String(j.media??"").trim();return Zj(G.split(",").map((K)=>K.trim()).filter(Boolean))}function $j(j){if(j.media===void 0)throw new H('Argument "media" is required.',"Provide --media /path/to/file.jpg or --media /path/a.jpg,/path/b.mp4")}function qj(j){if(!(j instanceof T))return!1;return j.message.startsWith("Instagram private publish")||j.message.startsWith("Instagram private route")}function R(j,G){return[{status:"✅ Posted",detail:Aj(j),url:G}]}function zj(j,G){const K=j instanceof Error?j.message:String(j),N=G instanceof Error?G.message:String(G);return`Private route failed first: ${K}. UI fallback then failed: ${N}`}async function Hj(j){const G=await Gj(j.page),K=j.mediaItems.every((O)=>O.type==="image")?await jj({page:j.page,imagePaths:j.mediaItems.map((O)=>O.filePath),caption:j.content,apiContext:G.apiContext,jazoest:G.jazoest}):await e({page:j.page,mediaItems:j.mediaItems,caption:j.content,apiContext:G.apiContext,jazoest:G.jazoest}),N=K.code?new URL(`/p/${K.code}/`,J).toString():j.canDiffProfile?await h(j.page,j.existingPostPaths):"";return R(j.mediaItems,N)}async function Jj(j){let G,K=null;for(let N=0;N<j.commandAttemptBudget;N++){let O=!1;try{await k(j.page,j.forceFreshStart||N>0);await j.installProtocolCapture();await j.page.wait({time:2});await A(j.page);await y(j.page);const Q=await v(j.page,j.mediaItems);if(j.preUploadDelaySeconds>0)await j.page.wait({time:j.preUploadDelaySeconds});let Y=!1,X=null;for(const q of Q){let V=q;for(let L=0;L<j.uploadAttemptBudget;L++){await Bj(j.page,j.mediaItems,V);const B=await c(j.page,j.previewProbeWindowSeconds);if(B.state==="preview"){Y=!0;break}if(B.state==="failed"){X=D(B.detail);for(let F=0;F<j.inlineUploadRetryBudget;F++){if(!await kj(j.page))break;await j.page.wait({time:3});const z=await c(j.page,Math.max(3,Math.floor(j.previewProbeWindowSeconds/2)));if(z.state==="preview"){Y=!0;break}if(z.state!=="failed")break}if(Y)break;await xj(j.page);await A(j.page);if(L<j.uploadAttemptBudget-1){try{await j.drainProtocolCapture();await k(j.page,!0);await j.installProtocolCapture();await j.page.wait({time:2});await A(j.page);await y(j.page);V=await I(j.page,V,j.mediaItems);if(j.preUploadDelaySeconds>0)await j.page.wait({time:j.preUploadDelaySeconds})}catch{throw X}await j.page.wait({time:1.5});continue}break}break}if(Y)break}if(!Y){if(X)throw X;await wj(j.page,j.finalPreviewWaitSeconds)}try{await Cj(j.page)}catch(q){await fj(j.page,q)}if(j.content){await Ej(j.page,j.content);await Sj(j.page,j.content)}if(j.preShareDelaySeconds>0)await j.page.wait({time:j.preShareDelaySeconds});await d(j.page,["Share","分享"],"caption");O=!0;let _="";try{_=await g(j.page)}catch(q){if(q instanceof T&&q.message==="Instagram post share failed"&&await bj(j.page)){await j.page.wait({time:Math.max(2,j.preShareDelaySeconds)});_=await g(j.page)}else throw q}await j.drainProtocolCapture();if(!_&&j.canDiffProfile)_=await h(j.page,j.existingPostPaths);return R(j.mediaItems,_)}catch(Q){G=Q;if(Q instanceof T&&Q.message!=="Failed to open Instagram post composer")K=Q;if(Q instanceof W)throw Q;if(O){let X="";if(j.canDiffProfile)try{X=await h(j.page,j.existingPostPaths)}catch{}if(X)return R(j.mediaItems,X);const _=Q instanceof Error?Q.message:String(Q);throw new T(`Instagram share was clicked but the outcome could not be confirmed: ${_}`,"The post may already be live. Do NOT rerun this command blindly — open the profile (or rerun after checking) and confirm no new post exists first; retrying on a false failure creates a duplicate post.")}if(!(Q instanceof T)||N===j.commandAttemptBudget-1){if(Q instanceof T&&Q.message==="Failed to open Instagram post composer"&&K)throw K;throw Q}let Y=!1;if(j.mediaItems.length>=10&&j.page.closeWindow)try{await j.drainProtocolCapture();await j.page.closeWindow();Y=!0}catch{}if(!Y){await A(j.page);await j.page.wait({time:1})}}}throw G instanceof Error?G:new T("Instagram post failed")}async function y(j){const G=await j.evaluate(buildEnsureComposerOpenJs());if(!G?.ok){if(G?.reason==="auth")throw new W("www.instagram.com","Instagram login required before posting");throw new T("Failed to open Instagram post composer")}}async function A(j){for(let G=0;G<4;G++){if(!(await j.evaluate(`
|
|
65
65
|
(() => {
|
|
66
66
|
const isVisible = (el) => {
|
|
67
67
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -105,7 +105,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
105
105
|
|
|
106
106
|
return { ok: false };
|
|
107
107
|
})()
|
|
108
|
-
`))?.ok)return;await j.wait({time:0.5})}}async function
|
|
108
|
+
`))?.ok)return;await j.wait({time:0.5})}}async function w(j,G){const K=G.some((O)=>O.type==="video"),N=await j.evaluate(`
|
|
109
109
|
((includesVideo) => {
|
|
110
110
|
const isVisible = (el) => {
|
|
111
111
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -164,55 +164,8 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
164
164
|
return '[data-opencli-ig-upload-index="' + index + '"]';
|
|
165
165
|
});
|
|
166
166
|
return { ok: true, selectors };
|
|
167
|
-
})(${JSON.stringify(
|
|
168
|
-
`);if(!
|
|
169
|
-
(() => {
|
|
170
|
-
window[${JSON.stringify(Q)}] = [];
|
|
171
|
-
return { ok: true };
|
|
172
|
-
})()
|
|
173
|
-
`);const _=JSON.stringify(O);for(let $=0;$<_.length;$+=X){const q=_.slice($,$+X);await j.evaluate(`
|
|
174
|
-
(() => {
|
|
175
|
-
const key = ${JSON.stringify(Q)};
|
|
176
|
-
const chunk = ${JSON.stringify(q)};
|
|
177
|
-
const parts = Array.isArray(window[key]) ? window[key] : [];
|
|
178
|
-
parts.push(chunk);
|
|
179
|
-
window[key] = parts;
|
|
180
|
-
return { ok: true, count: parts.length };
|
|
181
|
-
})()
|
|
182
|
-
`)}const Z=await j.evaluate(`
|
|
183
|
-
(() => {
|
|
184
|
-
const selector = ${JSON.stringify(N)};
|
|
185
|
-
const key = ${JSON.stringify(Q)};
|
|
186
|
-
const payload = JSON.parse(Array.isArray(window[key]) ? window[key].join('') : '[]');
|
|
187
|
-
|
|
188
|
-
const cleanup = () => { try { delete window[key]; } catch {} };
|
|
189
|
-
const input = document.querySelector(selector);
|
|
190
|
-
if (!(input instanceof HTMLInputElement)) {
|
|
191
|
-
cleanup();
|
|
192
|
-
return { ok: false, error: 'File input not found for fallback injection' };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
try {
|
|
196
|
-
const dt = new DataTransfer();
|
|
197
|
-
for (const img of payload) {
|
|
198
|
-
const binary = atob(img.base64);
|
|
199
|
-
const bytes = new Uint8Array(binary.length);
|
|
200
|
-
for (let i = 0; i < binary.length; i++) bytes[i] = binary.charCodeAt(i);
|
|
201
|
-
const blob = new Blob([bytes], { type: img.type });
|
|
202
|
-
const file = new File([blob], img.name, { type: img.type });
|
|
203
|
-
dt.items.add(file);
|
|
204
|
-
}
|
|
205
|
-
Object.defineProperty(input, 'files', { value: dt.files, configurable: true });
|
|
206
|
-
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
207
|
-
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
208
|
-
cleanup();
|
|
209
|
-
return { ok: true, count: dt.files.length };
|
|
210
|
-
} catch (error) {
|
|
211
|
-
cleanup();
|
|
212
|
-
return { ok: false, error: String(error) };
|
|
213
|
-
}
|
|
214
|
-
})()
|
|
215
|
-
`);if(!Z?.ok)throw new Y(Z?.error||"Instagram fallback file injection failed")}async function Bj(j,G){await j.evaluate(`
|
|
167
|
+
})(${JSON.stringify(K)})
|
|
168
|
+
`);if(!N?.ok||!N.selectors?.length)throw new T("Instagram upload input not found","Open the new-post composer in a logged-in browser session and retry");return N.selectors}async function v(j,G){try{return await w(j,G)}catch(K){if(!(K instanceof T)||!K.message.includes("upload input not found"))throw K;await y(j);await j.wait({time:1.5});try{return await w(j,G)}catch(N){if(!(N instanceof T)||!N.message.includes("upload input not found"))throw N;await k(j,!0);await j.wait({time:2});await A(j);await y(j);await j.wait({time:2});return w(j,G)}}}function Vj(j){const G=j.match(/data-opencli-ig-upload-index="(\d+)"/);if(!G)return null;const K=Number.parseInt(G[1]||"",10);return Number.isNaN(K)?null:K}async function I(j,G,K){const N=await v(j,K),O=Vj(G);if(O!==null&&N[O])return N[O];return N[0]||G}async function Lj(j,G){await j.evaluate(`
|
|
216
169
|
(() => {
|
|
217
170
|
const input = document.querySelector(${JSON.stringify(G)});
|
|
218
171
|
if (!(input instanceof HTMLInputElement)) return { ok: false };
|
|
@@ -255,7 +208,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
255
208
|
if (failed) return { state: 'failed', detail: dialogText || 'Something went wrong' };
|
|
256
209
|
return { state: 'pending', detail: dialogText || '' };
|
|
257
210
|
})()
|
|
258
|
-
`}async function M(j){const G=await j.evaluate(buildInspectUploadStageJs());if(G?.state)return G;if(G?.ok===!0)return{state:"preview",detail:G.detail};return{state:"pending",detail:G?.detail}}function D(j){return new
|
|
211
|
+
`}async function M(j){const G=await j.evaluate(buildInspectUploadStageJs());if(G?.state)return G;if(G?.ok===!0)return{state:"preview",detail:G.detail};return{state:"pending",detail:G?.detail}}function D(j){return new T("Instagram image upload failed",j?`Instagram rejected the upload: ${j}`:"Instagram rejected the upload before the preview stage")}async function Bj(j,G,K){const N=G.map((Q)=>Q.filePath);let O=K;for(let Q=0;Q<2;Q++)try{if((await Oj(j,O,N)).method==="cdp")await Lj(j,O);return}catch(Y){const X=Y instanceof Error?Y.message:String(Y);if((X.includes("No element found matching selector")||X.includes("Could not find node with given id")||X.includes("No node with given id found")||X.includes("未找到可用的文件上传输入框"))&&Q===0){O=await I(j,O,G);continue}throw Y}}function Aj(j){if(j.every((G)=>G.type==="image"))return j.length===1?"Single image post shared successfully":`${j.length}-image carousel post shared successfully`;return j.length===1?"Single mixed-media post shared successfully":`${j.length}-item mixed-media carousel post shared successfully`}function Mj(j){if(j.length>=10)return 6;if(j.length>=5)return 4;return 3}function Dj(j){if(j.length>=10)return 3;if(j.length>=5)return 1.5;return 0}function Fj(j){if(j.length>=10)return 3;if(j.length>=5)return 3;return 2}function Wj(j){if(j.length>=10)return 6;if(j.length>=5)return 6;return 4}function Rj(j){if(j.length>=10)return 12;if(j.length>=5)return 16;return 12}function yj(j){if(j.length>=10)return 4;if(j.length>=5)return 3;return 0}function hj(j){if(j.length>=10)return 3;if(j.length>=5)return 2;return 1}async function xj(j){return!!(await j.evaluate(`
|
|
259
212
|
(() => {
|
|
260
213
|
const isVisible = (el) => {
|
|
261
214
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -278,7 +231,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
278
231
|
}
|
|
279
232
|
return { ok: false };
|
|
280
233
|
})()
|
|
281
|
-
`))?.ok}async function
|
|
234
|
+
`))?.ok}async function kj(j){return!!(await j.evaluate(`
|
|
282
235
|
(() => {
|
|
283
236
|
const isVisible = (el) => {
|
|
284
237
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -312,7 +265,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
312
265
|
}
|
|
313
266
|
return { ok: false };
|
|
314
267
|
})()
|
|
315
|
-
`))?.ok}async function
|
|
268
|
+
`))?.ok}async function wj(j,G=12){const K=Math.max(1,Math.ceil(G));for(let N=0;N<K;N++){const O=await M(j);if(O.state==="preview")return;if(O.state==="failed"){await j.screenshot({path:"/tmp/instagram_post_preview_debug.png"});throw D("Inspect /tmp/instagram_post_preview_debug.png. "+(O.detail||""))}if(N<K-1)await j.wait({time:1})}await j.screenshot({path:"/tmp/instagram_post_preview_debug.png"});throw new T("Instagram image preview did not appear after upload","The selected file input may not match the active composer; inspect /tmp/instagram_post_preview_debug.png")}async function c(j,G=4){const K=Math.max(1,Math.ceil(G*2));for(let N=0;N<K;N++){const O=await M(j);if(O.state!=="pending")return O;if(N<K-1)await j.wait({time:0.5})}return{state:"pending"}}export function buildClickActionJs(j,G="any"){return`
|
|
316
269
|
((labels, scope) => {
|
|
317
270
|
const isVisible = (el) => {
|
|
318
271
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -367,7 +320,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
367
320
|
}
|
|
368
321
|
return { ok: false };
|
|
369
322
|
})(${JSON.stringify(j)}, ${JSON.stringify(G)})
|
|
370
|
-
`}async function d(j,G,
|
|
323
|
+
`}async function d(j,G,K="any"){const N=await j.evaluate(buildClickActionJs(G,K));if(!N?.ok)throw new T(`Instagram action button not found: ${G.join(" / ")}`);return N.label||G[0]}async function bj(j){return!!(await j.evaluate(`
|
|
371
324
|
(() => {
|
|
372
325
|
const isVisible = (el) => {
|
|
373
326
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -405,12 +358,12 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
405
358
|
|
|
406
359
|
return { ok: false };
|
|
407
360
|
})()
|
|
408
|
-
`))?.ok}async function
|
|
361
|
+
`))?.ok}async function m(j){return!!(await j.evaluate(`
|
|
409
362
|
(() => {
|
|
410
363
|
const editable = document.querySelector('textarea, [contenteditable="true"]');
|
|
411
364
|
return { ok: !!editable };
|
|
412
365
|
})()
|
|
413
|
-
`))?.ok}async function
|
|
366
|
+
`))?.ok}async function l(j){return!!(await j.evaluate(`
|
|
414
367
|
(() => {
|
|
415
368
|
const editable = document.querySelector('textarea, [contenteditable="true"]');
|
|
416
369
|
const dialogText = Array.from(document.querySelectorAll('[role="dialog"]'))
|
|
@@ -424,7 +377,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
424
377
|
|| dialogText.includes('advanced settings'),
|
|
425
378
|
};
|
|
426
379
|
})()
|
|
427
|
-
`))?.ok}async function
|
|
380
|
+
`))?.ok}async function Cj(j){for(let G=0;G<3;G++){if(await l(j))return;try{await d(j,["Next","下一步"],"media")}catch(N){if(N instanceof T){await j.wait({time:1.5});if(await l(j))return;const O=await M(j);if(O.state==="failed")throw D(O.detail);if(G<2)continue}throw N}await j.wait({time:1.5});if(await m(j))return;const K=await M(j);if(K.state==="failed")throw D(K.detail)}await j.screenshot({path:"/tmp/instagram_post_caption_debug.png"});throw new T("Instagram caption editor did not appear","Instagram may have changed the publish flow; inspect /tmp/instagram_post_caption_debug.png")}async function sj(j){if(!await m(j)){await j.screenshot({path:"/tmp/instagram_post_caption_debug.png"});throw new T("Instagram caption editor did not appear","Instagram may have changed the publish flow; inspect /tmp/instagram_post_caption_debug.png")}}async function fj(j,G){const K=await M(j);if(K.state==="failed")throw D(K.detail);throw G}async function Uj(j){return!!(await j.evaluate(`
|
|
428
381
|
(() => {
|
|
429
382
|
const textarea = document.querySelector('[aria-label="Write a caption..."], textarea');
|
|
430
383
|
if (textarea instanceof HTMLTextAreaElement) {
|
|
@@ -484,7 +437,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
484
437
|
|
|
485
438
|
return { ok: true, kind: 'contenteditable' };
|
|
486
439
|
})()
|
|
487
|
-
`))?.ok}async function
|
|
440
|
+
`))?.ok}async function Ej(j,G){if(j.insertText&&await Uj(j))try{await j.insertText(G);await j.wait({time:0.3});await j.evaluate(`
|
|
488
441
|
(() => {
|
|
489
442
|
const textarea = document.querySelector('[aria-label="Write a caption..."], textarea');
|
|
490
443
|
if (textarea instanceof HTMLTextAreaElement) {
|
|
@@ -596,7 +549,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
596
549
|
|
|
597
550
|
return { ok: false };
|
|
598
551
|
})(${JSON.stringify(G)})
|
|
599
|
-
`))?.ok)throw new
|
|
552
|
+
`))?.ok)throw new T("Failed to fill Instagram caption")}async function Pj(j,G){return!!(await j.evaluate(`
|
|
600
553
|
((content) => {
|
|
601
554
|
const normalized = content.trim();
|
|
602
555
|
const readLexicalText = (node) => {
|
|
@@ -650,10 +603,10 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
650
603
|
|
|
651
604
|
return { ok: false };
|
|
652
605
|
})(${JSON.stringify(G)})
|
|
653
|
-
`))?.ok}async function
|
|
606
|
+
`))?.ok}async function Sj(j,G){for(let K=0;K<6;K++){if(await Pj(j,G))return;if(K<5)await j.wait({time:0.5})}await j.screenshot({path:"/tmp/instagram_post_caption_fill_debug.png"});throw new T("Instagram caption did not stick before sharing","Inspect /tmp/instagram_post_caption_fill_debug.png for the caption editor state")}async function g(j){let G=0;for(let K=0;K<90;K++){const N=await j.evaluate(buildPublishStatusProbeJs());if(N?.failed){await j.screenshot({path:"/tmp/instagram_post_share_debug.png"});throw new T("Instagram post share failed","Inspect /tmp/instagram_post_share_debug.png for the share failure state")}if(N?.ok)return N.url||"";if(N?.settled){G+=1;if(G>=3)return""}else G=0;if(K<89)await j.wait({time:1})}await j.screenshot({path:"/tmp/instagram_post_share_debug.png"});throw new T("Instagram post share confirmation did not appear","Inspect /tmp/instagram_post_share_debug.png for the final publish state")}async function s(j){return(await j.getCookies({domain:"instagram.com"})).find((K)=>K.name==="ds_user_id")?.value||""}async function o(j,G=""){if(G){const N=await Nj(j),O=await j.evaluate(`
|
|
654
607
|
(async () => {
|
|
655
608
|
const userId = ${JSON.stringify(G)};
|
|
656
|
-
const appId = ${JSON.stringify(
|
|
609
|
+
const appId = ${JSON.stringify(N.appId||"")};
|
|
657
610
|
try {
|
|
658
611
|
const res = await fetch(
|
|
659
612
|
'https://www.instagram.com/api/v1/users/' + encodeURIComponent(userId) + '/info/',
|
|
@@ -670,7 +623,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
670
623
|
return { ok: false };
|
|
671
624
|
}
|
|
672
625
|
})()
|
|
673
|
-
`);if(
|
|
626
|
+
`);if(O?.ok&&O.username)return new URL(`/${O.username}/`,J).toString()}const K=await j.evaluate(`
|
|
674
627
|
(() => {
|
|
675
628
|
const isVisible = (el) => {
|
|
676
629
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -695,7 +648,7 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
695
648
|
const path = explicitProfile;
|
|
696
649
|
return { ok: !!path, path };
|
|
697
650
|
})()
|
|
698
|
-
`);if(!
|
|
651
|
+
`);if(!K?.ok||!K.path)return"";return new URL(K.path,J).toString()}async function n(j){const G=await j.evaluate(`
|
|
699
652
|
(() => {
|
|
700
653
|
const isVisible = (el) => {
|
|
701
654
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -715,4 +668,4 @@ import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy a
|
|
|
715
668
|
|
|
716
669
|
return { ok: hrefs.length > 0, hrefs };
|
|
717
670
|
})()
|
|
718
|
-
`);return Array.isArray(G?.hrefs)?G.hrefs.filter(Boolean):[]}async function
|
|
671
|
+
`);return Array.isArray(G?.hrefs)?G.hrefs.filter(Boolean):[]}async function vj(j){const G=await s(j);if(!G)return null;const K=await o(j,G);if(!K)return null;try{await j.goto(K);await j.wait({time:3});return new Set(await n(j))}catch{return null}}async function h(j,G){const K=await j.getCurrentUrl?.();if(K&&/\/p\//.test(K))return K;const N=await s(j),O=await o(j,N);if(!O)return"";await j.goto(O);await j.wait({time:4});for(let Q=0;Q<8;Q++){const X=(await n(j)).find((_)=>!G.has(_))||"";if(X)return new URL(X,J).toString();if(Q<7)await j.wait({time:1})}return""}r({site:"instagram",name:"post",access:"write",description:"Post an Instagram feed image or mixed-media carousel",domain:"www.instagram.com",strategy:p.UI,browser:!0,args:[{name:"media",required:!1,valueRequired:!0,help:`Comma-separated media paths (images/videos, up to ${x})`},{name:"content",positional:!0,required:!1,help:"Caption text"},{name:"timeout",type:"int",required:!1,default:300,help:"Max seconds for the overall command (default: 300)"}],columns:["status","detail","url"],validateArgs:$j,func:async(j,G)=>{const K=Yj(j),N=_j(G),O=String(G.content??"").trim(),Q=await vj(K),Y=Q!==null,X=Q??new Set,_=Mj(N),q=Dj(N),V=Fj(N),L=Wj(N),B=Rj(N),F=yj(N),b=hj(N),z=process.env.OPENCLI_INSTAGRAM_CAPTURE==="1",C=[],f=[],u=async()=>{if(!z)return;await a(K)},U=async()=>{if(!z)return;const Z=await t(K);if(Z.data.length)C.push(...Z.data);if(Z.errors.length)f.push(...Z.errors)};try{try{return await Hj({page:K,mediaItems:N,content:O,existingPostPaths:X,canDiffProfile:Y})}catch(Z){if(Z instanceof W)throw Z;if(Kj(Z)){let $="",E=!1;if(Y)try{$=await h(K,X);E=!0}catch{}if($)return R(N,$);if(!E){const i=Z instanceof Error?Z.message:String(Z);throw new T(`Instagram publish outcome uncertain: ${i}`,"The post may already be live. Do NOT rerun this command blindly — check the profile for a new post first; retrying on a false failure creates a duplicate post.")}}if(!qj(Z))throw Z;try{return await Jj({page:K,mediaItems:N,content:O,existingPostPaths:X,canDiffProfile:Y,commandAttemptBudget:_,preUploadDelaySeconds:q,uploadAttemptBudget:V,previewProbeWindowSeconds:L,finalPreviewWaitSeconds:B,preShareDelaySeconds:F,inlineUploadRetryBudget:b,installProtocolCapture:u,drainProtocolCapture:U,forceFreshStart:!0})}catch($){if($ instanceof W)throw $;if($ instanceof T)throw new T($.message,zj(Z,$));throw $}}}finally{if(z){try{await U()}catch{}P.writeFileSync(Xj,JSON.stringify({data:C,errors:f},null,2))}}}});
|
package/clis/instagram/reel.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";import{Page as A}from"@jackwener/opencli/browser/page";import{cli as b,Strategy as I}from"@jackwener/opencli/registry";import{ArgumentError as G,AuthRequiredError as R,CommandExecutionError as Z}from"@jackwener/opencli/errors";import{buildClickActionJs as W,buildEnsureComposerOpenJs as x,buildInspectUploadStageJs as S}from"./post.js";import{resolveInstagramRuntimeInfo as U}from"./_shared/runtime-info.js";const H="https://www.instagram.com/",
|
|
1
|
+
import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";import{Page as A}from"@jackwener/opencli/browser/page";import{cli as b,Strategy as I}from"@jackwener/opencli/registry";import{ArgumentError as G,AuthRequiredError as R,CommandExecutionError as Z}from"@jackwener/opencli/errors";import{buildClickActionJs as W,buildEnsureComposerOpenJs as x,buildInspectUploadStageJs as S}from"./post.js";import{resolveInstagramRuntimeInfo as U}from"./_shared/runtime-info.js";import{setInputFiles as f}from"../_shared/file-inject.js";const H="https://www.instagram.com/",h=new Set([".mp4"]),C=600;function m(j){if(!j)throw new Z("Browser session required for instagram reel");return j}async function v(j,z=!1){if(z){await j.goto(`${H}?__opencli_reset=${Date.now()}`);await j.wait({time:1})}await j.goto(H)}function u(j){const z=F.resolve(String(j||"").trim());if(!z)throw new G("Video path cannot be empty");if(!L.existsSync(z))throw new G(`Video file not found: ${z}`);const B=F.extname(z).toLowerCase();if(!h.has(B))throw new G(`Unsupported video format: ${B}`,"Supported formats: .mp4");return z}function P(j){if(j.video===void 0)throw new G('Argument "video" is required.',"Provide --video /path/to/file.mp4")}function n(j){return[{status:"✅ Posted",detail:"Single reel shared successfully",url:j}]}function d(j){if(!(j instanceof Z))return!1;return j.message==="Instagram reel upload input not found"||j.message==="Instagram reel preview did not appear after upload"||j.message==="Instagram reel upload failed"}function c(j){const z=F.extname(j).toLowerCase()||".mp4";return F.join(E.tmpdir(),`opencli-instagram-video-real${z}`)}function o(j){const z=F.basename(j);if(/^[a-zA-Z0-9._-]+$/.test(z))return{originalPath:j,uploadPath:j};const B=c(j);L.copyFileSync(j,B);return{originalPath:j,uploadPath:B,cleanupPath:B}}async function s(j){const z=await j.evaluate(x());if(!z?.ok){if(z?.reason==="auth")throw new R("www.instagram.com","Instagram login required before posting a reel");throw new Z("Failed to open Instagram reel composer")}for(let B=0;B<12;B+=1){if((await j.evaluate(`
|
|
2
2
|
(() => {
|
|
3
3
|
const isVisible = (el) => {
|
|
4
4
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -17,7 +17,7 @@ import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";
|
|
|
17
17
|
});
|
|
18
18
|
return { ok: inputs.length > 0 };
|
|
19
19
|
})()
|
|
20
|
-
`))?.ok)return;if(B<11)await j.wait({time:0.5})}throw new Z("Instagram reel upload input not found","Open the new-post composer in a logged-in browser session and retry")}async function
|
|
20
|
+
`))?.ok)return;if(B<11)await j.wait({time:0.5})}throw new Z("Instagram reel upload input not found","Open the new-post composer in a logged-in browser session and retry")}async function l(j){for(let z=0;z<4;z+=1){if(!(await j.evaluate(`
|
|
21
21
|
(() => {
|
|
22
22
|
const isVisible = (el) => {
|
|
23
23
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -55,7 +55,7 @@ import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";
|
|
|
55
55
|
}
|
|
56
56
|
return { ok: false };
|
|
57
57
|
})()
|
|
58
|
-
`))?.ok)return;await j.wait({time:0.5})}}async function
|
|
58
|
+
`))?.ok)return;await j.wait({time:0.5})}}async function i(j){const z=await j.evaluate(`
|
|
59
59
|
(() => {
|
|
60
60
|
const isVisible = (el) => {
|
|
61
61
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -87,13 +87,13 @@ import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";
|
|
|
87
87
|
|
|
88
88
|
return { ok: selectors.length > 0, selectors };
|
|
89
89
|
})()
|
|
90
|
-
`);if(!z?.ok||!Array.isArray(z.selectors)||z.selectors.length===0)throw new Z("Instagram reel upload input not found","Open the new-post composer in a logged-in browser session and retry");return z.selectors}async function
|
|
90
|
+
`);if(!z?.ok||!Array.isArray(z.selectors)||z.selectors.length===0)throw new Z("Instagram reel upload input not found","Open the new-post composer in a logged-in browser session and retry");return z.selectors}async function g(j,z,B){await f(j,B,[z])}async function p(j,z){const B=await j.evaluate(`
|
|
91
91
|
(() => {
|
|
92
92
|
const input = document.querySelector(${JSON.stringify(z)});
|
|
93
93
|
if (!(input instanceof HTMLInputElement)) return { count: null };
|
|
94
94
|
return { count: input.files?.length || 0 };
|
|
95
95
|
})()
|
|
96
|
-
`);if(B?.count===null||B?.count===void 0)return null;return Number(B.count)}async function
|
|
96
|
+
`);if(B?.count===null||B?.count===void 0)return null;return Number(B.count)}async function r(j,z=20){let B="";for(let Q=0;Q<z*2;Q+=1){const Y=await j.evaluate(S());B=String(Y?.detail||"").trim();if(Y?.state==="preview")return;if(Y?.state==="failed")throw new Z("Instagram reel upload failed",Y.detail?`Instagram rejected the reel upload: ${Y.detail}`:"Instagram rejected the reel upload before the preview stage");if(Q<z*2-1)await j.wait({time:0.5})}await j.screenshot({path:"/tmp/instagram_reel_preview_debug.png"});throw new Z("Instagram reel preview did not appear after upload",B?`Inspect /tmp/instagram_reel_preview_debug.png. Last visible dialog text: ${B}`:"Inspect /tmp/instagram_reel_preview_debug.png for the upload state")}async function D(j,z,B="any"){const Q=await j.evaluate(W(z,B));if(!Q?.ok)throw new Z(`Instagram action button not found: ${z.join(" / ")}`);return Q.label||z[0]||""}async function t(j,z,B="any"){return!!(await j.evaluate(W(z,B)))?.ok}function a(){return`
|
|
97
97
|
(() => {
|
|
98
98
|
const isVisible = (el) => {
|
|
99
99
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -127,7 +127,7 @@ import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";
|
|
|
127
127
|
}
|
|
128
128
|
return { state: 'pending', detail: text };
|
|
129
129
|
})()
|
|
130
|
-
`}async function O(j,z,B=20){for(let Q=0;Q<B*2;Q+=1){const Y=await j.evaluate(
|
|
130
|
+
`}async function O(j,z,B=20){for(let Q=0;Q<B*2;Q+=1){const Y=await j.evaluate(a());if(Y?.state===z)return;if(Y?.state==="failed")throw new Z("Instagram reel editor did not appear",Y.detail?`Instagram reel flow failed: ${Y.detail}`:"Instagram reel flow failed before the next editor stage");if(Q<B*2-1)await j.wait({time:0.5})}throw new Z(`Instagram reel ${z} editor did not appear`)}async function e(j){return!!(await j.evaluate(`
|
|
131
131
|
(() => {
|
|
132
132
|
const isVisible = (el) => {
|
|
133
133
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -199,7 +199,7 @@ import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";
|
|
|
199
199
|
}
|
|
200
200
|
return { ok: false };
|
|
201
201
|
})()
|
|
202
|
-
`))?.ok}async function
|
|
202
|
+
`))?.ok}async function jj(j,z){return!!(await j.evaluate(`
|
|
203
203
|
(() => {
|
|
204
204
|
const target = ${JSON.stringify(z.trim())}.replace(/\\u00a0/g, ' ').replace(/\\s+/g, ' ').trim();
|
|
205
205
|
const isVisible = (el) => {
|
|
@@ -253,7 +253,7 @@ import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";
|
|
|
253
253
|
}
|
|
254
254
|
return { ok: false };
|
|
255
255
|
})()
|
|
256
|
-
`))?.ok}async function
|
|
256
|
+
`))?.ok}async function zj(j,z){if(!await e(j))throw new Z("Instagram reel caption editor did not appear");if(j.insertText)try{await j.insertText(z);await j.wait({time:0.3});await j.evaluate(`
|
|
257
257
|
(() => {
|
|
258
258
|
const isVisible = (el) => {
|
|
259
259
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -383,7 +383,7 @@ import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";
|
|
|
383
383
|
}
|
|
384
384
|
return { ok: false };
|
|
385
385
|
})(${JSON.stringify(z)})
|
|
386
|
-
`)}async function
|
|
386
|
+
`)}async function Bj(j,z){for(let B=0;B<6;B+=1){if(await jj(j,z))return;if(B<5)await j.wait({time:0.5})}throw new Z("Instagram reel caption did not stick before sharing")}function Kj(){return`
|
|
387
387
|
(() => {
|
|
388
388
|
const isVisible = (el) => {
|
|
389
389
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -411,7 +411,7 @@ import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";
|
|
|
411
411
|
const settled = !shared && !composerOpen && !sharingVisible;
|
|
412
412
|
return { ok: shared, failed, settled, url: /\\/reel\\//.test(url) ? url : '' };
|
|
413
413
|
})()
|
|
414
|
-
`}async function
|
|
414
|
+
`}async function Qj(j){let z=0;for(let B=0;B<120;B+=1){const Q=await j.evaluate(Kj());if(Q?.failed)throw new Z("Instagram reel share failed");if(Q?.ok)return Q.url||"";if(Q?.settled){z+=1;if(z>=3)return""}else z=0;if(B<119)await j.wait({time:1})}throw new Z("Instagram reel share confirmation did not appear")}async function _(j){return(await j.getCookies({domain:"instagram.com"})).find((B)=>B.name==="ds_user_id")?.value||""}async function w(j,z=""){if(z){const B=await U(j),Q=await j.evaluate(`
|
|
415
415
|
(async () => {
|
|
416
416
|
const userId = ${JSON.stringify(z)};
|
|
417
417
|
const appId = ${JSON.stringify(B.appId||"")};
|
|
@@ -449,4 +449,4 @@ import*as L from"node:fs";import*as E from"node:os";import*as F from"node:path";
|
|
|
449
449
|
.filter((href, index, arr) => arr.indexOf(href) === index);
|
|
450
450
|
return { hrefs };
|
|
451
451
|
})()
|
|
452
|
-
`);return Array.isArray(z?.hrefs)?z.hrefs.filter(Boolean):[]}async function
|
|
452
|
+
`);return Array.isArray(z?.hrefs)?z.hrefs.filter(Boolean):[]}async function Yj(j){const z=await _(j);if(!z)return new Set;const B=await w(j,z);if(!B)return new Set;try{await j.goto(B);await j.wait({time:3});return new Set(await M(j))}catch{return new Set}}async function Zj(j,z){const B=await j.getCurrentUrl?.();if(B&&/\/reel\//.test(B))return B;const Q=await _(j),Y=await w(j,Q);if(!Y)return"";await j.goto(Y);await j.wait({time:4});for(let q=0;q<8;q+=1){const y=await M(j),K=y.find(($)=>$.includes("/reel/")&&!z.has($))||y.find(($)=>!z.has($))||"";if(K)return new URL(K,H).toString();if(q<7)await j.wait({time:1})}return""}b({site:"instagram",name:"reel",access:"write",description:"Post an Instagram reel video",domain:"www.instagram.com",strategy:I.UI,browser:!0,args:[{name:"video",required:!1,valueRequired:!0,help:"Path to a single .mp4 video file"},{name:"content",positional:!0,required:!1,help:"Caption text"},{name:"timeout",type:"int",required:!1,default:C,help:`Max seconds for the overall command (default: ${C})`}],columns:["status","detail","url"],validateArgs:P,func:async(j,z)=>{const B=m(j),Q=u(z.video),Y=String(z.content??"").trim(),q=o(Q),y=async(K,$=new Set)=>{if(typeof K.startNetworkCapture==="function")await K.startNetworkCapture("/rupload_igvideo/|/api/v1/|/reel/|/clips/|/media/|/configure|/upload");await v(K,!0);await K.wait({time:2});await l(K);await s(K);await K.wait({time:2});const X=await i(K);let V=!1,J;for(const N of X)try{await g(K,q.uploadPath,N);if(await p(K,N)===0)throw new Z("Instagram reel upload failed","The selected reel input never received the video file");await r(K,10);V=!0;break}catch(T){J=T}if(!V)throw J instanceof Error?J:new Z("Instagram reel preview did not appear after upload");await t(K,["OK"],"any");await D(K,["Next","下一步"],"media");await O(K,"edit",20);await D(K,["Next","下一步"],"media");await O(K,"composer",20);if(Y){await zj(K,Y);await Bj(K,Y)}await D(K,["Share","分享"],"caption");const k=await Qj(K)||await Zj(K,$);return n(k)};try{if(!process.env.VITEST){const $=async()=>{const X=new A(`site:instagram-reel-${Date.now()}`);try{return await y(X,new Set)}finally{await X.closeWindow?.()}};try{return await $()}catch(X){if(!d(X))throw X;return await $()}}const K=await Yj(B);return await y(B,K)}finally{if(q.cleanupPath)L.rmSync(q.cleanupPath,{force:!0})}}});
|