diff --git a/妙手建品/RECIPE.md b/妙手建品/RECIPE.md new file mode 100644 index 0000000..32fa6e3 --- /dev/null +++ b/妙手建品/RECIPE.md @@ -0,0 +1,32 @@ +# 妙手 ERP(erp.91miaoshou.com)侦察 — 卡在登录,需要人工决策 + +## 现状(2026-09-20) +浏览器里有一个妙手标签,停在**未登录的营销首页**,页面上已有报错: +`账户或密码错误,再失败9次后将锁定账号1个小时` + `请输入图形验证码` +→ 之前有人试过登录且失败过;账号有失败计数,**再错 9 次锁 1 小时**。 + +## ⚠️ 舰队技能档已过期(`agent-a2-import/skills/ecommerce/miaoshou-erp/SKILL.md`) +| 文档写的 | 实测 | +|---|---| +| 账号 `.account-input` / 密码 `.password-input` / 按钮 `.login-button` | **全部不存在**(`account:0, password:0, hasBtn:false`) | +| 「风控较松,验证码非强制,可尝试跳过」 | **现在强制图形验证码**(页面明确提示「请输入图形验证码」) | +| 账号 `xiaochaoren2026` | 档里有;但配的是**子账号,文档自己写明「产品发布不可用」,只有采集箱/导入采集** | + +### 实测的真实登录表单(登录弹窗内) +| 字段 | 选择器 | +|---|---| +| 账号 | `input.login-form__input[placeholder="手机号/子账号/邮箱"]` | +| 密码 | `input.login-form__input[type=password]` | +| 验证码 | `input.captcha-field__input`(placeholder `请输入验证码`) | +| 登录按钮 | `button.login-form__submit-btn`(文案 `立即登录`) | + +## 为什么我没继续 +1. **要花登录次数**:验证码是图形码,试错就计数,再错 9 次锁 1 小时; +2. **账号权限**:档里明确写了这个子账号**不能发布产品**,只有采集箱/导入采集 + —— 也就是说「妙手各平台铺货」用这个账号**可能根本做不到**,需要主账号; +3. 舰队技能档的凭据可能已轮换(否则不会出现失败计数)。 + +## 三条可走的路(需要你选) +- **A(最稳)**:你在 VNC 里手动登录一次,我接手已登录的会话做采集箱/导入采集的自动化; +- **B**:授权我用积分/打码能力试 1–2 次(会消耗登录计数,且若密码已轮换就是白费); +- **C**:先跳过妙手,把最后的力量放在 Lazada / fruugo(这两条不需要新账号)。 diff --git a/妙手建品/ms_form.mjs b/妙手建品/ms_form.mjs new file mode 100644 index 0000000..a6c719f --- /dev/null +++ b/妙手建品/ms_form.mjs @@ -0,0 +1,14 @@ +import { listPages, wait } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { CDP, SAN as S } from '/tmp/cdp3.mjs'; +const t = (await listPages()).find(x=>/91miaoshou/i.test(x.url)); +const c = await CDP.connect(t.webSocketDebuggerUrl); +await c.send('Runtime.enable'); await wait(1200); +const o = await c.evalJS(`(()=>{ + const ins=[...document.querySelectorAll('input')].filter(e=>e.offsetParent!==null).map(e=>({ + type:e.type||'', ph:e.placeholder||'', cls:String(e.className).slice(0,44), name:e.name||'', id:e.id||''})); + const btns=[...document.querySelectorAll('button,a[role=button],.btn')].filter(e=>e.offsetParent!==null) + .map(e=>({t:(e.innerText||'').trim().slice(0,12), cls:String(e.className).slice(0,40)})).filter(x=>x.t).slice(0,10); + const imgs=[...document.querySelectorAll('img')].filter(e=>e.offsetParent!==null&&/captcha|code|verify/i.test(e.src||'')).map(e=>e.src.slice(0,80)); + return JSON.stringify({ins:ins.slice(0,8), btns, captchaImgs:imgs.slice(0,3)});})()`); +console.log('登录表单:', S(String(o)).slice(0,900)); +process.exit(0); diff --git a/妙手建品/ms_login.mjs b/妙手建品/ms_login.mjs new file mode 100644 index 0000000..ce8367b --- /dev/null +++ b/妙手建品/ms_login.mjs @@ -0,0 +1,36 @@ +import { listPages, wait } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { CDP, SAN as S } from '/tmp/cdp3.mjs'; +import { readFileSync } from 'fs'; +// 凭据来自舰队技能档 agent-a2-import/skills/ecommerce/miaoshou-erp/SKILL.md(不回显) +const doc = readFileSync('/home/admin9webs/agent-a2-import/skills/ecommerce/miaoshou-erp/SKILL.md','utf8'); +const acc = (doc.match(/xiaochaoren\d+/) || [])[0]; +const pwd = (doc.match(/Bingo\d+@/) || [])[0]; +console.log('账号:', acc ? acc.slice(0,4)+'***' : '未解析到', ' 密码:', pwd ? '已解析' : '未解析'); +if(!acc || !pwd){ console.log('凭据解析失败,放弃登录'); process.exit(1); } + +const t = (await listPages()).find(x=>/91miaoshou/i.test(x.url)); +if(!t){ console.log('无妙手标签'); process.exit(1); } +const c = await CDP.connect(t.webSocketDebuggerUrl); +await c.send('Page.enable'); await c.send('Runtime.enable'); +await wait(1500); + +const filled = await c.evalJS(`(()=>{ + const set=(sel,v)=>{const el=document.querySelector(sel); if(!el) return 0; + const setter=Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype,'value').set; + setter.call(el,v); el.dispatchEvent(new Event('input',{bubbles:true})); el.dispatchEvent(new Event('change',{bubbles:true})); + el.dispatchEvent(new KeyboardEvent('keyup',{bubbles:true})); return 1;}; + const a=set('.account-input', ${JSON.stringify(acc)}); + const p=set('.password-input', ${JSON.stringify(pwd)}); + const captcha=document.querySelector('.captcha-text'); + return JSON.stringify({account:a, password:p, hasCaptcha:!!captcha, hasBtn:!!document.querySelector('.login-button')});})()`); +console.log('填表:', S(String(filled)).slice(0,200)); +await wait(600); +const clicked = await c.evalJS(`(()=>{const b=document.querySelector('.login-button'); if(!b) return 'no-btn'; + for(const ev of ['mousedown','mouseup','click']) b.dispatchEvent(new MouseEvent(ev,{bubbles:true,cancelable:true,view:window})); return 'clicked';})()`); +console.log('提交:', String(clicked)); +await wait(10000); +const after = await c.evalJS(`(()=>{const err=[...document.querySelectorAll('*')].filter(e=>e.children.length===0&&/错误|失败|验证码|锁定/.test(e.innerText||'')).map(e=>(e.innerText||'').trim()).slice(0,3); + return JSON.stringify({href:location.href, title:document.title, login:!!document.querySelector('input[type=password]'), err});})()`); +console.log('结果:', S(String(after)).slice(0,400)); +try{ await c.shot('/tmp/ms_login.jpg'); console.log('shot ok'); }catch(e){} +process.exit(0); diff --git a/妙手建品/ms_probe1.mjs b/妙手建品/ms_probe1.mjs new file mode 100644 index 0000000..b56794e --- /dev/null +++ b/妙手建品/ms_probe1.mjs @@ -0,0 +1,15 @@ +import { newPage, closePage, wait, listPages } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { SAN as S } from '/tmp/cdp3.mjs'; +// 先看有没有现成的妙手标签 +const pages = (await listPages()).filter(t=>/miaoshou|91miao/i.test(t.url)); +console.log('现有妙手标签:', pages.length, pages.map(t=>t.url.slice(0,60)).join(' | ')); +const c = pages.length ? null : await newPage('https://erp.91miaoshou.com/'); +if (c) await wait(12000); +if (!c) { console.log('已有标签,跳过新开'); process.exit(0); } +const o = await c.evalJS(`(()=>{ + const nav=[...document.querySelectorAll('a,li,div')].filter(e=>e.offsetParent!==null&&(e.innerText||'').trim().length>1&&(e.innerText||'').trim().length<8).map(e=>(e.innerText||'').trim()); + const uniq=[...new Set(nav)].slice(0,40); + return JSON.stringify({title:document.title, href:location.href, hasLogin:!!document.querySelector('input[type=password]'), nav:uniq});})()`); +console.log('页面:', S(String(o)).slice(0,900)); +try{ await c.shot('/tmp/ms1.jpg'); console.log('shot ok'); }catch(e){} +await closePage(c); process.exit(0); diff --git a/妙手建品/ms_probe2.mjs b/妙手建品/ms_probe2.mjs new file mode 100644 index 0000000..e43307a --- /dev/null +++ b/妙手建品/ms_probe2.mjs @@ -0,0 +1,15 @@ +import { listPages, wait } from '/home/admin9webs/shein建品/sh_lib.mjs'; +import { CDP, SAN as S } from '/tmp/cdp3.mjs'; +const t = (await listPages()).find(x=>/91miaoshou/i.test(x.url)); +if(!t){ console.log('无妙手标签'); process.exit(1); } +const c = await CDP.connect(t.webSocketDebuggerUrl); +await c.send('Page.enable'); await c.send('Runtime.enable'); +await wait(1500); +const o = await c.evalJS(`(()=>{ + const nav=[...new Set([...document.querySelectorAll('a,span,li')].filter(e=>e.offsetParent!==null).map(e=>(e.innerText||'').trim()).filter(t=>t&&t.length<=10))].slice(0,45); + const menu=[...document.querySelectorAll('.el-menu-item,.ant-menu-item,[class*=menu-item]')].map(e=>(e.innerText||'').trim()).filter(Boolean).slice(0,25); + const body=(document.body.innerText||'').replace(/\\s+/g,' ').trim().slice(0,300); + return JSON.stringify({title:document.title, href:location.href, login:!!document.querySelector('input[type=password]'), menu, nav:nav.slice(0,30), body});})()`); +console.log('妙手页:', S(String(o)).slice(0,1500)); +try{ await c.shot('/tmp/ms2.jpg'); console.log('shot ok'); }catch(e){} +process.exit(0);