zhilian-auto-hi
Version:
智联招聘自动打招呼工具 - 自动化招聘流程的命令行工具
228 lines (227 loc) • 11.4 kB
JavaScript
;
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;
}
}