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累积/内存,重置滚动状态)