create.js
1 import { cli, Strategy } from '@jackwener/opencli/registry'; 2 /** 3 * 发布即刻动态 4 * 5 * 即刻首页 /following 顶部有内联发帖框("分享你的想法..."), 6 * 直接在其中输入文本,点击"发送"按钮即可发布。 7 */ 8 cli({ 9 site: 'jike', 10 name: 'create', 11 description: '发布即刻动态', 12 domain: 'web.okjike.com', 13 strategy: Strategy.UI, 14 browser: true, 15 args: [ 16 { name: 'text', type: 'string', required: true, positional: true, help: '动态正文内容' }, 17 ], 18 columns: ['status', 'message'], 19 func: async (page, kwargs) => { 20 // 1. 导航到首页(有内联发帖框) 21 await page.goto('https://web.okjike.com'); 22 // 2. 在发帖框中输入文本 23 const textResult = await page.evaluate(`(async () => { 24 try { 25 const textToInsert = ${JSON.stringify(kwargs.text)}; 26 27 // 首页发帖框在 _postForm_ 容器内,查找其中的 contenteditable 28 const form = document.querySelector('[class*="_postForm_"]'); 29 const editor = form 30 ? form.querySelector('[contenteditable="true"]') 31 : document.querySelector('[contenteditable="true"]'); 32 33 if (editor) { 34 editor.focus(); 35 // 用 ClipboardEvent paste 触发 React 状态更新 36 const dt = new DataTransfer(); 37 dt.setData('text/plain', textToInsert); 38 editor.dispatchEvent(new ClipboardEvent('paste', { 39 clipboardData: dt, bubbles: true, cancelable: true, 40 })); 41 await new Promise(r => setTimeout(r, 800)); 42 43 // 检查是否成功插入 44 const inserted = editor.textContent || ''; 45 if (inserted.length > 0) { 46 return { ok: true, message: 'contenteditable' }; 47 } 48 } 49 50 // 回退:textarea 51 const textarea = form 52 ? form.querySelector('textarea') 53 : document.querySelector('textarea'); 54 55 if (textarea) { 56 textarea.focus(); 57 const setter = Object.getOwnPropertyDescriptor( 58 HTMLTextAreaElement.prototype, 'value' 59 )?.set; 60 setter?.call(textarea, textToInsert); 61 textarea.dispatchEvent(new Event('input', { bubbles: true })); 62 await new Promise(r => setTimeout(r, 500)); 63 return { ok: true, message: 'textarea' }; 64 } 65 66 return { ok: false, message: '未找到发帖输入框' }; 67 } catch (e) { 68 return { ok: false, message: e.toString() }; 69 } 70 })()`); 71 if (!textResult.ok) { 72 return [{ status: 'failed', message: textResult.message }]; 73 } 74 // 3. 点击"发送"按钮 75 const submitResult = await page.evaluate(`(async () => { 76 try { 77 await new Promise(r => setTimeout(r, 500)); 78 79 // 即刻首页发帖框的按钮文字为"发送" 80 const candidates = [ 81 ...Array.from(document.querySelectorAll('button')).filter(btn => { 82 const text = btn.textContent?.trim() || ''; 83 return text === '发送' || text === '发布'; 84 }), 85 ].filter(el => el && !el.disabled); 86 87 if (candidates.length === 0) { 88 return { ok: false, message: '未找到可用的发送按钮(按钮可能因内容为空而禁用)' }; 89 } 90 91 candidates[0].click(); 92 return { ok: true, message: '动态发布成功' }; 93 } catch (e) { 94 return { ok: false, message: e.toString() }; 95 } 96 })()`); 97 if (submitResult.ok) { 98 await page.wait(3); 99 } 100 return [{ 101 status: submitResult.ok ? 'success' : 'failed', 102 message: submitResult.message, 103 }]; 104 }, 105 });