# boss.fl **Repository Path**: mayunwe/boss.fl ## Basic Information - **Project Name**: boss.fl - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-10-14 - **Last Updated**: 2025-10-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README ``` // == 配置区域 == // 调试模式:true = 只点击不发送消息,false = 正常发送消息 const DEBUG_MODE = false; // 改为 true 开启调试模式 // 默认打招呼消息(用户未输入时使用) const defaultGreetingMessages = [ "Hi,之前和您联系过,看到消息未回复,猜您可能在忙,想问问您目前还在看机会吗?如果在的话,可以给您同步下适合的岗位信息", ]; // 操作间隔时间(毫秒),建议设置长一点避免被系统检测 const clickInterval = 3000; // 点击下一个聊天对象的间隔 const messageSendInterval = 1000; // 发送消息的延迟 const scrollInterval = 800; // 滚动加载的间隔(增加等待时间) // == 辅助函数:自动查找正确的滚动容器 == function findScrollableContainer() { // 方案1: 直接尝试已知的容器选择器 const knownContainers = [ '.user-list', '.user-list.b-scroll-stable', '.chat-conversation-list', '.conversation-list' ]; for (const selector of knownContainers) { const container = document.querySelector(selector); if (container) { const scrollHeight = container.scrollHeight; const clientHeight = container.clientHeight; if (scrollHeight > clientHeight) { console.log(`✅ 找到滚动容器: ${selector}`); console.log(` scrollHeight: ${scrollHeight}px, clientHeight: ${clientHeight}px`); return container; } } } // 方案2: 从聊天项向上查找 const firstItem = document.querySelector('.geek-item'); if (!firstItem) { console.log("❌ 未找到聊天项元素"); return null; } console.log("✅ 找到聊天项: .geek-item"); let currentElement = firstItem; let level = 0; while (currentElement && currentElement !== document.body) { currentElement = currentElement.parentElement; level++; if (!currentElement) break; const styles = window.getComputedStyle(currentElement); const overflowY = styles.overflowY; const scrollHeight = currentElement.scrollHeight; const clientHeight = currentElement.clientHeight; const isScrollable = (overflowY === 'scroll' || overflowY === 'auto') && scrollHeight > clientHeight; if (isScrollable) { console.log(`✅ 找到滚动容器 (层级${level}): ${currentElement.className || currentElement.tagName}`); console.log(` scrollHeight: ${scrollHeight}px, clientHeight: ${clientHeight}px`); return currentElement; } } console.log("⚠️ 未找到可滚动容器"); return null; } // == 核心功能:虚拟列表模式(边滚动边处理)== async function startGreeting(customMessage = '', startIndex = 0) { if (DEBUG_MODE) { console.log("🐛 调试模式 - 只点击不发送\n"); } // 确定使用的消息内容 const greetingMessages = customMessage?.trim() ? [customMessage.trim()] : defaultGreetingMessages; // 自动查找滚动容器 const chatListContainer = findScrollableContainer(); if (!chatListContainer) { console.log("❌ 未找到聊天列表容器"); return; } // 使用 Map 记录每个元素的序号(按遇到的顺序) const itemIndexMap = new Map(); // itemId -> 序号 const processedItems = new Set(); let encounterOrder = 0; // 遇到元素的顺序计数 let actualProcessedCount = 0; // 实际处理的数量(不包括跳过的) let noNewItemsCount = 0; // 使用固定的卡片容器高度(.geek-item-wrap 固定78px,已包含所有边距) const CARD_HEIGHT = 78; console.log(`📏 使用固定卡片高度: ${CARD_HEIGHT}px`); console.log(`需要跳过前 ${startIndex} 个,从第 ${startIndex + 1} 个开始处理\n${"=".repeat(50)}`); // 改进的快速定位逻辑:先快速滚动收集元素序号 if (startIndex > 0) { console.log("⏳ 第一阶段:快速滚动收集元素序号..."); // 先回到顶部 chatListContainer.scrollTop = 0; await new Promise(resolve => setTimeout(resolve, 500)); let collectNoNewCount = 0; // 快速滚动收集元素,直到收集到 startIndex + 20 个元素 while (encounterOrder < startIndex + 20 && collectNoNewCount < 5) { const currentItems = Array.from(document.querySelectorAll('.geek-item')); let newInThisRound = 0; console.log(` 📋 当前DOM中有 ${currentItems.length} 个元素`); for (let item of currentItems) { const itemId = item.getAttribute('data-geek-id') || item.getAttribute('data-id') || item.querySelector('.name')?.textContent?.trim() || item.textContent.substring(0, 100).trim(); const itemName = item.querySelector('.name')?.textContent?.trim() || '未知'; if (!itemIndexMap.has(itemId)) { itemIndexMap.set(itemId, encounterOrder); console.log(` 🆕 序号 ${encounterOrder}: ${itemName.substring(0, 30)}`); encounterOrder++; newInThisRound++; } } if (newInThisRound === 0) { collectNoNewCount++; } else { collectNoNewCount = 0; } console.log(` ✅ 本轮新增 ${newInThisRound} 个,已收集 ${encounterOrder} 个元素序号\n`); // 如果已经收集够了,停止 if (encounterOrder >= startIndex + 10) { break; } // 继续滚动 chatListContainer.scrollTop += CARD_HEIGHT * 5; chatListContainer.dispatchEvent(new Event('scroll', { bubbles: true })); await new Promise(resolve => setTimeout(resolve, 300)); // 快速滚动 } console.log(`✅ 收集完成,共 ${encounterOrder} 个元素`); console.log(`⏳ 第二阶段:定位到起始位置...`); // 滚动到目标位置 const targetScrollTop = (startIndex - 2) * CARD_HEIGHT; chatListContainer.scrollTop = Math.max(0, targetScrollTop); chatListContainer.dispatchEvent(new Event('scroll', { bubbles: true })); await new Promise(resolve => setTimeout(resolve, 1000)); console.log(`✅ 定位完成,准备从第 ${startIndex + 1} 个开始处理`); } else { chatListContainer.scrollTop = 0; await new Promise(resolve => setTimeout(resolve, 1000)); } console.log(`\n${"=".repeat(50)}`); console.log(`🚀 开始处理阶段,当前 itemIndexMap 中有 ${itemIndexMap.size} 个元素`); console.log(` 需要跳过前 ${startIndex} 个,从第 ${startIndex + 1} 个开始处理`); console.log(`${"=".repeat(50)}\n`); while (noNewItemsCount < 8) { const currentItems = Array.from(document.querySelectorAll('.geek-item')); if (currentItems.length === 0) break; console.log(`\n📋 当前批次:DOM中有 ${currentItems.length} 个 .geek-item 元素`); let processedInThisBatch = 0; let newItemsInBatch = 0; for (let item of currentItems) { const itemId = item.getAttribute('data-geek-id') || item.getAttribute('data-id') || item.querySelector('.name')?.textContent?.trim() || item.textContent.substring(0, 100).trim(); const itemName = item.querySelector('.name')?.textContent?.trim() || '未知'; if (processedItems.has(itemId)) { console.log(` ⏩ 已处理过: ${itemName.substring(0, 20)}`); continue; } newItemsInBatch++; processedItems.add(itemId); // 🔥 关键:如果是新遇到的元素,给它分配序号 if (!itemIndexMap.has(itemId)) { itemIndexMap.set(itemId, encounterOrder); console.log(` 🆕 新元素分配序号 ${encounterOrder}: ${itemName.substring(0, 20)}`); encounterOrder++; } // 获取该元素的序号 const itemIndex = itemIndexMap.get(itemId); console.log(` 🔍 检查元素: ${itemName.substring(0, 20)} | 序号: ${itemIndex + 1} | startIndex: ${startIndex + 1}`); // 检查是否需要跳过(根据序号判断) if (itemIndex < startIndex) { console.log(` ⏭️ 跳过第 ${itemIndex + 1} 个: ${itemName.substring(0, 20)}`); continue; } try { actualProcessedCount++; processedInThisBatch++; console.log(`\n✅ [实际处理第${actualProcessedCount}个,总第${itemIndex + 1}个] ${itemName}`); item.scrollIntoView({ behavior: 'auto', block: 'center' }); await new Promise(resolve => setTimeout(resolve, 500)); item.click(); await new Promise(resolve => setTimeout(resolve, DEBUG_MODE ? 1000 : clickInterval)); const inputBox = document.querySelector('#boss-chat-editor-input'); if (!inputBox) { console.log(" ⚠️ 未找到输入框"); continue; } if (DEBUG_MODE) { console.log(" 🐛 调试模式跳过发送"); await new Promise(resolve => setTimeout(resolve, 500)); continue; } const message = greetingMessages[Math.floor(Math.random() * greetingMessages.length)]; inputBox.textContent = message; inputBox.dispatchEvent(new Event('input', { bubbles: true })); await new Promise(resolve => setTimeout(resolve, messageSendInterval)); const sendButton = document.querySelector('.submit'); if (sendButton) { sendButton.click(); console.log(` ✅ 已发送`); } await new Promise(resolve => setTimeout(resolve, clickInterval)); } catch (error) { console.error(` ❌ 出错: ${error.message}`); } } // 更新无新项计数 if (newItemsInBatch > 0) { noNewItemsCount = 0; } else { noNewItemsCount++; console.log(`⚠️ 连续 ${noNewItemsCount} 次无新项目`); } // 优化滚动:固定滚动3个卡片的高度 const scrollStep = CARD_HEIGHT * 3; // 每次滚动3个卡片高度 chatListContainer.scrollTop += scrollStep; chatListContainer.dispatchEvent(new Event('scroll', { bubbles: true })); await new Promise(resolve => setTimeout(resolve, scrollInterval)); } console.log(`\n${"=".repeat(50)}`); console.log(DEBUG_MODE ? "🐛 调试完成" : "🎉 处理完毕"); console.log(`📊 总遇到: ${encounterOrder} 个 | 实际处理: ${actualProcessedCount} 个 | 跳过: ${startIndex} 个`); console.log("=".repeat(50)); } // == 自动启动 == (async function() { console.log("=== Boss直聘自动打招呼脚本 V2.1 ==="); console.log(DEBUG_MODE ? "🐛 调试模式" : "🚀 正常模式"); console.log("========================================\n"); // 等待页面加载完成 console.log("⏳ 等待页面加载..."); let waitCount = 0; while (document.querySelectorAll('.geek-item').length === 0 && waitCount < 10) { await new Promise(resolve => setTimeout(resolve, 500)); waitCount++; } const itemCount = document.querySelectorAll('.geek-item').length; if (itemCount === 0) { console.log("❌ 页面未加载完成或不在聊天页面"); console.log("💡 请确保:"); console.log(" 1. 已登录Boss直聘招聘端"); console.log(" 2. 在聊天页面(能看到左侧聊天列表)"); console.log(" 3. 页面已完全加载"); return; } console.log(`✅ 检测到 ${itemCount} 个聊天对象\n`); const customMessage = prompt("请输入打招呼的消息(留空则使用默认):"); const startInput = prompt("从第几个人开始(留空则从第1个开始):\n注意:由于虚拟列表特性,会先滚动到大概位置再精确定位"); const startIndex = parseInt(startInput) > 0 ? parseInt(startInput) - 1 : 0; await new Promise(resolve => setTimeout(resolve, 500)); await startGreeting(customMessage, startIndex); })(); ```