mirror of
https://github.com/JimLiu/baoyu-skills.git
synced 2026-07-13 22:29:48 +08:00
fix(baoyu-post-to-wechat): improve image upload with file chooser interception
This commit is contained in:
@@ -551,37 +551,126 @@ export async function postToWeChat(options: WeChatBrowserOptions): Promise<void>
|
|||||||
const absolutePaths = images.map(p => path.isAbsolute(p) ? p : path.resolve(process.cwd(), p));
|
const absolutePaths = images.map(p => path.isAbsolute(p) ? p : path.resolve(process.cwd(), p));
|
||||||
console.log(`[wechat-browser] Images: ${absolutePaths.join(', ')}`);
|
console.log(`[wechat-browser] Images: ${absolutePaths.join(', ')}`);
|
||||||
|
|
||||||
const { root } = await cdp.send<{ root: { nodeId: number } }>('DOM.getDocument', {}, { sessionId });
|
// --- PRIMARY approach: intercept file chooser dialog ---
|
||||||
|
let uploadSuccess = false;
|
||||||
|
try {
|
||||||
|
console.log('[wechat-browser] [primary] Enabling file chooser interception...');
|
||||||
|
await cdp.send('Page.setInterceptFileChooserDialog', { enabled: true }, { sessionId });
|
||||||
|
|
||||||
// Try primary selector, then fallback to any multi-file image input
|
// Set up listener for file chooser opened event BEFORE clicking
|
||||||
let { nodeId } = await cdp.send<{ nodeId: number }>('DOM.querySelector', {
|
const fileChooserPromise = new Promise<{ backendNodeId: number; mode: string }>((resolve, reject) => {
|
||||||
nodeId: root.nodeId,
|
const timeout = setTimeout(() => reject(new Error('File chooser dialog not opened within 10s')), 10_000);
|
||||||
selector: '.js_upload_btn_container input[type=file]',
|
cdp!.on('Page.fileChooserOpened', (params: unknown) => {
|
||||||
}, { sessionId });
|
clearTimeout(timeout);
|
||||||
|
const p = params as { backendNodeId: number; mode: string };
|
||||||
|
console.log(`[wechat-browser] [primary] File chooser opened: backendNodeId=${p.backendNodeId}, mode=${p.mode}`);
|
||||||
|
resolve(p);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
if (!nodeId) {
|
// Trigger file chooser by calling .click() on the file input with userGesture
|
||||||
console.log('[wechat-browser] Primary file input not found, trying fallback selector...');
|
const fileInputSelectors = [
|
||||||
const fallback = await cdp.send<{ nodeId: number }>('DOM.querySelector', {
|
'.js_upload_btn_container input[type=file]',
|
||||||
nodeId: root.nodeId,
|
'input[type=file][multiple][accept*="image"]',
|
||||||
selector: 'input[type=file][multiple][accept*="image"]',
|
'input[type=file][accept*="image"]',
|
||||||
|
'input[type=file][multiple]',
|
||||||
|
'input[type=file]',
|
||||||
|
];
|
||||||
|
|
||||||
|
console.log('[wechat-browser] [primary] Clicking file input via JS .click() with userGesture...');
|
||||||
|
const clickResult = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {
|
||||||
|
expression: `
|
||||||
|
(function() {
|
||||||
|
const selectors = ${JSON.stringify(fileInputSelectors)};
|
||||||
|
for (const sel of selectors) {
|
||||||
|
const el = document.querySelector(sel);
|
||||||
|
if (el) {
|
||||||
|
el.click();
|
||||||
|
return JSON.stringify({ clicked: sel });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const debug = [];
|
||||||
|
document.querySelectorAll('input[type=file]').forEach((inp, i) => {
|
||||||
|
debug.push({ i, accept: inp.accept, multiple: inp.multiple, parentClass: inp.parentElement?.className?.slice(0, 60) });
|
||||||
|
});
|
||||||
|
return JSON.stringify({ error: 'no file input found', fileInputs: debug });
|
||||||
|
})()
|
||||||
|
`,
|
||||||
|
returnByValue: true,
|
||||||
|
userGesture: true,
|
||||||
}, { sessionId });
|
}, { sessionId });
|
||||||
nodeId = fallback.nodeId;
|
console.log(`[wechat-browser] [primary] Click result: ${clickResult.result.value}`);
|
||||||
|
|
||||||
|
const clickStatus = JSON.parse(clickResult.result.value);
|
||||||
|
if (clickStatus.error) {
|
||||||
|
throw new Error(`File input not found: ${clickStatus.error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the file chooser event
|
||||||
|
console.log('[wechat-browser] [primary] Waiting for file chooser dialog...');
|
||||||
|
const chooser = await fileChooserPromise;
|
||||||
|
|
||||||
|
console.log(`[wechat-browser] [primary] Setting files via backendNodeId=${chooser.backendNodeId}...`);
|
||||||
|
await cdp.send('DOM.setFileInputFiles', {
|
||||||
|
files: absolutePaths,
|
||||||
|
backendNodeId: chooser.backendNodeId,
|
||||||
|
}, { sessionId });
|
||||||
|
console.log('[wechat-browser] [primary] Files set successfully via file chooser interception');
|
||||||
|
uploadSuccess = true;
|
||||||
|
} catch (primaryErr) {
|
||||||
|
console.log(`[wechat-browser] [primary] File chooser approach failed: ${primaryErr instanceof Error ? primaryErr.message : String(primaryErr)}`);
|
||||||
|
// Disable interception before falling back
|
||||||
|
try { await cdp.send('Page.setInterceptFileChooserDialog', { enabled: false }, { sessionId }); } catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!nodeId) throw new Error('File input not found');
|
// --- FALLBACK approach: direct DOM.setFileInputFiles on nodeId ---
|
||||||
|
if (!uploadSuccess) {
|
||||||
|
console.log('[wechat-browser] [fallback] Trying direct DOM.setFileInputFiles...');
|
||||||
|
const { root } = await cdp.send<{ root: { nodeId: number } }>('DOM.getDocument', {}, { sessionId });
|
||||||
|
|
||||||
await cdp.send('DOM.setFileInputFiles', {
|
const fileInputSelectors = [
|
||||||
nodeId,
|
'.js_upload_btn_container input[type=file]',
|
||||||
files: absolutePaths,
|
'input[type=file][multiple][accept*="image"]',
|
||||||
}, { sessionId });
|
'input[type=file][accept*="image"]',
|
||||||
|
'input[type=file][multiple]',
|
||||||
|
'input[type=file]',
|
||||||
|
];
|
||||||
|
|
||||||
// Dispatch change event to trigger the upload
|
let nodeId = 0;
|
||||||
await cdp.send('Runtime.evaluate', {
|
for (const sel of fileInputSelectors) {
|
||||||
expression: `
|
const result = await cdp.send<{ nodeId: number }>('DOM.querySelector', { nodeId: root.nodeId, selector: sel }, { sessionId });
|
||||||
const fileInput = document.querySelector('.js_upload_btn_container input[type=file]') || document.querySelector('input[type=file][multiple][accept*="image"]');
|
if (result.nodeId) {
|
||||||
if (fileInput) fileInput.dispatchEvent(new Event('change', { bubbles: true }));
|
console.log(`[wechat-browser] [fallback] Found file input with selector: ${sel}`);
|
||||||
`,
|
nodeId = result.nodeId;
|
||||||
}, { sessionId });
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!nodeId) throw new Error('File input not found with any selector');
|
||||||
|
|
||||||
|
await cdp.send('DOM.setFileInputFiles', { nodeId, files: absolutePaths }, { sessionId });
|
||||||
|
console.log('[wechat-browser] [fallback] Files set via nodeId');
|
||||||
|
|
||||||
|
// Dispatch change event
|
||||||
|
await cdp.send('Runtime.evaluate', {
|
||||||
|
expression: `
|
||||||
|
(function() {
|
||||||
|
const selectors = ${JSON.stringify(fileInputSelectors)};
|
||||||
|
for (const sel of selectors) {
|
||||||
|
const el = document.querySelector(sel);
|
||||||
|
if (el) {
|
||||||
|
el.dispatchEvent(new Event('change', { bubbles: true }));
|
||||||
|
el.dispatchEvent(new Event('input', { bubbles: true }));
|
||||||
|
return 'dispatched on ' + sel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 'no input found for event dispatch';
|
||||||
|
})()
|
||||||
|
`,
|
||||||
|
returnByValue: true,
|
||||||
|
}, { sessionId });
|
||||||
|
console.log('[wechat-browser] [fallback] Change event dispatched');
|
||||||
|
}
|
||||||
|
|
||||||
// Wait for images to upload
|
// Wait for images to upload
|
||||||
console.log('[wechat-browser] Waiting for images to upload...');
|
console.log('[wechat-browser] Waiting for images to upload...');
|
||||||
@@ -589,11 +678,16 @@ export async function postToWeChat(options: WeChatBrowserOptions): Promise<void>
|
|||||||
for (let i = 0; i < 30; i++) {
|
for (let i = 0; i < 30; i++) {
|
||||||
await sleep(2000);
|
await sleep(2000);
|
||||||
const uploadCheck = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {
|
const uploadCheck = await cdp.send<{ result: { value: string } }>('Runtime.evaluate', {
|
||||||
expression: `JSON.stringify({ uploaded: document.querySelectorAll('.weui-desktop-upload__thumb, .pic_item, [class*=upload_thumb]').length })`,
|
expression: `
|
||||||
|
JSON.stringify({
|
||||||
|
uploaded: document.querySelectorAll('.weui-desktop-upload__thumb, .pic_item, [class*=upload_thumb], [class*="pic_item"], [class*="upload__thumb"]').length,
|
||||||
|
loading: document.querySelectorAll('[class*="upload_loading"], [class*="uploading"], .weui-desktop-upload__loading').length
|
||||||
|
})
|
||||||
|
`,
|
||||||
returnByValue: true,
|
returnByValue: true,
|
||||||
}, { sessionId });
|
}, { sessionId });
|
||||||
const status = JSON.parse(uploadCheck.result.value);
|
const status = JSON.parse(uploadCheck.result.value);
|
||||||
console.log(`[wechat-browser] Upload progress: ${status.uploaded}/${targetCount}`);
|
console.log(`[wechat-browser] Upload progress: ${status.uploaded}/${targetCount} (loading: ${status.loading})`);
|
||||||
if (status.uploaded >= targetCount) break;
|
if (status.uploaded >= targetCount) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user