publishport-opencli 1.0.7 → 1.0.9
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/cli-manifest.json +445 -3
- package/clis/facebook/feed.js +78 -5
- package/clis/facebook/search.js +83 -22
- package/clis/instagram/_shared/private-publish.js +4 -4
- package/clis/instagram/post.js +20 -20
- package/clis/xianyu/collect.js +1 -0
- package/clis/xianyu/location.js +1 -0
- package/clis/xianyu/manage.js +1 -0
- package/clis/xianyu/mtop.js +47 -0
- package/clis/xianyu/order.js +1 -0
- package/clis/xianyu/rate.js +1 -0
- package/clis/xianyu/user.js +1 -0
- package/clis/xiaohongshu/publish.js +63 -44
- package/dist/src/browser/base-page.d.ts +19 -0
- package/dist/src/browser/base-page.js +1 -1
- package/dist/src/browser/target-resolver.d.ts +1 -0
- package/dist/src/browser/target-resolver.js +100 -6
- package/dist/src/cli.js +1 -1
- package/dist/src/external.js +1 -1
- package/dist/src/types.d.ts +3 -0
- package/package.json +1 -1
package/clis/facebook/search.js
CHANGED
|
@@ -1,22 +1,83 @@
|
|
|
1
|
-
import{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
return
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
1
|
+
import{ArgumentError as V,AuthRequiredError as j,CommandExecutionError as P,EmptyResultError as B}from"@jackwener/opencli/errors";import{cli as Q,Strategy as T}from"@jackwener/opencli/registry";const X="https://www.facebook.com",W=50;function Y(z){const D=Number(z);if(!Number.isInteger(D)||D<1||D>W)throw new V(`facebook search --limit must be an integer between 1 and ${W}`);return D}function Z(z){const D=String(z??"").trim();if(!D)throw new V("facebook search requires a non-empty query");return D}function F(z){if(z&&typeof z==="object"&&"data"in z)return z.data;return z}function $(z){return`(() => {
|
|
2
|
+
const limit = ${z};
|
|
3
|
+
|
|
4
|
+
function clean(value) {
|
|
5
|
+
return String(value || '')
|
|
6
|
+
.replace(/[\\u200b-\\u200f\\u202a-\\u202e\\u2060\\ufeff]/g, '')
|
|
7
|
+
.replace(/\\s+/g, ' ')
|
|
8
|
+
.trim();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// FB anti-scrape obfuscation: long spaceless digit tokens and spaced
|
|
12
|
+
// single-char strings ("a b c d e"). Real titles never look like this.
|
|
13
|
+
function isObfuscated(text) {
|
|
14
|
+
if (!text) return true;
|
|
15
|
+
if (/^\\d{8,}$/.test(text)) return true;
|
|
16
|
+
if (/^(?:\\S ){4,}\\S$/.test(text) && text.replace(/\\s/g, '').length <= 12) return true;
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function isEntityHref(href) {
|
|
21
|
+
if (!href) return false;
|
|
22
|
+
let u;
|
|
23
|
+
try { u = new URL(href, 'https://www.facebook.com'); } catch (e) { return false; }
|
|
24
|
+
// drop hidden-domain .com spam — real results stay on facebook.com
|
|
25
|
+
if (!/(^|\\.)facebook\\.com$/i.test(u.hostname)) return false;
|
|
26
|
+
const p = u.pathname;
|
|
27
|
+
if (/^\\/search(\\/|$)/i.test(p)) return false; // decoy links back to search (incl. bare /search)
|
|
28
|
+
// chrome / non-result destinations that the catch-all below would keep
|
|
29
|
+
if (/^\\/(login|checkpoint|help|policies|privacy|settings|bookmarks|messages|notifications|marketplace|gaming|friends|requests|saved|me)\\b/i.test(p)) return false;
|
|
30
|
+
return /^\\/(profile\\.php|groups\\/|events\\/|watch\\/|reel\\/|pages\\/|permalink\\.php|story\\.php|[^/]+\\/posts\\/|[^/]+\\/videos\\/|[A-Za-z0-9.\\-]{2,}\\/?$)/i.test(p);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function isAuthPage() {
|
|
34
|
+
const path = window.location && window.location.pathname ? window.location.pathname : '';
|
|
35
|
+
const body = clean(document.body && document.body.textContent);
|
|
36
|
+
return /^\\/(login|checkpoint)(\\/|$|\\.php)/.test(path)
|
|
37
|
+
|| /^(Log in to Facebook|Facebook登录|登录 Facebook)/i.test(body)
|
|
38
|
+
|| /You must log in to continue/i.test(body);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (isAuthPage()) return { status: 'auth', rows: [], diagnostics: {} };
|
|
42
|
+
|
|
43
|
+
const feed = document.querySelector('[role="feed"]')
|
|
44
|
+
|| document.querySelector('[role="main"]')
|
|
45
|
+
|| document.body;
|
|
46
|
+
const anchors = Array.from(feed.querySelectorAll('a[href]'));
|
|
47
|
+
const seen = new Set();
|
|
48
|
+
const rows = [];
|
|
49
|
+
for (const a of anchors) {
|
|
50
|
+
const rawHref = a.href || a.getAttribute('href') || '';
|
|
51
|
+
if (!isEntityHref(rawHref)) continue;
|
|
52
|
+
let key;
|
|
53
|
+
try { const u = new URL(rawHref, 'https://www.facebook.com'); key = u.origin + u.pathname; }
|
|
54
|
+
catch (e) { key = rawHref.split('?')[0].split('#')[0]; }
|
|
55
|
+
if (seen.has(key)) continue;
|
|
56
|
+
|
|
57
|
+
const title = clean(a.textContent).substring(0, 80);
|
|
58
|
+
if (!title || isObfuscated(title)) continue;
|
|
59
|
+
|
|
60
|
+
// climb a few levels for the surrounding card text
|
|
61
|
+
let card = a;
|
|
62
|
+
for (let i = 0; i < 4 && card.parentElement; i += 1) {
|
|
63
|
+
card = card.parentElement;
|
|
64
|
+
if (clean(card.textContent).length > title.length + 20) break;
|
|
65
|
+
}
|
|
66
|
+
const text = clean(card.textContent).substring(0, 150);
|
|
67
|
+
if (isObfuscated(text)) continue;
|
|
68
|
+
|
|
69
|
+
seen.add(key);
|
|
70
|
+
rows.push({ index: rows.length + 1, title, text, url: key });
|
|
71
|
+
if (rows.length >= limit) break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return {
|
|
75
|
+
status: rows.length ? 'ok' : 'no_rows',
|
|
76
|
+
rows,
|
|
77
|
+
diagnostics: {
|
|
78
|
+
feedFound: !!document.querySelector('[role="feed"]'),
|
|
79
|
+
anchorCount: anchors.length,
|
|
80
|
+
mainTextLength: clean((document.querySelector('[role="main"]') || {}).textContent).length,
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
})()`}async function H(z,D){const U=Z(D.query),f=Y(D.limit??10);try{await z.goto(X);await z.goto(`https://www.facebook.com/search/top?q=${encodeURIComponent(U)}`,{settleMs:4000})}catch(J){throw new P(`Failed to open facebook search: ${J instanceof Error?J.message:J}`,"Check that facebook.com is reachable and the browser extension is connected.")}let G;try{G=F(await z.evaluate($(f)))}catch(J){throw new P(`Failed to read facebook search results: ${J instanceof Error?J.message:J}`,"Facebook may not have rendered or the search markup may have changed.")}if(!G||typeof G!=="object"||!Array.isArray(G.rows))throw new P("facebook search returned malformed extraction payload");if(G.status==="auth")throw new j("www.facebook.com","Open Chrome and log in to Facebook before retrying.");if(G.rows.length>0)return G.rows;const N=G.diagnostics||{};if(N.anchorCount||N.mainTextLength>200)throw new P("facebook search page rendered but no entity results could be extracted",`Diagnostics: feed=${!!N.feedFound}, anchors=${N.anchorCount||0}, mainTextLength=${N.mainTextLength||0}.`);throw new B("facebook search",`No Facebook results were visible for "${U}".`)}const K={site:"facebook",name:"search",access:"read",description:"Search Facebook for people, pages, or posts",domain:"www.facebook.com",strategy:T.COOKIE,browser:!0,navigateBefore:!1,args:[{name:"query",required:!0,positional:!0,help:"Search query"},{name:"limit",type:"int",default:10,help:"Number of results"}],columns:["index","title","text","url"],func:H};Q(K);export const __test__={buildSearchExtractScript:$,command:K,searchFacebook:H,requireLimit:Y,requireQuery:Z};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as B from"node:crypto";import*as U from"node:fs";import*as R from"node:os";import*as V from"node:path";import{spawnSync as z}from"node:child_process";import{CommandExecutionError as K}from"@jackwener/opencli/errors";import{instagramPrivateApiFetch as j}from"./protocol-capture.js";import{buildReadInstagramRuntimeInfoJs as h}from"./runtime-info.js";export{buildReadInstagramRuntimeInfoJs,extractInstagramRuntimeInfo,resolveInstagramRuntimeInfo}from"./runtime-info.js";const C=0.8,M=1.91,D=0.5625,T=0.75,S="FFFFFF",l="https://www.instagram.com/",u="/api/v1/|/graphql/",k=2,v=2,w=20,d=2000,I=15000,n="19ce5f445dbfd9d29c59dc2a78c616a7fc090a8e018b9267bc4240a30244c53b",o="4",p={manufacturer:"samsung",model:"SM-G930F",android_version:24,android_release:"7.0"};export function markPublishOutcomeUncertain(Q){if(Q&&typeof Q==="object")Q.instagramPublishUncertain=!0;return Q}export function isPublishOutcomeUncertain(Q){return Boolean(Q&&typeof Q==="object"&&Q.instagramPublishUncertain)}export function derivePrivateApiContextFromCapture(Q){for(let X=Q.length-1;X>=0;X-=1){const Z=Q[X]?.requestHeaders??{},$={asbdId:String(Z["X-ASBD-ID"]||""),csrfToken:String(Z["X-CSRFToken"]||""),igAppId:String(Z["X-IG-App-ID"]||""),igWwwClaim:String(Z["X-IG-WWW-Claim"]||""),instagramAjax:String(Z["X-Instagram-AJAX"]||""),webSessionId:String(Z["X-Web-Session-ID"]||"")};if($.asbdId&&$.csrfToken&&$.igAppId&&$.igWwwClaim&&$.instagramAjax&&$.webSessionId)return $}return null}function i(Q){const X={};for(let Z=Q.length-1;Z>=0;Z-=1){const $=Q[Z]?.requestHeaders??{};if(!X.asbdId&&$["X-ASBD-ID"])X.asbdId=String($["X-ASBD-ID"]);if(!X.csrfToken&&$["X-CSRFToken"])X.csrfToken=String($["X-CSRFToken"]);if(!X.igAppId&&$["X-IG-App-ID"])X.igAppId=String($["X-IG-App-ID"]);if(!X.igWwwClaim&&$["X-IG-WWW-Claim"])X.igWwwClaim=String($["X-IG-WWW-Claim"]);if(!X.instagramAjax&&$["X-Instagram-AJAX"])X.instagramAjax=String($["X-Instagram-AJAX"]);if(!X.webSessionId&&$["X-Web-Session-ID"])X.webSessionId=String($["X-Web-Session-ID"])}return X}export function deriveInstagramJazoest(Q){if(!Q)return"";return`2${Array.from(Q).reduce((Z,$)=>Z+$.charCodeAt(0),0)}`}function r(Q){return new Promise((X)=>setTimeout(X,Q))}function g(Q){const X=Q instanceof Error?Q.message:String(Q);return/fetch failed|network|socket hang up|econnreset|etimedout/i.test(X)}function s(Q,X){return Q.find((Z)=>Z.name===X)?.value||""}export async function resolveInstagramPrivatePublishConfig(Q){let X;for(let Z=0;Z<k;Z+=1)try{if(typeof Q.startNetworkCapture==="function")await Q.startNetworkCapture(u);await Q.goto(`${l}?__opencli_private_probe=${Date.now()}`);await Q.wait({time:2});const[$,q,F]=await Promise.all([Q.getCookies({domain:"instagram.com"}),Q.evaluate(h()),typeof Q.readNetworkCapture==="function"?Q.readNetworkCapture():Promise.resolve([])]),O=Array.isArray(F)?F:[],G=derivePrivateApiContextFromCapture(O)??i(O),N=q?.csrfToken||s($,"csrftoken")||G.csrfToken||"",J=q?.appId||G.igAppId||"",W=q?.instagramAjax||G.instagramAjax||"";if(!N)throw new K("Instagram private route could not derive CSRF token from browser session");if(!J)throw new K("Instagram private route could not derive X-IG-App-ID from instagram runtime");if(!W)throw new K("Instagram private route could not derive X-Instagram-AJAX from instagram runtime");const H=G.asbdId||"",Y=G.igWwwClaim||"",y=G.webSessionId||"";return{apiContext:{asbdId:H,csrfToken:N,igAppId:J,igWwwClaim:Y,instagramAjax:W,webSessionId:y},jazoest:deriveInstagramJazoest(N)}}catch($){X=$;if(!g($)||Z>=k-1)throw $}throw X instanceof Error?X:Error(String(X))}export function buildConfigureBody(Q){const X=new URLSearchParams;X.set("archive_only","false");X.set("caption",Q.caption);X.set("clips_share_preview_to_feed","1");X.set("disable_comments","0");X.set("disable_oa_reuse","false");X.set("igtv_share_preview_to_feed","1");X.set("is_meta_only_post","0");X.set("is_unified_video","1");X.set("like_and_view_counts_disabled","0");X.set("media_share_flow","creation_flow");X.set("share_to_facebook","");X.set("share_to_fb_destination_type","USER");X.set("source_type","library");X.set("upload_id",Q.uploadId);X.set("video_subtitles_enabled","0");X.set("jazoest",Q.jazoest);return X.toString()}export function buildConfigureSidecarPayload(Q){return{archive_only:!1,caption:Q.caption,children_metadata:Q.uploadIds.map((X)=>({upload_id:X})),client_sidecar_id:Q.clientSidecarId,disable_comments:"0",is_meta_only_post:!1,is_open_to_public_submission:!1,like_and_view_counts_disabled:0,media_share_flow:"creation_flow",share_to_facebook:"",share_to_fb_destination_type:"USER",source_type:"library",jazoest:Q.jazoest}}function a(Q){switch(V.extname(Q).toLowerCase()){case".png":return"image/png";case".webp":return"image/webp";case".jpg":case".jpeg":default:return"image/jpeg"}}function t(Q){if(Q.length<24)return null;if(Q.subarray(0,8).toString("hex").toUpperCase()!=="89504E470D0A1A0A")return null;if(Q.subarray(12,16).toString("ascii")!=="IHDR")return null;return{width:Q.readUInt32BE(16),height:Q.readUInt32BE(20)}}function e(Q){if(Q.length<4||Q[0]!==255||Q[1]!==216)return null;let X=2;while(X+9<Q.length){if(Q[X]!==255){X+=1;continue}const Z=Q[X+1];X+=2;if(Z===216||Z===217)continue;if(X+2>Q.length)break;const $=Q.readUInt16BE(X);if($<2||X+$>Q.length)break;if(Z>=192&&Z<=207&&![196,200,204].includes(Z)&&$>=7)return{height:Q.readUInt16BE(X+3),width:Q.readUInt16BE(X+5)};X+=$}return null}function QQ(Q){if(Q.length<30)return null;if(Q.subarray(0,4).toString("ascii")!=="RIFF"||Q.subarray(8,12).toString("ascii")!=="WEBP")return null;const X=Q.subarray(12,16).toString("ascii");if(X==="VP8X"&&Q.length>=30)return{width:1+Q.readUIntLE(24,3),height:1+Q.readUIntLE(27,3)};if(X==="VP8 "&&Q.length>=30)return{width:Q.readUInt16LE(26)&16383,height:Q.readUInt16LE(28)&16383};if(X==="VP8L"&&Q.length>=25){const Z=Q.readUInt32LE(21);return{width:(Z&16383)+1,height:(Z>>14&16383)+1}}return null}function XQ(Q,X){const Z=V.extname(Q).toLowerCase(),$=Z===".png"?t(X):Z===".webp"?QQ(X):e(X);if(!$)throw new K(`Failed to read image dimensions for ${Q}`);return $}export function readImageAsset(Q){const X=U.readFileSync(Q),{width:Z,height:$}=XQ(Q,X);return{filePath:Q,fileName:V.basename(Q),mimeType:a(Q),width:Z,height:$,byteLength:X.length,bytes:X}}export function isInstagramFeedAspectRatioAllowed(Q,X){const Z=Q/Math.max(X,1);return Z>=C-0.001&&Z<=M+0.001}export function getInstagramFeedNormalizedDimensions(Q,X){const Z=Q/Math.max(X,1);if(Z<C)return{width:Math.ceil(X*C),height:X};if(Z>M)return{width:Q,height:Math.ceil(Q/M)};return null}export function isInstagramStoryAspectRatioAllowed(Q,X){const Z=Q/Math.max(X,1);return Z>=D-0.001&&Z<=T+0.001}export function getInstagramStoryNormalizedDimensions(Q,X){const Z=Q/Math.max(X,1);if(Z<D)return{width:Math.ceil(X*D),height:X};if(Z>T)return{width:Q,height:Math.ceil(Q/T)};return null}function x(Q){const X=V.parse(Q);return V.join(R.tmpdir(),`opencli-instagram-private-${X.name}-${B.randomUUID()}${X.ext||".png"}`)}export function prepareImageAssetForPrivateUpload(Q){const X=readImageAsset(Q),Z=getInstagramFeedNormalizedDimensions(X.width,X.height);if(!Z)return X;if(process.platform!=="darwin")throw new K(`Instagram private publish does not support auto-normalizing ${X.fileName} on ${process.platform}`,`Use images within ${C.toFixed(2)}-${M.toFixed(2)} aspect ratio, or use the UI route`);const $=x(Q),q=z("sips",["--padToHeightWidth",String(Z.height),String(Z.width),"--padColor",S,Q,"--out",$],{encoding:"utf8"});if(q.error||q.status!==0||!U.existsSync($)){const F=[q.error?.message,q.stderr,q.stdout].map((O)=>String(O||"").trim()).filter(Boolean).join(" ");throw new K(`Instagram private publish failed to normalize ${X.fileName}`,F||"sips padToHeightWidth failed")}return{...readImageAsset($),cleanupPath:$}}export function prepareImageAssetForPrivateStoryUpload(Q){const X=readImageAsset(Q),Z=getInstagramStoryNormalizedDimensions(X.width,X.height);if(!Z)return X;if(process.platform!=="darwin")throw new K(`Instagram private story publish does not support auto-normalizing ${X.fileName} on ${process.platform}`,`Use images within ${D.toFixed(2)}-${T.toFixed(2)} aspect ratio, or use the UI route`);const $=x(Q),q=z("sips",["--padToHeightWidth",String(Z.height),String(Z.width),"--padColor",S,Q,"--out",$],{encoding:"utf8"});if(q.error||q.status!==0||!U.existsSync($)){const F=[q.error?.message,q.stderr,q.stdout].map((O)=>String(O||"").trim()).filter(Boolean).join(" ");throw new K(`Instagram private story publish failed to normalize ${X.fileName}`,F||"sips padToHeightWidth failed")}return{...readImageAsset($),cleanupPath:$}}function A(Q,X,Z){const $=V.join(R.tmpdir(),`opencli-instagram-${B.randomUUID()}.swift`);U.writeFileSync($,Q);try{const q=z("swift",[$,...X],{encoding:"utf8"});if(q.error||q.status!==0){const F=[q.error?.message,q.stderr,q.stdout].map((O)=>String(O||"").trim()).filter(Boolean).join(" ");throw new K(`Instagram private publish failed to ${Z}`,F||"swift helper failed")}return JSON.parse(String(q.stdout||"{}"))}catch(q){if(q instanceof K)throw q;throw new K(`Instagram private publish failed to ${Z}`,q instanceof Error?q.message:String(q))}finally{U.rmSync($,{force:!0})}}function ZQ(Q){if(process.platform!=="darwin")throw new K(`Instagram private mixed-media publish does not support reading video metadata on ${process.platform}`,"Use macOS for private mixed-media publishing, or rely on the UI fallback");const X=A(`
|
|
2
2
|
import AVFoundation
|
|
3
3
|
import Foundation
|
|
4
4
|
|
|
@@ -20,7 +20,7 @@ let payload: [String: Int] = [
|
|
|
20
20
|
]
|
|
21
21
|
let data = try JSONSerialization.data(withJSONObject: payload, options: [])
|
|
22
22
|
FileHandle.standardOutput.write(data)
|
|
23
|
-
`,[Q],"read video metadata");if(!X.width||!X.height||!X.durationMs)throw new K(`Instagram private publish failed to read video metadata for ${Q}`);return{width:X.width,height:X.height,durationMs:X.durationMs}}function $Q(Q){const X=
|
|
23
|
+
`,[Q],"read video metadata");if(!X.width||!X.height||!X.durationMs)throw new K(`Instagram private publish failed to read video metadata for ${Q}`);return{width:X.width,height:X.height,durationMs:X.durationMs}}function $Q(Q){const X=V.parse(Q);return V.join(R.tmpdir(),`opencli-instagram-private-video-cover-${X.name}-${B.randomUUID()}.jpg`)}function qQ(Q){const X=V.parse(Q);return V.join(R.tmpdir(),`opencli-instagram-story-video-${X.name}-${B.randomUUID()}${X.ext||".mp4"}`)}function FQ(Q){if(process.platform!=="darwin")throw new K(`Instagram private mixed-media publish does not support generating video covers on ${process.platform}`,"Use macOS for private mixed-media publishing, or rely on the UI fallback");const X=$Q(Q);A(`
|
|
24
24
|
import AVFoundation
|
|
25
25
|
import AppKit
|
|
26
26
|
import Foundation
|
|
@@ -40,7 +40,7 @@ try data.write(to: URL(fileURLWithPath: outputPath))
|
|
|
40
40
|
let payload = ["ok": true]
|
|
41
41
|
let json = try JSONSerialization.data(withJSONObject: payload, options: [])
|
|
42
42
|
FileHandle.standardOutput.write(json)
|
|
43
|
-
`,[Q,X],"generate video cover");return{...readImageAsset(X),cleanupPath:X}}export function readVideoAsset(Q){const X=
|
|
43
|
+
`,[Q,X],"generate video cover");return{...readImageAsset(X),cleanupPath:X}}export function readVideoAsset(Q){const X=U.readFileSync(Q),Z=ZQ(Q),$=FQ(Q);return{filePath:Q,fileName:V.basename(Q),mimeType:"video/mp4",width:Z.width,height:Z.height,durationMs:Z.durationMs,byteLength:X.length,bytes:X,coverImage:$,cleanupPaths:$.cleanupPath?[$.cleanupPath]:[]}}function KQ(Q,X){if(process.platform!=="darwin")throw new K(`Instagram private story publish does not support trimming long videos on ${process.platform}`,"Use macOS for private story video publishing, or trim the video to 15 seconds first");const Z=qQ(Q);A(`
|
|
44
44
|
import AVFoundation
|
|
45
45
|
import Foundation
|
|
46
46
|
|
|
@@ -72,4 +72,4 @@ if exportSession.status != .completed {
|
|
|
72
72
|
let payload = ["ok": true]
|
|
73
73
|
let json = try JSONSerialization.data(withJSONObject: payload, options: [])
|
|
74
74
|
FileHandle.standardOutput.write(json)
|
|
75
|
-
`,[Q,Z,String(X)],"trim story video");return Z}function NQ(Q){const X=readVideoAsset(Q);if(X.durationMs<=g)return X;const Z=KQ(Q,g),$=readVideoAsset(Z);return{...$,cleanupPaths:[...$.cleanupPaths||[],Z]}}function b(Q){const X=Q();return X>10000000000?Math.floor(X/1000):Math.floor(X)}export function buildConfigureToStoryPhotoPayload(Q){const X=Q.now??(()=>Date.now()),Z=b(X);return{source_type:"4",upload_id:Q.uploadId,story_media_creation_date:String(Z-17),client_shared_at:String(Z-5),client_timestamp:String(Z),configure_mode:1,edits:{crop_original_size:[Q.width,Q.height],crop_center:[0,0],crop_zoom:1.3333334},extra:{source_width:Q.width,source_height:Q.height},jazoest:Q.jazoest}}export function buildConfigureToStoryVideoPayload(Q){const X=Q.now??(()=>Date.now()),Z=b(X),$=Number((Q.durationMs/1000).toFixed(3));return{source_type:"4",upload_id:Q.uploadId,story_media_creation_date:String(Z-17),client_shared_at:String(Z-5),client_timestamp:String(Z),configure_mode:1,poster_frame_index:0,length:$,audio_muted:!1,filter_type:"0",video_result:"deprecated",extra:{source_width:Q.width,source_height:Q.height},jazoest:Q.jazoest}}function BQ(Q){const X=new URLSearchParams;for(const[Z,$]of Object.entries(Q)){if($===void 0||$===null)continue;if(typeof $==="object"){X.set(Z,JSON.stringify($));continue}X.set(Z,String($))}return X.toString()}function m(Q){const X=JSON.stringify(Q),Z=L.createHmac("sha256",o).update(X).digest("hex"),$=new URLSearchParams;$.set("ig_sig_key_version",n);$.set("signed_body",`${Z}.${X}`);return $.toString()}function U(Q){return Object.fromEntries(Object.entries({"X-ASBD-ID":Q.asbdId,"X-CSRFToken":Q.csrfToken,"X-IG-App-ID":Q.igAppId,"X-IG-WWW-Claim":Q.igWwwClaim,"X-Instagram-AJAX":Q.instagramAjax,"X-Web-Session-ID":Q.webSessionId}).filter(([,X])=>!!X))}function OQ(Q,X,Z){return{...U(Z),Accept:"*/*","Content-Type":Q.mimeType,Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Entity-Type":Q.mimeType,"X-Instagram-Rupload-Params":JSON.stringify({media_type:1,upload_id:X,upload_media_height:Q.height,upload_media_width:Q.width})}}function f(Q){const X=Math.min(Q.width,Q.height),Z=Number((Q.durationMs/1000).toFixed(3));return{crop_height:X,crop_width:X,crop_x1:Math.max(0,Math.floor((Q.width-X)/2)),crop_y1:Math.max(0,Math.floor((Q.height-X)/2)),mute:!1,trim_end:Z,trim_start:0}}function WQ(Q,X,Z){return{...U(Z),Accept:"*/*",Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Instagram-Rupload-Params":JSON.stringify({"client-passthrough":"1",is_unified_video:"0",is_sidecar:"1",media_type:2,for_album:!1,video_format:"",upload_id:X,upload_media_duration_ms:Q.durationMs,upload_media_height:Q.height,upload_media_width:Q.width,video_transform:null,video_edit_params:f(Q)})}}function GQ(Q,X,Z){return{...U(Z),Accept:"*/*",Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Instagram-Rupload-Params":JSON.stringify({"client-passthrough":"1",media_type:2,upload_id:X,upload_media_duration_ms:Q.durationMs,upload_media_height:Q.height,upload_media_width:Q.width,video_transform:null,video_edit_params:f(Q)})}}function HQ(Q,X,Z){return{...U(Z),Accept:"*/*","Content-Type":Q.coverImage.mimeType,Offset:"0","X-Entity-Length":String(Q.coverImage.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Entity-Type":Q.coverImage.mimeType,"X-Instagram-Rupload-Params":JSON.stringify({media_type:2,upload_id:X,upload_media_height:Q.height,upload_media_width:Q.width})}}async function _(Q,X){const Z=await Q.text();let $;try{$=Z?JSON.parse(Z):{}}catch{throw new K(`Instagram private publish ${X} returned invalid JSON`)}if(!Q.ok){const q=Z?` ${Z.slice(0,500)}`:"";throw new K(`Instagram private publish ${X} failed: ${Q.status}${q}`)}return $}async function j(Q,X,Z){let $;for(let q=0;q<w;q+=1)try{return await Q(X,Z)}catch(F){$=F;if(!x(F)||q>=w-1)throw F}throw $ instanceof Error?$:Error(String($))}async function JQ(Q){if(Q.type==="video")return{type:"video",asset:readVideoAsset(Q.filePath)};return{type:"image",asset:prepareImageAssetForPrivateUpload(Q.filePath)}}function c(Q){for(const X of Q){if(X.type==="image"){if(X.asset.cleanupPath)Y.rmSync(X.asset.cleanupPath,{force:!0});continue}for(const Z of X.asset.cleanupPaths||[])Y.rmSync(Z,{force:!0});if(X.asset.coverImage.cleanupPath)Y.rmSync(X.asset.coverImage.cleanupPath,{force:!0})}}async function y(Q,X,Z,$,q="feed"){if(X.type==="image"){const V=await j(Q,`https://i.instagram.com/rupload_igphoto/fb_uploader_${Z}`,{method:"POST",headers:OQ(X.asset,Z,$),body:X.asset.bytes}),G=await _(V,"upload");if(String(G?.status||"")!=="ok")throw new K(`Instagram private publish upload failed for ${X.asset.fileName}`);return}const F=await j(Q,`https://i.instagram.com/rupload_igvideo/fb_uploader_${Z}`,{method:"POST",headers:q==="story"?GQ(X.asset,Z,$):WQ(X.asset,Z,$),body:X.asset.bytes}),N=await _(F,"video upload");if(String(N?.status||"")!=="ok")throw new K(`Instagram private publish video upload failed for ${X.asset.fileName}`);const W=await j(Q,`https://i.instagram.com/rupload_igphoto/fb_uploader_${Z}`,{method:"POST",headers:HQ(X.asset,Z,$),body:X.asset.coverImage.bytes}),O=await _(W,"video cover upload");if(String(O?.status||"")!=="ok")throw new K(`Instagram private publish video cover upload failed for ${X.asset.fileName}`)}async function VQ(Q){const X=Q.waitMs??i,Z={method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/json"},body:JSON.stringify(Q.payload)};for(let $=0;$<I;$+=1){const q=await Q.fetcher("https://www.instagram.com/api/v1/media/configure_sidecar/",Z),F=await q.text();let N={};try{N=F?JSON.parse(F):{}}catch{throw new K("Instagram private publish configure_sidecar returned invalid JSON")}if(!q.ok){const O=F?` ${F.slice(0,500)}`:"";throw new K(`Instagram private publish configure_sidecar failed: ${q.status}${O}`)}const W=String(N?.message||"");if(q.status===202||/transcode not finished yet/i.test(W)){if($>=I-1)throw new K("Instagram private publish configure_sidecar timed out waiting for video transcode",F.slice(0,500));await X(d);continue}if(String(N?.status||"").toLowerCase()==="fail")throw new K("Instagram private publish configure_sidecar failed",W||F.slice(0,500));return{code:N?.media?.code}}throw new K("Instagram private publish configure_sidecar failed")}export async function publishMediaViaPrivateApi(Q){const X=Q.now??(()=>Date.now()),Z=String(X()),$=Q.mediaItems.length>1?Q.mediaItems.map((W,O)=>String(X()+O+1)):[String(X())],q=Q.fetcher??((W,O)=>S(Q.page,W,O)),F=Q.prepareMediaAsset??JQ,N=await Promise.all(Q.mediaItems.map((W)=>F(W)));try{for(let O=0;O<N.length;O+=1){const V=N[O],G=$[O];await y(q,V,G,Q.apiContext)}if($.length===1){if(N[0]?.type!=="image")throw new K("Instagram private publish only supports single-video uploads through instagram reel");const O=await q("https://www.instagram.com/api/v1/media/configure/",{method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:buildConfigureBody({uploadId:$[0],caption:Q.caption,jazoest:Q.jazoest})});return{code:(await _(O,"configure"))?.media?.code,uploadIds:$}}return{code:(await VQ({fetcher:q,payload:buildConfigureSidecarPayload({uploadIds:$,caption:Q.caption,clientSidecarId:Z,jazoest:Q.jazoest}),apiContext:Q.apiContext,waitMs:Q.waitMs})).code,uploadIds:$}}finally{c(N)}}export async function publishImagesViaPrivateApi(Q){return publishMediaViaPrivateApi({page:Q.page,mediaItems:Q.imagePaths.map((X)=>({type:"image",filePath:X})),caption:Q.caption,apiContext:Q.apiContext,jazoest:Q.jazoest,now:Q.now,fetcher:Q.fetcher,waitMs:Q.waitMs,prepareMediaAsset:Q.prepareAsset?async(X)=>({type:"image",asset:await Q.prepareAsset(X.filePath)}):void 0})}export async function publishStoryViaPrivateApi(Q){const X=Q.now??(()=>Date.now()),Z=String(X()),$=Q.fetcher??((G,J)=>S(Q.page,G,J)),F=await(Q.prepareMediaAsset??(async(G)=>G.type==="video"?{type:"video",asset:NQ(G.filePath)}:{type:"image",asset:prepareImageAssetForPrivateStoryUpload(G.filePath)}))(Q.mediaItem),N=Q.currentUserId||("getCookies"in Q.page?String((await(Q.page.getCookies?.({domain:"instagram.com"})??Promise.resolve([]))).find((G)=>G.name==="ds_user_id")?.value||""):"");if(!N)throw new K("Instagram story publish could not derive current user id from browser session");const W={_csrftoken:Q.apiContext.csrfToken,_uid:N,_uuid:L.randomUUID(),device:r},O=(G,J)=>m({...buildConfigureToStoryPhotoPayload({uploadId:Z,width:G,height:J,now:X,jazoest:Q.jazoest}),...W}),V=(G,J,B)=>m({...buildConfigureToStoryVideoPayload({uploadId:Z,width:G,height:J,durationMs:B,now:X,jazoest:Q.jazoest}),...W});try{await y($,F,Z,Q.apiContext,"story");if(F.type==="image"){const B=await $("https://i.instagram.com/api/v1/media/configure_to_story/",{method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:O(F.asset.width,F.asset.height)}),R=await _(B,"configure_to_story");return{mediaPk:String(R?.media?.pk||R?.media?.id||"").split("_")[0]||void 0,uploadId:Z}}await _(await $("https://i.instagram.com/api/v1/media/configure_to_story/",{method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:O(F.asset.width,F.asset.height)}),"configure_to_story cover");const G=await $("https://i.instagram.com/api/v1/media/configure_to_story/?video=1",{method:"POST",headers:{...U(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:V(F.asset.width,F.asset.height,F.asset.durationMs)}),J=await _(G,"configure_to_story");return{mediaPk:String(J?.media?.pk||J?.media?.id||"").split("_")[0]||void 0,uploadId:Z}}finally{c([F])}}
|
|
75
|
+
`,[Q,Z,String(X)],"trim story video");return Z}function NQ(Q){const X=readVideoAsset(Q);if(X.durationMs<=I)return X;const Z=KQ(Q,I),$=readVideoAsset(Z);return{...$,cleanupPaths:[...$.cleanupPaths||[],Z]}}function P(Q){const X=Q();return X>10000000000?Math.floor(X/1000):Math.floor(X)}export function buildConfigureToStoryPhotoPayload(Q){const X=Q.now??(()=>Date.now()),Z=P(X);return{source_type:"4",upload_id:Q.uploadId,story_media_creation_date:String(Z-17),client_shared_at:String(Z-5),client_timestamp:String(Z),configure_mode:1,edits:{crop_original_size:[Q.width,Q.height],crop_center:[0,0],crop_zoom:1.3333334},extra:{source_width:Q.width,source_height:Q.height},jazoest:Q.jazoest}}export function buildConfigureToStoryVideoPayload(Q){const X=Q.now??(()=>Date.now()),Z=P(X),$=Number((Q.durationMs/1000).toFixed(3));return{source_type:"4",upload_id:Q.uploadId,story_media_creation_date:String(Z-17),client_shared_at:String(Z-5),client_timestamp:String(Z),configure_mode:1,poster_frame_index:0,length:$,audio_muted:!1,filter_type:"0",video_result:"deprecated",extra:{source_width:Q.width,source_height:Q.height},jazoest:Q.jazoest}}function BQ(Q){const X=new URLSearchParams;for(const[Z,$]of Object.entries(Q)){if($===void 0||$===null)continue;if(typeof $==="object"){X.set(Z,JSON.stringify($));continue}X.set(Z,String($))}return X.toString()}function b(Q){const X=JSON.stringify(Q),Z=B.createHmac("sha256",n).update(X).digest("hex"),$=new URLSearchParams;$.set("ig_sig_key_version",o);$.set("signed_body",`${Z}.${X}`);return $.toString()}function _(Q){return Object.fromEntries(Object.entries({"X-ASBD-ID":Q.asbdId,"X-CSRFToken":Q.csrfToken,"X-IG-App-ID":Q.igAppId,"X-IG-WWW-Claim":Q.igWwwClaim,"X-Instagram-AJAX":Q.instagramAjax,"X-Web-Session-ID":Q.webSessionId}).filter(([,X])=>!!X))}function OQ(Q,X,Z){return{..._(Z),Accept:"*/*","Content-Type":Q.mimeType,Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Entity-Type":Q.mimeType,"X-Instagram-Rupload-Params":JSON.stringify({media_type:1,upload_id:X,upload_media_height:Q.height,upload_media_width:Q.width})}}function m(Q){const X=Math.min(Q.width,Q.height),Z=Number((Q.durationMs/1000).toFixed(3));return{crop_height:X,crop_width:X,crop_x1:Math.max(0,Math.floor((Q.width-X)/2)),crop_y1:Math.max(0,Math.floor((Q.height-X)/2)),mute:!1,trim_end:Z,trim_start:0}}function WQ(Q,X,Z){return{..._(Z),Accept:"*/*",Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Instagram-Rupload-Params":JSON.stringify({"client-passthrough":"1",is_unified_video:"0",is_sidecar:"1",media_type:2,for_album:!1,video_format:"",upload_id:X,upload_media_duration_ms:Q.durationMs,upload_media_height:Q.height,upload_media_width:Q.width,video_transform:null,video_edit_params:m(Q)})}}function GQ(Q,X,Z){return{..._(Z),Accept:"*/*",Offset:"0","X-Entity-Length":String(Q.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Instagram-Rupload-Params":JSON.stringify({"client-passthrough":"1",media_type:2,upload_id:X,upload_media_duration_ms:Q.durationMs,upload_media_height:Q.height,upload_media_width:Q.width,video_transform:null,video_edit_params:m(Q)})}}function HQ(Q,X,Z){return{..._(Z),Accept:"*/*","Content-Type":Q.coverImage.mimeType,Offset:"0","X-Entity-Length":String(Q.coverImage.byteLength),"X-Entity-Name":`fb_uploader_${X}`,"X-Entity-Type":Q.coverImage.mimeType,"X-Instagram-Rupload-Params":JSON.stringify({media_type:2,upload_id:X,upload_media_height:Q.height,upload_media_width:Q.width})}}async function L(Q,X){const Z=await Q.text();let $;try{$=Z?JSON.parse(Z):{}}catch{throw new K(`Instagram private publish ${X} returned invalid JSON`)}if(!Q.ok){const q=Z?` ${Z.slice(0,500)}`:"";throw new K(`Instagram private publish ${X} failed: ${Q.status}${q}`)}return $}async function E(Q,X,Z){let $;for(let q=0;q<v;q+=1)try{return await Q(X,Z)}catch(F){$=F;if(!g(F)||q>=v-1)throw F}throw $ instanceof Error?$:Error(String($))}async function JQ(Q){if(Q.type==="video")return{type:"video",asset:readVideoAsset(Q.filePath)};return{type:"image",asset:prepareImageAssetForPrivateUpload(Q.filePath)}}function f(Q){for(const X of Q){if(X.type==="image"){if(X.asset.cleanupPath)U.rmSync(X.asset.cleanupPath,{force:!0});continue}for(const Z of X.asset.cleanupPaths||[])U.rmSync(Z,{force:!0});if(X.asset.coverImage.cleanupPath)U.rmSync(X.asset.coverImage.cleanupPath,{force:!0})}}async function c(Q,X,Z,$,q="feed"){if(X.type==="image"){const J=await E(Q,`https://i.instagram.com/rupload_igphoto/fb_uploader_${Z}`,{method:"POST",headers:OQ(X.asset,Z,$),body:X.asset.bytes}),W=await L(J,"upload");if(String(W?.status||"")!=="ok")throw new K(`Instagram private publish upload failed for ${X.asset.fileName}`);return}const F=await E(Q,`https://i.instagram.com/rupload_igvideo/fb_uploader_${Z}`,{method:"POST",headers:q==="story"?GQ(X.asset,Z,$):WQ(X.asset,Z,$),body:X.asset.bytes}),O=await L(F,"video upload");if(String(O?.status||"")!=="ok")throw new K(`Instagram private publish video upload failed for ${X.asset.fileName}`);const G=await E(Q,`https://i.instagram.com/rupload_igphoto/fb_uploader_${Z}`,{method:"POST",headers:HQ(X.asset,Z,$),body:X.asset.coverImage.bytes}),N=await L(G,"video cover upload");if(String(N?.status||"")!=="ok")throw new K(`Instagram private publish video cover upload failed for ${X.asset.fileName}`)}async function VQ(Q){const X=Q.waitMs??r,Z={method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/json"},body:JSON.stringify(Q.payload)};for(let $=0;$<w;$+=1){let q,F;try{q=await Q.fetcher("https://www.instagram.com/api/v1/media/configure_sidecar/",Z);F=await q.text()}catch(N){throw markPublishOutcomeUncertain(N instanceof Error?N:new K(`Instagram private publish configure_sidecar request failed: ${String(N)}`))}let O={};try{O=F?JSON.parse(F):{}}catch{throw markPublishOutcomeUncertain(new K("Instagram private publish configure_sidecar returned invalid JSON"))}if(!q.ok){const N=F?` ${F.slice(0,500)}`:"";throw markPublishOutcomeUncertain(new K(`Instagram private publish configure_sidecar failed: ${q.status}${N}`))}const G=String(O?.message||"");if(q.status===202||/transcode not finished yet/i.test(G)){if($>=w-1)throw markPublishOutcomeUncertain(new K("Instagram private publish configure_sidecar timed out waiting for video transcode",F.slice(0,500)));await X(d);continue}if(String(O?.status||"").toLowerCase()==="fail")throw new K("Instagram private publish configure_sidecar failed",G||F.slice(0,500));return{code:O?.media?.code}}throw new K("Instagram private publish configure_sidecar failed")}export async function publishMediaViaPrivateApi(Q){const X=Q.now??(()=>Date.now()),Z=String(X()),$=Q.mediaItems.length>1?Q.mediaItems.map((G,N)=>String(X()+N+1)):[String(X())],q=Q.fetcher??((G,N)=>j(Q.page,G,N)),F=Q.prepareMediaAsset??JQ,O=await Promise.all(Q.mediaItems.map((G)=>F(G)));try{for(let N=0;N<O.length;N+=1){const J=O[N],W=$[N];await c(q,J,W,Q.apiContext)}if($.length===1){if(O[0]?.type!=="image")throw new K("Instagram private publish only supports single-video uploads through instagram reel");let N;try{const J=await q("https://www.instagram.com/api/v1/media/configure/",{method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:buildConfigureBody({uploadId:$[0],caption:Q.caption,jazoest:Q.jazoest})});N=await L(J,"configure")}catch(J){throw markPublishOutcomeUncertain(J instanceof Error?J:new K(`Instagram private publish configure request failed: ${String(J)}`))}return{code:N?.media?.code,uploadIds:$}}return{code:(await VQ({fetcher:q,payload:buildConfigureSidecarPayload({uploadIds:$,caption:Q.caption,clientSidecarId:Z,jazoest:Q.jazoest}),apiContext:Q.apiContext,waitMs:Q.waitMs})).code,uploadIds:$}}finally{f(O)}}export async function publishImagesViaPrivateApi(Q){return publishMediaViaPrivateApi({page:Q.page,mediaItems:Q.imagePaths.map((X)=>({type:"image",filePath:X})),caption:Q.caption,apiContext:Q.apiContext,jazoest:Q.jazoest,now:Q.now,fetcher:Q.fetcher,waitMs:Q.waitMs,prepareMediaAsset:Q.prepareAsset?async(X)=>({type:"image",asset:await Q.prepareAsset(X.filePath)}):void 0})}export async function publishStoryViaPrivateApi(Q){const X=Q.now??(()=>Date.now()),Z=String(X()),$=Q.fetcher??((W,H)=>j(Q.page,W,H)),F=await(Q.prepareMediaAsset??(async(W)=>W.type==="video"?{type:"video",asset:NQ(W.filePath)}:{type:"image",asset:prepareImageAssetForPrivateStoryUpload(W.filePath)}))(Q.mediaItem),O=Q.currentUserId||("getCookies"in Q.page?String((await(Q.page.getCookies?.({domain:"instagram.com"})??Promise.resolve([]))).find((W)=>W.name==="ds_user_id")?.value||""):"");if(!O)throw new K("Instagram story publish could not derive current user id from browser session");const G={_csrftoken:Q.apiContext.csrfToken,_uid:O,_uuid:B.randomUUID(),device:p},N=(W,H)=>b({...buildConfigureToStoryPhotoPayload({uploadId:Z,width:W,height:H,now:X,jazoest:Q.jazoest}),...G}),J=(W,H,Y)=>b({...buildConfigureToStoryVideoPayload({uploadId:Z,width:W,height:H,durationMs:Y,now:X,jazoest:Q.jazoest}),...G});try{await c($,F,Z,Q.apiContext,"story");if(F.type==="image"){let H;try{const Y=await $("https://i.instagram.com/api/v1/media/configure_to_story/",{method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:N(F.asset.width,F.asset.height)});H=await L(Y,"configure_to_story")}catch(Y){throw markPublishOutcomeUncertain(Y instanceof Error?Y:new K(`Instagram private publish configure_to_story request failed: ${String(Y)}`))}return{mediaPk:String(H?.media?.pk||H?.media?.id||"").split("_")[0]||void 0,uploadId:Z}}let W;try{await L(await $("https://i.instagram.com/api/v1/media/configure_to_story/",{method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:N(F.asset.width,F.asset.height)}),"configure_to_story cover");const H=await $("https://i.instagram.com/api/v1/media/configure_to_story/?video=1",{method:"POST",headers:{..._(Q.apiContext),"Content-Type":"application/x-www-form-urlencoded"},body:J(F.asset.width,F.asset.height,F.asset.durationMs)});W=await L(H,"configure_to_story")}catch(H){throw markPublishOutcomeUncertain(H instanceof Error?H:new K(`Instagram private publish configure_to_story request failed: ${String(H)}`))}return{mediaPk:String(W?.media?.pk||W?.media?.id||"").split("_")[0]||void 0,uploadId:Z}}finally{f([F])}}
|
package/clis/instagram/post.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import*as
|
|
1
|
+
import*as b from"node:fs";import*as W from"node:path";import{cli as r,Strategy as p}from"@jackwener/opencli/registry";import{ArgumentError as z,AuthRequiredError as R,CommandExecutionError as Y}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 Nj}from"./_shared/private-publish.js";import{resolveInstagramRuntimeInfo as Oj}from"./_shared/runtime-info.js";import{throwIfFileAccessDenied as Qj}from"../_shared/video-publish.js";const K="https://www.instagram.com/",Xj=new Set([".jpg",".jpeg",".png",".webp"]),Yj=new Set([".mp4"]),C=10,Zj="/tmp/instagram_post_protocol_trace.json";async function k(j,G=!1){if(G){await j.goto(`${K}?__opencli_reset=${Date.now()}`);await j.wait({time:1})}await j.goto(K)}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 w from"node:fs";import*as W from"node:path";import{cli as s,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 _j(j){if(!j)throw new Y("Browser session required for instagram post");return j}function $j(j){if(!j.length)throw new z('Argument "media" is required.',"Provide --media /path/to/file.jpg or --media /path/a.jpg,/path/b.mp4");if(j.length>C)throw new z(`Too many media items: ${j.length}`,`Instagram carousel posts support at most ${C} items`);return j.map((N)=>{const O=W.resolve(String(N||"").trim());if(!O)throw new z("Media path cannot be empty");if(!b.existsSync(O))throw new z(`Media file not found: ${O}`);const Q=W.extname(O).toLowerCase();if(Xj.has(Q))return{type:"image",filePath:O};if(Yj.has(Q))return{type:"video",filePath:O};throw new z(`Unsupported media format: ${Q}`,"Supported formats: images (.jpg, .jpeg, .png, .webp) and videos (.mp4)")})}function qj(j){const G=String(j.media??"").trim();return $j(G.split(",").map((N)=>N.trim()).filter(Boolean))}function Hj(j){if(j.media===void 0)throw new z('Argument "media" is required.',"Provide --media /path/to/file.jpg or --media /path/a.jpg,/path/b.mp4")}function Jj(j){if(!(j instanceof Y))return!1;return j.message.startsWith("Instagram private publish")||j.message.startsWith("Instagram private route")}function x(j,G){return[{status:"✅ Posted",detail:Mj(j),url:G}]}function Tj(j,G){const N=j instanceof Error?j.message:String(j),O=G instanceof Error?G.message:String(G);return`Private route failed first: ${N}. UI fallback then failed: ${O}`}async function Vj(j){const G=await Gj(j.page),N=j.mediaItems.every((Q)=>Q.type==="image")?await jj({page:j.page,imagePaths:j.mediaItems.map((Q)=>Q.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}),O=N.code?new URL(`/p/${N.code}/`,K).toString():j.canDiffProfile?await w(j.page,j.existingPostPaths):"";return x(j.mediaItems,O)}async function zj(j){let G,N=null;for(let O=0;O<j.commandAttemptBudget;O++){let Q=!1;try{await k(j.page,j.forceFreshStart||O>0);await j.installProtocolCapture();await j.page.wait({time:2});await A(j.page);await y(j.page);const X=await v(j.page,j.mediaItems);if(j.preUploadDelaySeconds>0)await j.page.wait({time:j.preUploadDelaySeconds});let _=!1,Z=null;for(const q of X){let T=q;for(let L=0;L<j.uploadAttemptBudget;L++){await Aj(j.page,j.mediaItems,T);const B=await c(j.page,j.previewProbeWindowSeconds);if(B.state==="preview"){_=!0;break}if(B.state==="failed"){Z=D(B.detail);for(let F=0;F<j.inlineUploadRetryBudget;F++){if(!await Cj(j.page))break;await j.page.wait({time:3});const V=await c(j.page,Math.max(3,Math.floor(j.previewProbeWindowSeconds/2)));if(V.state==="preview"){_=!0;break}if(V.state!=="failed")break}if(_)break;await bj(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);T=await I(j.page,T,j.mediaItems);if(j.preUploadDelaySeconds>0)await j.page.wait({time:j.preUploadDelaySeconds})}catch{throw Z}await j.page.wait({time:1.5});continue}break}break}if(_)break}if(!_){if(Z)throw Z;await kj(j.page,j.finalPreviewWaitSeconds)}try{await hj(j.page)}catch(q){await Ej(j.page,q)}if(j.content){await Sj(j.page,j.content);await vj(j.page,j.content)}if(j.preShareDelaySeconds>0)await j.page.wait({time:j.preShareDelaySeconds});await d(j.page,["Share","分享"],"caption");Q=!0;let $="";try{$=await g(j.page)}catch(q){if(q instanceof Y&&q.message==="Instagram post share failed"&&await Uj(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 w(j.page,j.existingPostPaths);return x(j.mediaItems,$)}catch(X){G=X;if(X instanceof Y&&X.message!=="Failed to open Instagram post composer")N=X;if(X instanceof R)throw X;if(Q){let Z="";if(j.canDiffProfile)try{Z=await w(j.page,j.existingPostPaths)}catch{}if(Z)return x(j.mediaItems,Z);const $=X instanceof Error?X.message:String(X);throw new Y(`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(!(X instanceof Y)||O===j.commandAttemptBudget-1){if(X instanceof Y&&X.message==="Failed to open Instagram post composer"&&N)throw N;throw X}let _=!1;if(j.mediaItems.length>=10&&j.page.closeWindow)try{await j.drainProtocolCapture();await j.page.closeWindow();_=!0}catch{}if(!_){await A(j.page);await j.page.wait({time:1})}}}throw G instanceof Error?G:new Y("Instagram post failed")}async function y(j){const G=await j.evaluate(buildEnsureComposerOpenJs());if(!G?.ok){if(G?.reason==="auth")throw new R("www.instagram.com","Instagram login required before posting");throw new Y("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 w from"node:fs";import*as W from"node:path";import{cli as s,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 U(j,G){const N=G.some((Q)=>Q.type==="video"),O=await j.evaluate(`
|
|
109
109
|
((includesVideo) => {
|
|
110
110
|
const isVisible = (el) => {
|
|
111
111
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -165,12 +165,12 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
165
165
|
});
|
|
166
166
|
return { ok: true, selectors };
|
|
167
167
|
})(${JSON.stringify(N)})
|
|
168
|
-
`);if(!O?.ok||!O.selectors?.length)throw new Y("Instagram upload input not found","Open the new-post composer in a logged-in browser session and retry");return O.selectors}async function
|
|
168
|
+
`);if(!O?.ok||!O.selectors?.length)throw new Y("Instagram upload input not found","Open the new-post composer in a logged-in browser session and retry");return O.selectors}async function v(j,G){try{return await U(j,G)}catch(N){if(!(N instanceof Y)||!N.message.includes("upload input not found"))throw N;await y(j);await j.wait({time:1.5});try{return await U(j,G)}catch(O){if(!(O instanceof Y)||!O.message.includes("upload input not found"))throw O;await k(j,!0);await j.wait({time:2});await A(j);await y(j);await j.wait({time:2});return U(j,G)}}}function Kj(j){const G=j.match(/data-opencli-ig-upload-index="(\d+)"/);if(!G)return null;const N=Number.parseInt(G[1]||"",10);return Number.isNaN(N)?null:N}async function I(j,G,N){const O=await v(j,N),Q=Kj(G);if(Q!==null&&O[Q])return O[Q];return O[0]||G}async function Lj(j,G,N){const O=G.map(($)=>{const q=W.extname($).toLowerCase(),T=q===".png"?"image/png":q===".webp"?"image/webp":"image/jpeg";return{name:W.basename($),type:T,base64:b.readFileSync($).toString("base64")}}),Q=`__opencliInstagramUpload_${Date.now()}_${Math.random().toString(36).slice(2)}`,X=262144;await j.evaluate(`
|
|
169
169
|
(() => {
|
|
170
170
|
window[${JSON.stringify(Q)}] = [];
|
|
171
171
|
return { ok: true };
|
|
172
172
|
})()
|
|
173
|
-
`);const
|
|
173
|
+
`);const _=JSON.stringify(O);for(let $=0;$<_.length;$+=X){const q=_.slice($,$+X);await j.evaluate(`
|
|
174
174
|
(() => {
|
|
175
175
|
const key = ${JSON.stringify(Q)};
|
|
176
176
|
const chunk = ${JSON.stringify(q)};
|
|
@@ -179,7 +179,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
179
179
|
window[key] = parts;
|
|
180
180
|
return { ok: true, count: parts.length };
|
|
181
181
|
})()
|
|
182
|
-
`)}const
|
|
182
|
+
`)}const Z=await j.evaluate(`
|
|
183
183
|
(() => {
|
|
184
184
|
const selector = ${JSON.stringify(N)};
|
|
185
185
|
const key = ${JSON.stringify(Q)};
|
|
@@ -212,7 +212,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
212
212
|
return { ok: false, error: String(error) };
|
|
213
213
|
}
|
|
214
214
|
})()
|
|
215
|
-
`);if(!
|
|
215
|
+
`);if(!Z?.ok)throw new Y(Z?.error||"Instagram fallback file injection failed")}async function Bj(j,G){await j.evaluate(`
|
|
216
216
|
(() => {
|
|
217
217
|
const input = document.querySelector(${JSON.stringify(G)});
|
|
218
218
|
if (!(input instanceof HTMLInputElement)) return { ok: false };
|
|
@@ -255,7 +255,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
255
255
|
if (failed) return { state: 'failed', detail: dialogText || 'Something went wrong' };
|
|
256
256
|
return { state: 'pending', detail: dialogText || '' };
|
|
257
257
|
})()
|
|
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
|
|
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 Y("Instagram image upload failed",j?`Instagram rejected the upload: ${j}`:"Instagram rejected the upload before the preview stage")}async function Aj(j,G,N){const O=G.map((X)=>X.filePath);if(!j.setFileInput)throw new Y("Instagram posting requires Browser Bridge file upload support","Use Browser Bridge or another browser mode that supports setFileInput");let Q=N;for(let X=0;X<2;X++)try{await j.setFileInput(O,Q);await Bj(j,Q);return}catch(_){const Z=_ instanceof Error?_.message:String(_);if((Z.includes("No element found matching selector")||Z.includes("Could not find node with given id")||Z.includes("No node with given id found"))&&X===0){Q=await I(j,Q,G);continue}if(!Z.includes("Unknown action")&&!Z.includes("set-file-input")&&!Z.includes("not supported")&&!Z.includes("Not allowed"))throw _;if(G.some((q)=>q.type==="video")){Qj(_);throw new Y("Instagram mixed-media posting requires Browser Bridge file upload support","Use Browser Bridge or another browser mode that supports setFileInput for image/video uploads")}await Lj(j,O,Q);return}}function Mj(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 Dj(j){if(j.length>=10)return 6;if(j.length>=5)return 4;return 3}function Fj(j){if(j.length>=10)return 3;if(j.length>=5)return 1.5;return 0}function Wj(j){if(j.length>=10)return 3;if(j.length>=5)return 3;return 2}function Rj(j){if(j.length>=10)return 6;if(j.length>=5)return 6;return 4}function xj(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 wj(j){if(j.length>=10)return 3;if(j.length>=5)return 2;return 1}async function bj(j){return!!(await j.evaluate(`
|
|
259
259
|
(() => {
|
|
260
260
|
const isVisible = (el) => {
|
|
261
261
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -278,7 +278,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
278
278
|
}
|
|
279
279
|
return { ok: false };
|
|
280
280
|
})()
|
|
281
|
-
`))?.ok}async function
|
|
281
|
+
`))?.ok}async function Cj(j){return!!(await j.evaluate(`
|
|
282
282
|
(() => {
|
|
283
283
|
const isVisible = (el) => {
|
|
284
284
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -312,7 +312,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
312
312
|
}
|
|
313
313
|
return { ok: false };
|
|
314
314
|
})()
|
|
315
|
-
`))?.ok}async function
|
|
315
|
+
`))?.ok}async function kj(j,G=12){const N=Math.max(1,Math.ceil(G));for(let O=0;O<N;O++){const Q=await M(j);if(Q.state==="preview")return;if(Q.state==="failed"){await j.screenshot({path:"/tmp/instagram_post_preview_debug.png"});throw D("Inspect /tmp/instagram_post_preview_debug.png. "+(Q.detail||""))}if(O<N-1)await j.wait({time:1})}await j.screenshot({path:"/tmp/instagram_post_preview_debug.png"});throw new Y("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 N=Math.max(1,Math.ceil(G*2));for(let O=0;O<N;O++){const Q=await M(j);if(Q.state!=="pending")return Q;if(O<N-1)await j.wait({time:0.5})}return{state:"pending"}}export function buildClickActionJs(j,G="any"){return`
|
|
316
316
|
((labels, scope) => {
|
|
317
317
|
const isVisible = (el) => {
|
|
318
318
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -367,7 +367,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
367
367
|
}
|
|
368
368
|
return { ok: false };
|
|
369
369
|
})(${JSON.stringify(j)}, ${JSON.stringify(G)})
|
|
370
|
-
`}async function
|
|
370
|
+
`}async function d(j,G,N="any"){const O=await j.evaluate(buildClickActionJs(G,N));if(!O?.ok)throw new Y(`Instagram action button not found: ${G.join(" / ")}`);return O.label||G[0]}async function Uj(j){return!!(await j.evaluate(`
|
|
371
371
|
(() => {
|
|
372
372
|
const isVisible = (el) => {
|
|
373
373
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -405,12 +405,12 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
405
405
|
|
|
406
406
|
return { ok: false };
|
|
407
407
|
})()
|
|
408
|
-
`))?.ok}async function
|
|
408
|
+
`))?.ok}async function l(j){return!!(await j.evaluate(`
|
|
409
409
|
(() => {
|
|
410
410
|
const editable = document.querySelector('textarea, [contenteditable="true"]');
|
|
411
411
|
return { ok: !!editable };
|
|
412
412
|
})()
|
|
413
|
-
`))?.ok}async function
|
|
413
|
+
`))?.ok}async function m(j){return!!(await j.evaluate(`
|
|
414
414
|
(() => {
|
|
415
415
|
const editable = document.querySelector('textarea, [contenteditable="true"]');
|
|
416
416
|
const dialogText = Array.from(document.querySelectorAll('[role="dialog"]'))
|
|
@@ -424,7 +424,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
424
424
|
|| dialogText.includes('advanced settings'),
|
|
425
425
|
};
|
|
426
426
|
})()
|
|
427
|
-
`))?.ok}async function
|
|
427
|
+
`))?.ok}async function hj(j){for(let G=0;G<3;G++){if(await m(j))return;try{await d(j,["Next","下一步"],"media")}catch(O){if(O instanceof Y){await j.wait({time:1.5});if(await m(j))return;const Q=await M(j);if(Q.state==="failed")throw D(Q.detail);if(G<2)continue}throw O}await j.wait({time:1.5});if(await l(j))return;const N=await M(j);if(N.state==="failed")throw D(N.detail)}await j.screenshot({path:"/tmp/instagram_post_caption_debug.png"});throw new Y("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 l(j)){await j.screenshot({path:"/tmp/instagram_post_caption_debug.png"});throw new Y("Instagram caption editor did not appear","Instagram may have changed the publish flow; inspect /tmp/instagram_post_caption_debug.png")}}async function Ej(j,G){const N=await M(j);if(N.state==="failed")throw D(N.detail);throw G}async function fj(j){return!!(await j.evaluate(`
|
|
428
428
|
(() => {
|
|
429
429
|
const textarea = document.querySelector('[aria-label="Write a caption..."], textarea');
|
|
430
430
|
if (textarea instanceof HTMLTextAreaElement) {
|
|
@@ -484,7 +484,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
484
484
|
|
|
485
485
|
return { ok: true, kind: 'contenteditable' };
|
|
486
486
|
})()
|
|
487
|
-
`))?.ok}async function
|
|
487
|
+
`))?.ok}async function Sj(j,G){if(j.insertText&&await fj(j))try{await j.insertText(G);await j.wait({time:0.3});await j.evaluate(`
|
|
488
488
|
(() => {
|
|
489
489
|
const textarea = document.querySelector('[aria-label="Write a caption..."], textarea');
|
|
490
490
|
if (textarea instanceof HTMLTextAreaElement) {
|
|
@@ -596,7 +596,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
596
596
|
|
|
597
597
|
return { ok: false };
|
|
598
598
|
})(${JSON.stringify(G)})
|
|
599
|
-
`))?.ok)throw new Y("Failed to fill Instagram caption")}async function
|
|
599
|
+
`))?.ok)throw new Y("Failed to fill Instagram caption")}async function Pj(j,G){return!!(await j.evaluate(`
|
|
600
600
|
((content) => {
|
|
601
601
|
const normalized = content.trim();
|
|
602
602
|
const readLexicalText = (node) => {
|
|
@@ -650,7 +650,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
650
650
|
|
|
651
651
|
return { ok: false };
|
|
652
652
|
})(${JSON.stringify(G)})
|
|
653
|
-
`))?.ok}async function
|
|
653
|
+
`))?.ok}async function vj(j,G){for(let N=0;N<6;N++){if(await Pj(j,G))return;if(N<5)await j.wait({time:0.5})}await j.screenshot({path:"/tmp/instagram_post_caption_fill_debug.png"});throw new Y("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 N=0;N<90;N++){const O=await j.evaluate(buildPublishStatusProbeJs());if(O?.failed){await j.screenshot({path:"/tmp/instagram_post_share_debug.png"});throw new Y("Instagram post share failed","Inspect /tmp/instagram_post_share_debug.png for the share failure state")}if(O?.ok)return O.url||"";if(O?.settled){G+=1;if(G>=3)return""}else G=0;if(N<89)await j.wait({time:1})}await j.screenshot({path:"/tmp/instagram_post_share_debug.png"});throw new Y("Instagram post share confirmation did not appear","Inspect /tmp/instagram_post_share_debug.png for the final publish state")}async function o(j){return(await j.getCookies({domain:"instagram.com"})).find((N)=>N.name==="ds_user_id")?.value||""}async function s(j,G=""){if(G){const O=await Oj(j),Q=await j.evaluate(`
|
|
654
654
|
(async () => {
|
|
655
655
|
const userId = ${JSON.stringify(G)};
|
|
656
656
|
const appId = ${JSON.stringify(O.appId||"")};
|
|
@@ -670,7 +670,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
670
670
|
return { ok: false };
|
|
671
671
|
}
|
|
672
672
|
})()
|
|
673
|
-
`);if(Q?.ok&&Q.username)return new URL(`/${Q.username}/`,
|
|
673
|
+
`);if(Q?.ok&&Q.username)return new URL(`/${Q.username}/`,K).toString()}const N=await j.evaluate(`
|
|
674
674
|
(() => {
|
|
675
675
|
const isVisible = (el) => {
|
|
676
676
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -695,7 +695,7 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
695
695
|
const path = explicitProfile;
|
|
696
696
|
return { ok: !!path, path };
|
|
697
697
|
})()
|
|
698
|
-
`);if(!N?.ok||!N.path)return"";return new URL(N.path,
|
|
698
|
+
`);if(!N?.ok||!N.path)return"";return new URL(N.path,K).toString()}async function n(j){const G=await j.evaluate(`
|
|
699
699
|
(() => {
|
|
700
700
|
const isVisible = (el) => {
|
|
701
701
|
if (!(el instanceof HTMLElement)) return false;
|
|
@@ -715,4 +715,4 @@ import*as w from"node:fs";import*as W from"node:path";import{cli as s,Strategy a
|
|
|
715
715
|
|
|
716
716
|
return { ok: hrefs.length > 0, hrefs };
|
|
717
717
|
})()
|
|
718
|
-
`);return Array.isArray(G?.hrefs)?G.hrefs.filter(Boolean):[]}async function
|
|
718
|
+
`);return Array.isArray(G?.hrefs)?G.hrefs.filter(Boolean):[]}async function Ij(j){const G=await o(j);if(!G)return null;const N=await s(j,G);if(!N)return null;try{await j.goto(N);await j.wait({time:3});return new Set(await n(j))}catch{return null}}async function w(j,G){const N=await j.getCurrentUrl?.();if(N&&/\/p\//.test(N))return N;const O=await o(j),Q=await s(j,O);if(!Q)return"";await j.goto(Q);await j.wait({time:4});for(let X=0;X<8;X++){const Z=(await n(j)).find(($)=>!G.has($))||"";if(Z)return new URL(Z,K).toString();if(X<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 ${C})`},{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:Hj,func:async(j,G)=>{const N=_j(j),O=qj(G),Q=String(G.content??"").trim(),X=await Ij(N),_=X!==null,Z=X??new Set,$=Dj(O),q=Fj(O),T=Wj(O),L=Rj(O),B=xj(O),F=yj(O),h=wj(O),V=process.env.OPENCLI_INSTAGRAM_CAPTURE==="1",E=[],f=[],u=async()=>{if(!V)return;await a(N)},S=async()=>{if(!V)return;const H=await t(N);if(H.data.length)E.push(...H.data);if(H.errors.length)f.push(...H.errors)};try{try{return await Vj({page:N,mediaItems:O,content:Q,existingPostPaths:Z,canDiffProfile:_})}catch(H){if(H instanceof R)throw H;if(Nj(H)){let J="",P=!1;if(_)try{J=await w(N,Z);P=!0}catch{}if(J)return x(O,J);if(!P){const i=H instanceof Error?H.message:String(H);throw new Y(`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(!Jj(H))throw H;try{return await zj({page:N,mediaItems:O,content:Q,existingPostPaths:Z,canDiffProfile:_,commandAttemptBudget:$,preUploadDelaySeconds:q,uploadAttemptBudget:T,previewProbeWindowSeconds:L,finalPreviewWaitSeconds:B,preShareDelaySeconds:F,inlineUploadRetryBudget:h,installProtocolCapture:u,drainProtocolCapture:S,forceFreshStart:!0})}catch(J){if(J instanceof R)throw J;if(J instanceof Y)throw new Y(J.message,Tj(H,J));throw J}}}finally{if(V){try{await S()}catch{}b.writeFileSync(Zj,JSON.stringify({data:E,errors:f},null,2))}}}});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ArgumentError as J}from"@jackwener/opencli/errors";import{cli as K,Strategy as Q}from"@jackwener/opencli/registry";import{HOME_URL as T,mtopRequest as V}from"./mtop.js";function F(C,D){const x=String(C??"").trim();if(!x)return 1;if(!/^\d+$/.test(x)||Number(x)<1)throw new J(`xianyu collect ${D} must be a positive integer`);return Number(x)}K({site:"xianyu",name:"collect",access:"read",description:"列出我收藏的商品(分页)",domain:"www.goofish.com",strategy:Q.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"page",type:"int",default:1,help:"页码,从 1 开始"},{name:"page_size",type:"int",default:20,help:"每页条数"}],columns:["item_id","title","price","seller_nick","want_cnt","status_str"],func:async(C,D)=>{const x=F(D.page,"page"),I=F(D.page_size,"page_size");await C.goto(T);await C.wait(1);const B=await V(C,"collect",{api:"mtop.taobao.idle.web.favor.item.list",data:{pageNumber:x,pageSize:I},version:"1.0"}),G=(Array.isArray(B.items)?B.items:Array.isArray(B.itemList)?B.itemList:Array.isArray(B.list)?B.list:[]).filter((h)=>h&&typeof h==="object").map((h)=>({item_id:String(h.itemId||h.id||""),title:h.title||"",price:String(h.price||h.soldPrice||""),main_pic:h.picUrl||h.mainPic||"",seller_id:String(h.sellerId||h.userId||""),seller_nick:h.sellerNick||h.userNick||"",want_cnt:Number(h.wantCnt||0),status_str:h.itemStatusStr||""}));return G.length?G:[{item_id:"",title:"(无收藏)",price:"",seller_nick:"",want_cnt:0,status_str:""}]}});export const __test__={normalizePage:F};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{cli as D,Strategy as F}from"@jackwener/opencli/registry";import{HOME_URL as G,mtopRequest as I}from"./mtop.js";D({site:"xianyu",name:"location",access:"read",description:"获取账号默认发布地址",domain:"www.goofish.com",strategy:F.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"longitude",type:"float",default:121.4737,help:"经度(默认上海)"},{name:"latitude",type:"float",default:31.2304,help:"纬度(默认上海)"}],columns:["prov","city","area","poi","division_id"],func:async(h,z)=>{const B=Number(z.longitude??121.4737),C=Number(z.latitude??31.2304);await h.goto(G);await h.wait(1);const x=await I(h,"location",{api:"mtop.taobao.idle.local.poi.get",data:{longitude:B,latitude:C},version:"1.0"}),A=Array.isArray(x.commonAddresses)?x.commonAddresses:[],f=x.selectedPoi||(A.length?A[0]:null);if(!f)return[{prov:"",city:"",area:"",poi:"",division_id:""}];return[{prov:f.prov||"",city:f.city||"",area:f.area||"",poi:f.poi||"",division_id:String(f.divisionId||"")}]}});export const __test__={};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ArgumentError as P,CommandExecutionError as j}from"@jackwener/opencli/errors";import{cli as Z,Strategy as $}from"@jackwener/opencli/registry";import{HOME_URL as f,mtopRequest as W}from"./mtop.js";import{normalizeNumericId as A}from"./utils.js";function C(F,B){const G=String(F??"").trim();if(!G)return 1;if(!/^\d+$/.test(G)||Number(G)<1)throw new P(`xianyu ${B} must be a positive integer`);return Number(G)}function H(F){const B=F.cardData||{},G=B.priceInfo||{},J=B.picInfo||{},K=B.detailParams||{};return{item_id:String(B.id||K.itemId||""),title:B.title||K.title||"",price:G.price||K.soldPrice||"",status:B.itemStatus,main_pic:J.picUrl||K.picUrl||"",post_info:K.postInfo||""}}Z({site:"xianyu",name:"mine",access:"read",description:"列出当前账号在售/已发布商品(分页)",domain:"www.goofish.com",strategy:$.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"page",type:"int",default:1,help:"页码,从 1 开始"},{name:"page_size",type:"int",default:20,help:"每页条数"}],columns:["item_id","title","price","status","main_pic"],func:async(F,B)=>{const G=C(B.page,"page"),J=C(B.page_size,"page_size");await F.goto(f);await F.wait(1);const K=await W(F,"item mine",{api:"mtop.idle.web.user.page.head",data:{self:!0},version:"1.0"}),Q=String((K.base||K.baseInfo||{}).kcUserId||"").trim();if(!Q)throw new j("无法解析当前账号 userId,请确认已登录闲鱼");const V=await W(F,"item mine",{api:"mtop.idle.web.xyh.item.list",data:{userId:Q,pageNumber:G,pageSize:J},version:"1.0"}),X=(Array.isArray(V.cardList)?V.cardList:Array.isArray(V.itemList)?V.itemList:[]).filter((Y)=>Y&&typeof Y==="object").map(H);return X.length?X:[{item_id:"",title:"(无在售商品)",price:"",status:"",main_pic:""}]}});Z({site:"xianyu",name:"polish",access:"write",description:"擦亮商品(刷新曝光排序,不改内容)",domain:"www.goofish.com",strategy:$.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"item_id",required:!0,positional:!0,help:"商品 itemId"}],columns:["item_id","ok","already_polished"],func:async(F,B)=>{const G=A(B.item_id,"item_id","1017530128619");await F.goto(f);await F.wait(1);let J;try{J=await W(F,"item polish",{api:"mtop.taobao.idle.item.polish",data:{itemId:G},version:"1.0"})}catch(K){const Q=String(K?.message||K);if(/POLISH_AGAIN|已经擦亮/.test(Q))return[{item_id:G,ok:!0,already_polished:!0,note:"今天已擦亮过,明天再来"}];throw K}return[{item_id:G,ok:!0,already_polished:!1,data:J}]}});Z({site:"xianyu",name:"delete",access:"write",description:"下架/删除商品",domain:"www.goofish.com",strategy:$.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"item_id",required:!0,positional:!0,help:"商品 itemId"}],columns:["item_id","ok"],func:async(F,B)=>{const G=A(B.item_id,"item_id","1017530128619");await F.goto(f);await F.wait(1);const J=await W(F,"item delete",{api:"com.taobao.idle.item.delete",data:{itemId:G},version:"1.1"});return[{item_id:G,ok:!0,data:J}]}});Z({site:"xianyu",name:"edit",access:"write",description:"编辑商品(标题/详情/价格/划线价/库存,按需传)",domain:"www.goofish.com",strategy:$.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"item_id",required:!0,positional:!0,help:"商品 itemId"},{name:"title",help:"新标题"},{name:"desc",help:"新详情描述"},{name:"price",type:"float",help:"新价格(元)"},{name:"original_price",type:"float",help:"新划线价(元),传 0 清除"},{name:"quantity",type:"int",help:"新库存数量"}],columns:["item_id","ok"],func:async(F,B)=>{const G=A(B.item_id,"item_id","1017530128619"),J=(T)=>T!=null&&String(T).trim()!=="";if(![B.title,B.desc,B.price,B.original_price,B.quantity].some(J))throw new P("至少传一个待改字段:--title / --desc / --price / --original-price / --quantity");await F.goto(f);await F.wait(1);const K=await W(F,"item edit",{api:"mtop.idle.pc.idleitem.editDetail",data:{itemId:G},version:"1.0"});if(!K||!Object.keys(K).length)throw new j(`editDetail 返回为空:itemId=${G}(可能已下架 / 不属于当前账号 / 风控)`);const Q={...K,itemId:G};if(J(B.title)||J(B.desc)){const T={...K.itemTextDTO||{}};if(J(B.title))T.title=String(B.title);if(J(B.desc))T.desc=String(B.desc);if(T.titleDescSeparate==null)T.titleDescSeparate=!0;Q.itemTextDTO=T}if(J(B.price)||J(B.original_price)){const T={...K.itemPriceDTO||{}},X=Number(B.price),Y=Number(B.original_price);if(J(B.price)&&X>0)T.priceInCent=String(Math.round(X*100));if(J(B.original_price))if(Y>0)T.origPriceInCent=String(Math.round(Y*100));else delete T.origPriceInCent;Q.itemPriceDTO=T}if(J(B.quantity))Q.quantity=String(Number(B.quantity));if(Q.uniqueCode==null)Q.uniqueCode=String(Date.now()*1000);if(Q.sourceId==null)Q.sourceId="pcMainPublish";if(Q.bizcode==null)Q.bizcode="pcMainPublish";if(Q.publishScene==null)Q.publishScene="pcMainPublish";const V=await W(F,"item edit",{api:"mtop.idle.pc.idleitem.edit",data:Q,version:"1.0"});return[{item_id:G,ok:!0,data:V}]}});export const __test__={normalizeMyItem:H,normalizePage:C};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import{AuthRequiredError as P,CommandExecutionError as $,EmptyResultError as N,selectorError as j}from"@jackwener/opencli/errors";export const HOME_URL="https://www.goofish.com";const Q=["RGV587_ERROR","FAIL_SYS_USER_VALIDATE","哎哟喂","/punish"],V=["FAIL_SYS_SESSION_EXPIRED","FAIL_SYS_TOKEN_EXOIRED","FAIL_SYS_TOKEN_EMPTY","FAIL_SYS_ILLEGAL_ACCESS","令牌过期","NEED_LOGIN"];function X(B,z,F){return`
|
|
2
|
+
(async () => {
|
|
3
|
+
const clean = (v) => String(v ?? '').replace(/\\s+/g, ' ').trim();
|
|
4
|
+
const extractRetCode = (ret) => clean(Array.isArray(ret) ? ret[0] : '').split('::')[0] || '';
|
|
5
|
+
const waitFor = async (pred, t = 6000) => {
|
|
6
|
+
const s = Date.now();
|
|
7
|
+
while (Date.now() - s < t) { if (pred()) return true; await new Promise((r) => setTimeout(r, 150)); }
|
|
8
|
+
return false;
|
|
9
|
+
};
|
|
10
|
+
const bodyText = document.body?.innerText || '';
|
|
11
|
+
if (/请先登录|扫码登录|登录后/.test(bodyText)) return { error: 'auth-required' };
|
|
12
|
+
if (/验证码|安全验证|异常访问/.test(bodyText)) return { error: 'blocked' };
|
|
13
|
+
await waitFor(() => window.lib?.mtop?.request);
|
|
14
|
+
if (typeof window.lib?.mtop?.request !== 'function') return { error: 'mtop-not-ready' };
|
|
15
|
+
let response;
|
|
16
|
+
try {
|
|
17
|
+
response = await window.lib.mtop.request({
|
|
18
|
+
api: ${JSON.stringify(B)},
|
|
19
|
+
data: ${JSON.stringify(z)},
|
|
20
|
+
type: 'POST',
|
|
21
|
+
v: ${JSON.stringify(String(F))},
|
|
22
|
+
dataType: 'json',
|
|
23
|
+
needLogin: false,
|
|
24
|
+
needLoginPC: false,
|
|
25
|
+
sessionOption: 'AutoLoginOnly',
|
|
26
|
+
ecode: 0,
|
|
27
|
+
});
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const ret = error?.ret || [];
|
|
30
|
+
return {
|
|
31
|
+
error: 'mtop-request-failed',
|
|
32
|
+
error_code: extractRetCode(ret),
|
|
33
|
+
error_message: clean(Array.isArray(ret) ? ret.join(' | ') : error?.message || error),
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
const retCode = extractRetCode(response?.ret || []);
|
|
37
|
+
if (retCode && retCode !== 'SUCCESS') {
|
|
38
|
+
return {
|
|
39
|
+
error: 'mtop-response-error',
|
|
40
|
+
error_code: retCode,
|
|
41
|
+
error_message: clean((response?.ret || []).join(' | ')),
|
|
42
|
+
ret: response?.ret || [],
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return { ok: true, ret: response?.ret || [], data: response?.data || {} };
|
|
46
|
+
})()
|
|
47
|
+
`}function Z(B,z){if(z?.error==="auth-required")throw new P("www.goofish.com",`闲鱼 ${B} 需要已登录的浏览器会话`);if(z?.error==="blocked")throw new N(`xianyu ${B}`,"闲鱼页面被验证码 / 安全验证拦截,请人工处理后重试");if(z?.error==="mtop-not-ready")throw j("window.lib.mtop","闲鱼页面未完成初始化,无法调用 mtop 接口");const F=String(z?.error_code||""),J=String(z?.error_message||""),L=`${F} ${J}`;if(V.some((G)=>L.includes(G)))throw new P("www.goofish.com",`闲鱼 ${B} 登录态失效:${J||F}`);if(Q.some((G)=>L.includes(G)))throw new N(`xianyu ${B}`,`闲鱼 ${B} 触发风控:${J||F},请人工处理`);if(z?.error)throw new $(`闲鱼 ${B} 接口调用失败:${J||F||z.error}`)}export async function mtopRequest(B,z,{api:F,data:J,version:L="1.0"}){const G=await B.evaluate(X(F,J,L));Z(z,G);if(!G||typeof G!=="object")throw new N(`xianyu ${z}`,"闲鱼接口未返回有效数据");return G.data||{}}export function retOk(B){return Array.isArray(B)&&B.some((z)=>String(z).includes("SUCCESS"))}export const __test__={buildMtopEvaluate:X,classifyAndThrow:Z,RISK_KEYWORDS:Q,AUTH_KEYWORDS:V};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ArgumentError as M}from"@jackwener/opencli/errors";import{cli as $,Strategy as C}from"@jackwener/opencli/registry";import{HOME_URL as h,mtopRequest as f}from"./mtop.js";import{normalizeNumericId as Y}from"./utils.js";const j=["ALL","WAITING_PAY","WAITING_SEND","WAITING_RECEIVE","WAITING_RATE","SUCCESS","REFUND"];function q(x){const B=String(x||"ALL").trim().toUpperCase();if(!j.includes(B))throw new M(`xianyu order --status must be one of: ${j.join(", ")}`);return B}function L(x,B){const F=String(x??"").trim();if(!F)return 1;if(!/^\d+$/.test(F)||Number(F)<1)throw new M(`xianyu order ${B} must be a positive integer`);return Number(F)}$({site:"xianyu",name:"order",access:"read",description:"列出卖家已售订单(可按状态筛选)",domain:"www.goofish.com",strategy:C.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"page",type:"int",default:1,help:"页码,从 1 开始"},{name:"page_size",type:"int",default:20,help:"每页条数"},{name:"status",default:"ALL",help:`订单状态筛选:${j.join(" / ")}`}],columns:["order_id","item_id","buyer_nick","status_str","total_price","create_time"],func:async(x,B)=>{const F=L(B.page,"page"),Q=L(B.page_size,"page_size"),J=q(B.status);await x.goto(h);await x.wait(1);const X=(await f(x,"order list",{api:"mtop.taobao.idle.trade.merchant.sold.get",data:{pageNumber:F,rowsPerPage:Q,orderIds:"",queryCode:J,orderSearchParam:"{}"},version:"1.0"})).module||{},W=(Array.isArray(X.items)?X.items:[]).map((K)=>{const G=K.order||K.orderInfo||K,Z=K.buyer||K.buyerInfo||{},H=K.item||K.itemInfo||{};return{order_id:String(G.orderId||G.bizOrderId||G.id||""),item_id:String(H.itemId||H.id||""),item_title:H.title||"",buyer_id:String(Z.userId||Z.buyerId||""),buyer_nick:Z.nick||Z.userNick||"",status:G.status??G.orderStatus??"",status_str:G.statusStr||G.orderStatusStr||"",total_price:String(G.totalPrice||G.actualPrice||""),create_time:String(G.createTime||G.gmtCreate||"")}});return W.length?W:[{order_id:"",status_str:"(无已售订单)",total_price:"",buyer_nick:"",item_id:"",create_time:""}]}});$({site:"xianyu",name:"order-get",access:"read",description:"查询单个闲鱼订单详情",domain:"www.goofish.com",strategy:C.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"order_id",required:!0,positional:!0,help:"订单号 orderId"},{name:"role",default:"seller",help:"视角:seller / buyer"}],columns:["order_id","item_id","status_str","total_price","buyer_nick"],func:async(x,B)=>{const F=Y(B.order_id,"order_id","2800000000000000000"),Q=String(B.role||"seller").trim()==="buyer"?"buyer":"seller";await x.goto(h);await x.wait(1);const J=await f(x,"order get",{api:"mtop.idle.web.trade.order.detail",data:{orderId:F,role:Q},version:"1.0"}),V=J.itemInfoDTO||{},X=J.buyerInfoDTO||{},D=J.sellerInfoDTO||{},W=J.priceInfoDTO||{},K=J.logisticsInfoDTO||{},G=J.statusInfoDTO||{};return[{order_id:F,role:Q,item_id:String(V.itemId||""),title:V.title||"",main_pic:V.picUrl||"",quantity:Number(V.quantity||1),buyer_id:String(X.buyerId||""),buyer_nick:X.userNick||"",seller_id:String(D.sellerId||""),seller_nick:D.userNick||"",total_price:String(W.totalPrice||""),auction_price:String(W.auctionPrice||""),post_fee:String(W.postFee||""),status:G.orderStatus,status_str:G.orderStatusStr||"",logistic_no:K.logisticsNo||"",logistic_company:K.logisticsCompany||""}]}});$({site:"xianyu",name:"order-ship-dummy",access:"write",description:"虚拟发货(仅虚拟商品,无需物流单号)",domain:"www.goofish.com",strategy:C.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"order_id",required:!0,positional:!0,help:"订单号 orderId"},{name:"text",default:"",help:"发货附言(可选)"}],columns:["order_id","ok"],func:async(x,B)=>{const F=Y(B.order_id,"order_id","2800000000000000000");await x.goto(h);await x.wait(1);const Q=await f(x,"order ship-dummy",{api:"mtop.taobao.idle.logistic.consign.dummy",data:{orderId:F,tradeText:String(B.text||""),picList:[],newUnconsign:!0},version:"1.0"});return[{order_id:F,ok:!0,data:Q}]}});$({site:"xianyu",name:"order-ship-freeshipping",access:"write",description:"包邮发货(拼团活动专用)",domain:"www.goofish.com",strategy:C.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"order_id",required:!0,positional:!0,help:"订单号 bizOrderId"},{name:"item_id",required:!0,help:"商品 itemId"},{name:"buyer_id",required:!0,help:"买家 userId"}],columns:["order_id","ok"],func:async(x,B)=>{const F=Y(B.order_id,"order_id","2800000000000000000"),Q=Y(B.item_id,"item_id","1017530128619"),J=Y(B.buyer_id,"buyer_id","2208502750464");await x.goto(h);await x.wait(1);const V=await f(x,"order ship-freeshipping",{api:"mtop.idle.groupon.activity.seller.freeshipping",data:{bizOrderId:F,itemId:Number(Q),buyerId:Number(J)},version:"1.0"});return[{order_id:F,ok:!0,data:V}]}});export const __test__={normalizeStatus:q,normalizePage:L,ORDER_STATUS:j};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{ArgumentError as h}from"@jackwener/opencli/errors";import{cli as Q,Strategy as V}from"@jackwener/opencli/registry";import{HOME_URL as W,mtopRequest as X}from"./mtop.js";import{normalizeNumericId as Y}from"./utils.js";Q({site:"xianyu",name:"rate",access:"read",description:"拉某用户收到的信用评价列表",domain:"www.goofish.com",strategy:V.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"user_id",required:!0,positional:!0,help:"被评价用户 userId(明文数字 uid)"},{name:"page",type:"int",default:1,help:"页码,从 1 开始"},{name:"page_size",type:"int",default:20,help:"每页条数"}],columns:["rate_id","rater_nick","rate_content","create_time"],func:async(A,B)=>{const G=Y(B.user_id,"user_id","2208502750464"),J=Math.max(1,Number(B.page)||1),K=Math.max(1,Number(B.page_size)||20);await A.goto(W);await A.wait(1);const C=await X(A,"rate",{api:"mtop.idle.web.trade.rate.list",data:{rateType:0,ratedUid:G,raterType:0,rowsPerPage:K,pageNumber:J,foldFlag:0,fishAdCode:"",extraTag:""},version:"1.0"}),D=(Array.isArray(C.cardList)?C.cardList:[]).filter((x)=>x&&typeof x==="object").map((x)=>{const v=x.cardData||x;return{rate_id:String(v.rateId||v.id||""),rater_id:String(v.raterUid||v.userId||""),rater_nick:v.raterNick||v.userNick||v.nick||"",rate_content:v.content||v.rateContent||"",rate_type:v.rateType??v.attitude??"",item_id:String(v.itemId||""),item_title:v.itemTitle||"",create_time:String(v.gmtCreate||v.createTime||v.rateTime||""),reply:v.reply||v.sellerReply||""}});return D.length?D:[{rate_id:"",rater_nick:"(该用户暂无评价或评价不可见)",rate_content:"",create_time:""}]}});export const __test__={};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{cli as P,Strategy as Q}from"@jackwener/opencli/registry";import{HOME_URL as V,mtopRequest as W}from"./mtop.js";import{normalizeNumericId as X}from"./utils.js";P({site:"xianyu",name:"user",access:"read",description:"查询闲鱼用户主页信息(不传 user_id 看自己)",domain:"www.goofish.com",strategy:Q.COOKIE,navigateBefore:!1,browser:!0,args:[{name:"user_id",positional:!0,help:"目标用户 userId;留空看自己"}],columns:["user_id","nick","city","credit_level","fans_count","follow_count"],func:async(A,B)=>{const G=B.user_id!=null&&String(B.user_id).trim()!=="",J=G?X(B.user_id,"user_id","2208502750464"):"";await A.goto(V);await A.wait(1);const C=await W(A,"user",{api:"mtop.idle.web.user.page.head",data:G?{userId:J,self:!1}:{self:!0},version:"1.0"}),x=C.baseInfo||C.base||{},D=C.module||{},v=D.base||{},F=D.social||{},K=(D.tabs||{}).rate||{};return[{user_id:String(x.kcUserId||x.userId||J||""),nick:v.displayName||x.userNick||"",avatar:v.avatar&&v.avatar.avatar||v.avatar||"",location:v.ipLocation||"",signature:v.introduction||"",followers:Number(F.followers||0),following:Number(F.following||0),follow_status:F.followStatus??"",rate_count:Number(K.number||0),tags:x.tags||{}}]}});export const __test__={};
|