From bb841271d39b279348d970209bfe399a340d3697 Mon Sep 17 00:00:00 2001 From: hermes Date: Thu, 27 Aug 2026 10:12:33 +0000 Subject: [PATCH] =?UTF-8?q?v10.7=2018=E7=82=B9=E5=B9=B3=E7=A8=B3=E8=90=BD?= =?UTF-8?q?=E5=9C=B0=E4=B8=89=E8=BF=9E=E4=BF=AE:=20=E2=91=A0scrape?= =?UTF-8?q?=E6=89=B9=E5=86=8518=E7=82=B9=E8=BD=AF=E6=88=AA=E6=AD=A2(?= =?UTF-8?q?=E5=A4=84=E7=90=86=E5=AE=8C=E5=BD=93=E5=89=8D=E5=80=99=E9=80=89?= =?UTF-8?q?=E4=BA=BA=E5=8D=B3=E4=BC=98=E9=9B=85=E6=94=B6,=E4=B8=8D?= =?UTF-8?q?=E5=86=8D=E8=A2=AB=E6=94=B6=E9=98=9F=E5=BC=BA=E6=9D=80)=20?= =?UTF-8?q?=E2=91=A1runner=E8=AF=86=E5=88=AB=E8=BD=AF=E6=88=AA=E6=AD=A2?= =?UTF-8?q?=E6=AD=A3=E5=B8=B8=E6=94=B6=E5=B7=A5=20=E2=91=A2enforce-cutoff?= =?UTF-8?q?=E6=97=A0=E6=9D=A1=E4=BB=B6=E4=BB=A5=E6=9C=80=E6=96=B0=E6=95=B0?= =?UTF-8?q?=E6=8D=AE=E5=8F=91=E6=97=A5=E6=8A=A5(=E7=99=BD=E5=A4=A9?= =?UTF-8?q?=E5=BC=82=E5=B8=B8=E6=97=A9=E5=8F=91=E7=9A=84=E4=B8=8D=E5=AE=8C?= =?UTF-8?q?=E6=95=B4=E6=97=A5=E6=8A=A5=E4=B8=8D=E5=86=8D=E6=8B=A6=E6=88=AA?= =?UTF-8?q?18=E7=82=B9=E6=AD=A3=E5=BC=8F=E6=97=A5=E6=8A=A5)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- scripts/daily-runner.js | 2 ++ scripts/enforce-cutoff.js | 31 ++++++++++++++++++++----------- scripts/zhaopin-scrape.js | 5 +++++ 3 files changed, 27 insertions(+), 11 deletions(-) diff --git a/scripts/daily-runner.js b/scripts/daily-runner.js index 8ede18f..b9b37b8 100644 --- a/scripts/daily-runner.js +++ b/scripts/daily-runner.js @@ -333,6 +333,8 @@ async function verifyBlockedPage() { sessionTotal += newN; zeroStreak = newN === 0 ? zeroStreak + 1 : 0; + // v10.7:批内软截止输出识别(scrape 在18点后优雅退出,日志含"软截止")→ 正常收工 + if (/软截止/.test(out)) { log('批内已到18点软截止,收工'); maybeSendReport('time_cutoff', lastChatLeft); break; } if (pastCutoff()) { log('到达北京时间18:00,收工'); maybeSendReport('time_cutoff', lastChatLeft); break; } const restMin = zeroStreak >= 2 ? randInt(30, 45) : randInt(10, 20); diff --git a/scripts/enforce-cutoff.js b/scripts/enforce-cutoff.js index 42304c1..5d60d8d 100644 --- a/scripts/enforce-cutoff.js +++ b/scripts/enforce-cutoff.js @@ -77,26 +77,35 @@ for (const acct of ['hr1', 'hr2']) { } } -// ---------- 3. 日报补发 ---------- +// ---------- 3. 日报发送(v1.1:18:05 无条件生成当日完整数据日报发群)---------- +// 2026-08-27 教训:白天异常时旧代码发过一次不完整日报并落 flag,收队看到 flag 就跳过, +// 导致当天最终日报缺失。现在收队是日报唯一出口,无条件以最新数据重发。 const sentFlag = path.join(STATE_DIR, `report-sent-${today()}`); -if (fs.existsSync(sentFlag)) { - reportSent = true; - log('日报今日已由 runner 发送 ✅'); -} else { - log('日报未发送,补发中...'); +{ + log('生成当日日报并发群...'); try { const env = { ...process.env }; - // 从状态文件取聊天剩余 - for (const [acct, key] of [['hr1', 'CHAT_LEFT_HR1'], ['hr2', 'CHAT_LEFT_HR2']]) { - try { const s = JSON.parse(fs.readFileSync(path.join(STATE_DIR, `state-${acct}.json`), 'utf8')); if (s.chatLeft != null) env[key] = String(s.chatLeft); } catch { } + // 从状态文件取聊天剩余 + 状态文案 + for (const [acct, key, skey] of [['hr1', 'CHAT_LEFT_HR1', 'HR1_STATUS'], ['hr2', 'CHAT_LEFT_HR2', 'HR2_STATUS']]) { + try { + const s = JSON.parse(fs.readFileSync(path.join(STATE_DIR, `state-${acct}.json`), 'utf8')); + if (s.chatLeft != null) env[key] = String(s.chatLeft); + const map = { + kicked_out: '🚨 被顶号(他处登录)', login_expired: '🚨 登录失效', platform_blocked: '⛔ 平台阻碍(限额等)', + qr_expired: '🚨 等扫码但二维码过期', waiting_relogin: '⏳ 等待扫码登录', stopped_by_user: '⏹️ 手动停止', + tunnel_failed: '⚠️ 隧道失败', alert: '⚠️ 异常中止', chat_exhausted: '✅ 聊天耗尽收工', + time_cutoff: '✅ 18点收工', running: '✅ 在线', + }; + if (s.date === today()) env[skey] = map[s.status] || '✅ 在线'; + } catch { } } const md = execSync(`node ${SCRIPTS}/daily-report.js`, { cwd: SCRIPTS, encoding: 'utf8', timeout: 60000, env }); fs.writeFileSync(`/tmp/daily-report-enforce-${today()}.md`, md); execSync(`${HERMES} send -t "feishu:${GROUP}" -f /tmp/daily-report-enforce-${today()}.md`, { stdio: ['pipe', 'pipe', 'pipe'], timeout: 60000 }); fs.writeFileSync(sentFlag, new Date().toISOString()); reportSent = true; - log('日报已补发到群 ✅'); - } catch (e) { findings.push(`日报补发失败: ${String(e.message).slice(0, 150)}`); } + log('日报已发送到群 ✅'); + } catch (e) { findings.push(`日报发送失败: ${String(e.message).slice(0, 150)}`); } } // ---------- 4. 汇总输出 ---------- diff --git a/scripts/zhaopin-scrape.js b/scripts/zhaopin-scrape.js index ccd35a6..4753158 100755 --- a/scripts/zhaopin-scrape.js +++ b/scripts/zhaopin-scrape.js @@ -373,6 +373,11 @@ async function scrapeZhaopin(maxCount = 20, fullMode = false, scoreOnly = false) console.log(`启动跳过 ${skipN} 人,从 data-index=${currentDataIndex} 开始`); while (newCount < maxCount) { + // v10.7:批内 18 点软截止——处理完当前候选人就优雅收(STOP.flag 同款退出路径,日志带统计) + // 北京18:00 = UTC 10:00。之后不再开新弹窗,保证 18 点平稳落地,收队任务无需强杀 + if (process.env.DAILY_CUTOFF !== '0' && new Date(Date.now() + 8 * 3600 * 1000).getUTCHours() >= 18) { + return stop('⏰ 到达北京时间18:00,批内软截止收工', 0); + } // v10.3:随时叫停——每轮循环检查停止旗标(当前候选人处理完才退,不会打断打招呼中途) if (stopRequested()) return stop('🛑 收到停止指令,优雅退出本批', 0); // v10.4:账号累计处理满200人 → 刷新页面(清虚拟列表DOM累积/内存,重置滚动状态)