Refactor GitCode daily bot
This commit is contained in:
100
utils/taskRunner.js
Normal file
100
utils/taskRunner.js
Normal file
@@ -0,0 +1,100 @@
|
||||
/**
|
||||
* 统一任务执行器
|
||||
*
|
||||
* 功能:
|
||||
* - try/catch 包裹每个任务
|
||||
* - 自动记录开始/结束/失败日志
|
||||
* - 单任务失败不影响其他任务
|
||||
*/
|
||||
|
||||
const logger = require('./logger');
|
||||
const config = require('../config');
|
||||
const { safeGoto } = require('./page');
|
||||
|
||||
function withTimeout(promise, timeoutMs, taskName) {
|
||||
let timer = null;
|
||||
const timeout = new Promise((_, reject) => {
|
||||
timer = setTimeout(() => {
|
||||
reject(new Error(`${taskName} 单任务超时 ${timeoutMs}ms`));
|
||||
}, timeoutMs);
|
||||
});
|
||||
|
||||
return Promise.race([promise, timeout]).finally(() => {
|
||||
if (timer) clearTimeout(timer);
|
||||
});
|
||||
}
|
||||
|
||||
async function prepareRetry(page, taskName, attempt) {
|
||||
logger.info(` → ${taskName} 准备第${attempt}次重试,回到积分中心...`);
|
||||
await safeGoto(page, config.urls.points, {
|
||||
label: '积分中心',
|
||||
timeout: config.navigationTimeout,
|
||||
waitFor: ['.g-user-avatar', '.novice-task-item-box'],
|
||||
}).catch((err) => {
|
||||
logger.warn(` → 重试前刷新积分中心失败: ${err.message}`);
|
||||
});
|
||||
await page.waitForTimeout(1000).catch(() => {});
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行任务(带防护)
|
||||
*
|
||||
* @param {string} taskName - 任务显示名称(如 "每日Star")
|
||||
* @param {Function} handler - 异步任务函数 async (page) => { ... }
|
||||
* @param {import('playwright').Page} page - 当前页面
|
||||
* @returns {Promise<boolean>} 任务是否成功
|
||||
*/
|
||||
async function runTask(taskName, handler, page) {
|
||||
logger.divider();
|
||||
logger.info(`开始执行任务: ${taskName}`);
|
||||
|
||||
const maxAttempts = Math.max(1, Number(config.retryCount) || 1);
|
||||
let lastError = null;
|
||||
|
||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
||||
if (attempt > 1) {
|
||||
await prepareRetry(page, taskName, attempt);
|
||||
}
|
||||
|
||||
try {
|
||||
logger.info(` → ${taskName} 第${attempt}/${maxAttempts}次尝试`);
|
||||
await withTimeout(handler(page), config.taskTimeout, taskName);
|
||||
logger.info(`✅ ${taskName} 执行成功`);
|
||||
return true;
|
||||
} catch (err) {
|
||||
lastError = err;
|
||||
if (attempt < maxAttempts) {
|
||||
logger.warn(`⚠️ ${taskName} 第${attempt}/${maxAttempts}次失败: ${err.message}`);
|
||||
} else {
|
||||
logger.error(`❌ ${taskName} 执行失败: ${err.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (lastError && lastError.stack) {
|
||||
logger.warn(lastError.stack);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 批量执行任务列表
|
||||
*
|
||||
* @param {import('playwright').Page} page - 主页面
|
||||
* @param {Array<{name: string, handler: Function}>} tasks - 任务列表
|
||||
* @returns {Promise<Object<string, boolean>>} 任务名 → 是否成功
|
||||
*/
|
||||
async function runTasks(page, tasks) {
|
||||
const results = {};
|
||||
|
||||
for (const { name, handler } of tasks) {
|
||||
results[name] = await runTask(name, handler, page);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
runTask,
|
||||
runTasks,
|
||||
};
|
||||
Reference in New Issue
Block a user