UNPKG

zhilian-auto-hi

Version:

智联招聘自动打招呼工具 - 自动化招聘流程的命令行工具

228 lines (227 loc) 11.4 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.OperationInterruptedException = void 0; exports.handleGreetModal = handleGreetModal; exports.autoGreet = autoGreet; exports.clickNextPageButton = clickNextPageButton; const index_1 = __importDefault(require("../logger/index")); const delay_1 = require("../utils/delay"); const graceful_shutdown_1 = __importDefault(require("./graceful-shutdown")); /** * 自动化操作服务 */ /** * 操作中断异常类 */ class OperationInterruptedException extends Error { constructor(message = '操作被中断') { super(message); this.name = 'OperationInterruptedException'; } } exports.OperationInterruptedException = OperationInterruptedException; /** * 检查是否应该中断操作 * @throws {OperationInterruptedException} 如果正在关闭则抛出异常 */ function checkShutdownStatus() { if (graceful_shutdown_1.default.isShuttingDown()) { throw new OperationInterruptedException('检测到关闭信号,操作被中断'); } } /** * 安全执行 Playwright 操作的包装器 * @param operation 要执行的操作 * @param operationName 操作名称(用于日志) * @returns 操作结果 */ async function safePlaywrightOperation(operation, operationName) { try { checkShutdownStatus(); return await operation(); } catch (error) { if (error instanceof OperationInterruptedException) { index_1.default.info(`${operationName}被中断: ${error.message}`); throw error; } // 重新抛出其他错误 throw error; } } /** * 处理打招呼弹窗函数 * @param page Playwright页面对象 * @returns 是否处理成功 * @throws {OperationInterruptedException} 如果操作被中断 */ async function handleGreetModal(page) { try { // 检查关闭状态 checkShutdownStatus(); index_1.default.info('检查是否出现打招呼弹窗...'); await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'short'), '等待弹窗检查延迟'); const modal = page.locator('.km-modal.km-modal--open'); const sendButton = modal.locator('.km-modal__footer button').filter({ hasText: '发送' }); // 检查弹窗是否存在且包含发送按钮 const modalCount = await safePlaywrightOperation(() => modal.count(), '检查弹窗数量'); const sendButtonCount = await safePlaywrightOperation(() => sendButton.count(), '检查发送按钮数量'); if (modalCount > 0 && sendButtonCount > 0) { index_1.default.highlight('检测到打招呼弹窗!(确认包含发送按钮)', '🔔'); await safePlaywrightOperation(() => sendButton.scrollIntoViewIfNeeded(), '滚动到发送按钮'); await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'short'), '点击前延迟'); await safePlaywrightOperation(() => sendButton.click(), '点击发送按钮'); index_1.default.success('成功点击发送按钮'); // 等待成功确认弹窗出现 - 增加等待时间 index_1.default.info('等待确认弹窗出现...', '⏳'); await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'medium'), '等待确认弹窗延迟'); // 使用重试机制查找并点击"我知道了"按钮 const maxRetries = 5; let confirmSuccess = false; for (let retry = 1; retry <= maxRetries; retry++) { // 在每次重试前检查关闭状态 checkShutdownStatus(); index_1.default.info(`第${retry}次尝试查找"我知道了"按钮...`, '🔍'); // 尝试多种选择器策略 const selectors = [ '.km-modal.km-modal--open .km-button--primary:has-text("我知道了")', '.km-modal.km-modal--open button:has-text("我知道了")', '.km-modal button:has-text("我知道了")', '.km-button--primary:has-text("我知道了")', 'button:has-text("我知道了")' ]; for (const selector of selectors) { try { checkShutdownStatus(); // 在每个选择器尝试前检查 const confirmButton = page.locator(selector); const confirmButtonCount = await safePlaywrightOperation(() => confirmButton.count(), `检查选择器"${selector}"的按钮数量`); if (confirmButtonCount > 0) { index_1.default.highlight(`使用选择器"${selector}"检测到确认弹窗!`, '🔔'); await safePlaywrightOperation(() => confirmButton.scrollIntoViewIfNeeded(), '滚动到确认按钮'); await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'short'), '点击确认按钮前延迟'); await safePlaywrightOperation(() => confirmButton.click(), '点击确认按钮'); index_1.default.success('成功点击"我知道了"按钮'); // 验证弹窗是否关闭 await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'short'), '验证弹窗关闭延迟'); const remainingModals = await safePlaywrightOperation(() => page.locator('.km-modal.km-modal--open').count(), '检查剩余弹窗数量'); if (remainingModals === 0) { index_1.default.success('确认弹窗已关闭'); confirmSuccess = true; break; } else { index_1.default.warning('弹窗仍然存在,继续尝试...'); } } } catch (selectorError) { if (selectorError instanceof OperationInterruptedException) { throw selectorError; // 重新抛出中断异常 } // 继续尝试下一个选择器 continue; } } if (confirmSuccess) { break; } // 如果这次重试失败,等待一段时间再试 if (retry < maxRetries) { index_1.default.info(`第${retry}次尝试失败,等待1秒后重试...`, '⏳'); await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'short'), '重试前延迟'); } } if (!confirmSuccess) { index_1.default.warning('多次尝试后仍未找到"我知道了"按钮,可能已自动关闭'); } await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'medium'), '打招呼完成后延迟'); index_1.default.success('打招呼发送完成'); return true; } else { // 提供更详细的检测结果信息 if (modalCount > 0 && sendButtonCount === 0) { index_1.default.info('检测到弹窗但无发送按钮,可能不是打招呼弹窗', 'ℹ️'); } else if (modalCount === 0) { index_1.default.info('未检测到弹窗,可能直接发送成功', 'ℹ️'); } return true; } } catch (error) { if (error instanceof OperationInterruptedException) { index_1.default.info(`处理弹窗被中断: ${error.message}`); throw error; // 重新抛出中断异常 } index_1.default.info(`处理弹窗时出现异常: ${error}`, 'ℹ️'); return true; } } /** * 自动打招呼函数 * @param page Playwright页面对象 * @returns 是否成功 * @throws {OperationInterruptedException} 如果操作被中断 */ async function autoGreet(page) { try { // 检查关闭状态 checkShutdownStatus(); index_1.default.info('正在查找打招呼按钮...'); // 使用更精确的选择器定位到侧边栏中的打招呼按钮 // 先定位到侧边栏操作区域,然后找到包含"打招呼"文本的按钮 const greetButton = page.locator('.new-resume-sidebar__actions-relation .resume-btn__text:has-text("打招呼")').locator('xpath=ancestor::button[1]'); index_1.default.info('找到侧边栏中的打招呼按钮'); // 滚动到按钮位置确保可见 await safePlaywrightOperation(() => greetButton.scrollIntoViewIfNeeded(), '滚动到打招呼按钮'); await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'short'), '点击打招呼按钮前延迟'); // 点击按钮 await safePlaywrightOperation(() => greetButton.click(), '点击打招呼按钮'); index_1.default.success('成功点击侧边栏中的打招呼按钮'); // 处理可能出现的弹窗 const modalHandled = await handleGreetModal(page); return modalHandled; } catch (error) { if (error instanceof OperationInterruptedException) { index_1.default.info(`自动打招呼被中断: ${error.message}`); throw error; // 重新抛出中断异常 } index_1.default.error(`点击打招呼按钮时出错: ${error}`); return false; } } /** * 点击下一页按钮函数 * @param page Playwright页面对象 * @returns 是否成功 * @throws {OperationInterruptedException} 如果操作被中断 */ async function clickNextPageButton(page) { try { // 检查关闭状态 checkShutdownStatus(); index_1.default.info('正在查找下一页按钮...'); const nextButton = page.locator('.new-shortcut-resume__right'); await safePlaywrightOperation(() => nextButton.scrollIntoViewIfNeeded(), '滚动到下一页按钮'); await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'short'), '点击下一页按钮前延迟'); await safePlaywrightOperation(() => nextButton.click(), '点击下一页按钮'); index_1.default.success('成功点击下一页按钮'); await safePlaywrightOperation(() => delay_1.DelayUtils.randomDelay(page, 'medium'), '下一页加载延迟'); await safePlaywrightOperation(() => page.waitForSelector('.new-resume-detail--inner', { timeout: 10000 }), '等待下一页加载完成'); index_1.default.success('下一页加载完成'); return true; } catch (error) { if (error instanceof OperationInterruptedException) { index_1.default.info(`点击下一页按钮被中断: ${error.message}`); throw error; // 重新抛出中断异常 } index_1.default.error(`点击下一页按钮时出错: ${error}`); return false; } }