v10.7 18点平稳落地三连修: ①scrape批内18点软截止(处理完当前候选人即优雅收,不再被收队强杀) ②runner识别软截止正常收工 ③enforce-cutoff无条件以最新数据发日报(白天异常早发的不完整日报不再拦截18点正式日报)
This commit is contained in:
@@ -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);
|
||||
|
||||
+20
-11
@@ -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. 汇总输出 ----------
|
||||
|
||||
@@ -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累积/内存,重置滚动状态)
|
||||
|
||||
Reference in New Issue
Block a user