// ==UserScript== // @name BOSS海投助手-专业版 (BOSS Helper - Professional Edition) // @namespace BOSS_helper // @version 1.0.1 // @description 求职者自己的自动化工具,由Yangshengzhou开发,提升在BOSS上的简历投递效率,自动投递,简化繁琐的手动操作。 // @author Yangshengzhou // @match https://www.zhipin.com/* // @grant none // @run-at document-idle // @supportURL https://github.com/yangshengzhou03 // @homepageURL https://gitee.com/yangshengzhou // @license MIT // @icon https://static.zhipin.com/favicon.ico // @connect zhipin.com // @noframes // @downloadURL none // ==/UserScript== (function () { 'use strict'; // 配置项 const CONFIG = { INTERVAL: 2500, // 自动化操作的时间间隔(毫秒) CARD_STYLE: { BACKGROUND: '#ffffff', // 面板背景颜色 SHADOW: '0 6px 18px rgba(0,0,0,0.12)', // 面板阴影效果 BORDER: '1px solid #e4e7ed' // 面板边框样式 }, COLORS: { PRIMARY: '#2196f3', // 主色调(蓝色) SECONDARY: '#ff5722', // 次要色调(橙色) NEUTRAL: '#95a5a6' // 中性色(灰色) }, MINI_ICON_SIZE: 40 // 悬浮球图标大小(像素) }; // 状态管理 const state = { isRunning: false, // 是否正在运行自动化流程 currentIndex: 0, // 当前处理的岗位索引 filterKeyword: '', // 用户输入的过滤关键词 jobList: [], // 收集到的所有岗位数据 isMinimized: false // 是否处于最小化状态 }; // DOM元素引用 const elements = { panel: null, // 控制面板元素 controlBtn: null, // 启动/暂停按钮 log: null, // 日志输出区域 filterInput: null, // 过滤关键词输入框 miniIcon: null // 最小化后的悬浮图标 }; const UI = { createControlPanel() { if (document.getElementById('boss-pro-panel')) return; elements.panel = this._createPanel(); const header = this._createHeader(); const controls = this._createControls(); elements.log = this._createLogger(); const footer = this._createFooter(); elements.panel.append(header, controls, elements.log, footer); document.body.appendChild(elements.panel); this._makeDraggable(elements.panel); }, _createPanel() { const panel = document.createElement('div'); panel.id = 'boss-pro-panel'; panel.className = 'boss-pro-panel'; panel.style.cssText = ` position: fixed; top: 36px; right: 24px; width: 380px; background: linear-gradient(145deg, #ffffff, #f9f9fc); border-radius: 16px; box-shadow: 0 10px 25px rgba(0,0,0,0.1); padding: 18px; font-family: 'Segoe UI', system-ui, sans-serif; z-index: 2147483647; cursor: move; display: flex; flex-direction: column; transition: all 0.3s ease; `; return panel; }, _createHeader() { const header = document.createElement('div'); header.className = 'boss-header'; header.style.cssText = ` display: flex; justify-content: space-between; align-items: center; margin-bottom: 1.5rem; `; const title = document.createElement('div'); title.innerHTML = `

BOSS海投助手

v1.0-Beta (Professional) `; const closeBtn = this._createIconButton('✕', () => { state.isMinimized = true; elements.panel.style.transform = 'translateY(160%)'; elements.miniIcon.style.display = 'flex'; }); closeBtn.style.color = CONFIG.COLORS.NEUTRAL; header.append(title, closeBtn); return header; }, _createControls() { const container = document.createElement('div'); container.className = 'boss-controls'; container.style.marginBottom = '0.8rem'; // 岗位标签和输入框 const jobLabel = document.createElement('label'); jobLabel.textContent = '投递岗位筛选:'; jobLabel.style.cssText = 'display:block; margin-bottom:0.5rem;'; elements.filterInput = document.createElement('input'); elements.filterInput.id = 'job-filter'; elements.filterInput.placeholder = '职位关键词(逗号分隔,为空则不限制)'; elements.filterInput.className = 'boss-filter-input'; elements.filterInput.style.cssText = ` width: calc(100%); padding: 12px 16px; border-radius: 10px; border: 2px solid #ddd; margin-bottom: 1rem; font-size: 14px; transition: border-color 0.3s ease; outline: none; `; elements.filterInput.addEventListener('focus', () => { elements.filterInput.style.borderColor = CONFIG.COLORS.PRIMARY; }); elements.filterInput.addEventListener('blur', () => { elements.filterInput.style.borderColor = '#ddd'; }); // 地区标签和输入框 const locationLabel = document.createElement('label'); locationLabel.textContent = '地区:'; locationLabel.style.cssText = 'display:block; margin-bottom:0.5rem;'; elements.locationInput = document.createElement('input'); elements.locationInput.id = 'location-filter'; elements.locationInput.placeholder = '岗位地区(为空则不限制)'; elements.locationInput.className = 'boss-filter-input'; elements.locationInput.style.cssText = ` width: calc(100%); padding: 12px 16px; border-radius: 10px; border: 2px solid #ddd; margin-bottom: 1rem; font-size: 14px; transition: border-color 0.3s ease; outline: none; `; elements.locationInput.addEventListener('focus', () => { elements.locationInput.style.borderColor = CONFIG.COLORS.PRIMARY; }); elements.locationInput.addEventListener('blur', () => { elements.locationInput.style.borderColor = '#ddd'; }); // 控制按钮 elements.controlBtn = this._createTextButton('启动海投', `linear-gradient(45deg, ${CONFIG.COLORS.PRIMARY}, #4db6ac)`, () => { toggleProcess(); }); // 工具按钮组 const utilGroup = document.createElement('div'); utilGroup.className = 'boss-util-group'; utilGroup.style.cssText = ` display: flex; gap: 10px; justify-content: flex-end; margin-top: 1rem; `; const clearLogBtn = this._createIconButton('🗑', () => { elements.log.innerHTML = `
海投助手,工作我有
`; }); const settingsBtn = this._createIconButton('⚙', () => { alert('Yangshengzhou劝你不要乱点'); }); container.append(jobLabel, elements.filterInput, locationLabel, elements.locationInput, elements.controlBtn, utilGroup); utilGroup.append(clearLogBtn, settingsBtn); return container; }, _createLogger() { const log = document.createElement('div'); log.id = 'pro-log'; log.className = 'boss-log'; log.style.cssText = ` height: 200px; overflow-y: auto; background: #f8f9fa; border-radius: 10px; padding: 12px; border: 1px solid #eceff1; font-size: 13px; line-height: 1.5; margin-bottom: 1rem; transition: all 0.3s ease; user-select: text; `; log.innerHTML = `
欢迎使用海投助手,智慧海投,祝您一切顺利~
`; return log; }, _createFooter() { const footer = document.createElement('div'); footer.textContent = '© 2025 Yangshengzhou · All Rights Reserved'; footer.style.cssText = ` text-align: center; font-size: 0.8em; color: ${CONFIG.COLORS.NEUTRAL}; padding-top: 10px; border-top: 1px solid #eee; margin-top: auto; transition: color 0.3s ease; `; return footer; }, _createTextButton(text, bgColor, onClick) { const btn = document.createElement('button'); btn.className = 'boss-btn'; btn.textContent = text; btn.style.cssText = ` width: 100%; padding: 12px 16px; background: ${bgColor}; color: #fff; border: none; border-radius: 10px; cursor: pointer; font-size: 15px; font-weight: 500; transition: all 0.3s ease; display: flex; justify-content: center; align-items: center; box-shadow: 0 4px 10px rgba(0,0,0,0.1); `; btn.addEventListener('click', onClick); btn.addEventListener('mouseenter', () => { btn.style.transform = 'scale(1.03)'; btn.style.boxShadow = '0 6px 15px rgba(0,0,0,0.15)'; }); btn.addEventListener('mouseleave', () => { btn.style.transform = 'scale(1)'; btn.style.boxShadow = '0 4px 10px rgba(0,0,0,0.1)'; }); return btn; }, _createIconButton(icon, onClick) { const btn = document.createElement('button'); btn.className = 'boss-icon-btn'; btn.innerHTML = icon; btn.style.cssText = ` width: 36px; height: 36px; border-radius: 50%; border: none; background: #f0f0f0; cursor: pointer; font-size: 18px; transition: all 0.2s ease; display: flex; justify-content: center; align-items: center; color: ${CONFIG.COLORS.PRIMARY}; transform: translateY(8px); /* 视觉微调:向下移动8px以补偿视觉中心偏上的错觉 */ `; btn.addEventListener('click', onClick); btn.addEventListener('mouseenter', () => { btn.style.backgroundColor = CONFIG.COLORS.PRIMARY; btn.style.color = '#fff'; btn.style.transform = 'translateY(8px) scale(1.1)'; }); btn.addEventListener('mouseleave', () => { btn.style.backgroundColor = '#f0f0f0'; btn.style.color = CONFIG.COLORS.PRIMARY; btn.style.transform = 'translateY(8px)'; }); return btn; }, _makeDraggable(panel) { const draggableAreaHeightRatio = 0.3; // 可拖动区域占面板高度的30% panel.addEventListener('mousemove', (e) => { const rect = panel.getBoundingClientRect(); const relativeY = e.clientY - rect.top; const draggableHeight = rect.height * draggableAreaHeightRatio; if (relativeY <= draggableHeight) { panel.style.cursor = 'move'; } else { panel.style.cursor = 'default'; } }); let isDragging = false; let startX = 0, startY = 0; let initialX = panel.offsetLeft, initialY = panel.offsetTop; panel.addEventListener('mousedown', (e) => { const rect = panel.getBoundingClientRect(); const relativeY = e.clientY - rect.top; const draggableHeight = rect.height * draggableAreaHeightRatio; if (relativeY <= draggableHeight) { isDragging = true; startX = e.clientX; startY = e.clientY; initialX = panel.offsetLeft; initialY = panel.offsetTop; panel.style.transition = 'none'; } }); document.addEventListener('mousemove', (e) => { if (!isDragging) return; const dx = e.clientX - startX; const dy = e.clientY - startY; panel.style.left = `${initialX + dx}px`; panel.style.top = `${initialY + dy}px`; panel.style.right = 'auto'; }); document.addEventListener('mouseup', () => { if (isDragging) { isDragging = false; panel.style.transition = 'all 0.3s ease'; } }); }, createMiniIcon() { elements.miniIcon = document.createElement('div'); elements.miniIcon.style.cssText = ` width: ${CONFIG.MINI_ICON_SIZE}px; height: ${CONFIG.MINI_ICON_SIZE}px; position: fixed; bottom: 40px; left: 40px; background: linear-gradient(135deg, ${CONFIG.COLORS.PRIMARY}, #4db6ac); border-radius: 50%; box-shadow: 0 6px 16px rgba(33, 150, 243, 0.4); cursor: pointer; display: none; justify-content: center; align-items: center; color: #fff; font-size: 18px; z-index: 2147483647; transition: all 0.3s ease; overflow: hidden; text-align: center; line-height: 1; `; elements.miniIcon.innerHTML = '↗'; elements.miniIcon.addEventListener('mouseenter', () => { elements.miniIcon.style.transform = 'scale(1.1)'; elements.miniIcon.style.boxShadow = '0 8px 20px rgba(33, 150, 243, 0.5)'; }); elements.miniIcon.addEventListener('mouseleave', () => { elements.miniIcon.style.transform = 'scale(1)'; elements.miniIcon.style.boxShadow = '0 6px 16px rgba(33, 150, 243, 0.4)'; }); elements.miniIcon.addEventListener('click', () => { state.isMinimized = false; elements.panel.style.transform = 'translateY(0)'; elements.miniIcon.style.display = 'none'; }); document.body.appendChild(elements.miniIcon); } }; // 核心代码 const Core = { async startProcessing() { while (state.isRunning) { if (location.pathname.includes('/jobs')) { await this.processJobList(); window.open('https://www.zhipin.com/web/geek/chat', '_blank'); } else if (location.pathname.includes('/chat')) { await this.handleChatPage(); } await this.delay(CONFIG.INTERVAL); } }, // 点击Job页面的立即沟通按钮 async processJobList() { state.jobList = Array.from(document.querySelectorAll('li.job-card-box')) .filter(card => { const title = card.querySelector('.job-name')?.textContent?.toLowerCase() || ''; return state.filterKeyword ? state.filterKeyword.split(',').some(kw => title.includes(kw.trim())) : true; }); if (!state.jobList.length) { this.log('未找到符合条件的职位'); toggleProcess(); return; } if (state.currentIndex >= state.jobList.length) { this.resetCycle(); return; } const currentCard = state.jobList[state.currentIndex]; currentCard.scrollIntoView({ behavior: 'smooth', block: 'center' }); currentCard.click(); this.log(`处理:${++state.currentIndex}/${state.jobList.length}`); await this.delay(600); const chatBtn = document.querySelector('a.op-btn-chat'); if (chatBtn) { const btnText = chatBtn.textContent.trim(); if (btnText === '立即沟通') { chatBtn.click(); await this.handleGreetingModal(); } } }, async handleGreetingModal() { await this.delay(800); const btn = [...document.querySelectorAll('.default-btn.cancel-btn')].find(b => b.textContent.trim() === '留在此页'); if (btn) { btn.click(); await this.delay(500); } }, async handleChatPage() { const chatList = await this.waitForElement('ul'); if (!chatList) { this.log('未找到聊天列表'); return; } const observer = new MutationObserver(async () => { await this.clickLatestChat(); }); observer.observe(chatList, { childList: true }); await this.clickLatestChat(); }, getLatestChatLi() { return document.querySelector('li[role="listitem"][class]:has(.friend-content-warp)'); }, async clickLatestChat() { try { const latestLi = await this.waitForElement(this.getLatestChatLi); if (!latestLi) return; if (latestLi.classList.contains('last-clicked')) return; // 获取聊天人姓名和公司信息 const nameEl = latestLi.querySelector('.name-text'); const companyEl = latestLi.querySelector('.name-box span:nth-child(2)'); const name = nameEl ? nameEl.textContent : '未知'; const company = companyEl ? companyEl.textContent : ''; this.log(`介绍并发送简历: ${name}${company ? ' - ' + company : ''}`); const avatar = latestLi.querySelector('.figure'); await this.simulateClick(avatar); latestLi.classList.add('last-clicked'); await this.processChatContent(); } catch (error) { this.log(`自我介绍时出错: ${error.message}`); } }, async processChatContent() { try { await this.delay(500); const dictBtn = await this.waitForElement('.btn-dict'); if (!dictBtn) { this.log('未找到常用语按钮'); return; } await this.simulateClick(dictBtn); await this.delay(500); const dictList = await this.waitForElement('ul[data-v-8e790d94=""]'); if (!dictList) { this.log('未找到常用语列表'); return; } const dictItems = dictList.querySelectorAll('li'); if (!dictItems || dictItems.length === 0) { this.log('常用语列表为空'); return; } for (let i = 0; i < dictItems.length; i++) { const item = dictItems[i]; this.log(`发送常用语 ${i + 1}/${dictItems.length}`); await this.simulateClick(item); await this.delay(600); } const resumeBtn = await this.waitForElement('div[d-c="62009"]'); if (!resumeBtn) { this.log('无法发送简历'); return; } if (resumeBtn.classList.contains('unable')) { this.log('对方未回复,您无权发送简历'); return; } await this.simulateClick(resumeBtn); await this.delay(500); const confirmBtn = await this.waitForElement('span.btn-sure-v2'); if (!confirmBtn) { return; } await this.simulateClick(confirmBtn); } catch (error) { this.log(`处理出错: ${error.message}`); } }, async simulateClick(element) { if (!element) return; const rect = element.getBoundingClientRect(); const x = rect.left + rect.width / 2; const y = rect.top + rect.height / 2; const dispatchMouseEvent = (type, options) => { const event = new MouseEvent(type, { bubbles: true, cancelable: true, view: window, clientX: x, clientY: y, ...options }); element.dispatchEvent(event); }; dispatchMouseEvent('mouseover'); await this.delay(50); dispatchMouseEvent('mousemove'); await this.delay(50); dispatchMouseEvent('mousedown', { button: 0 }); await this.delay(50); dispatchMouseEvent('mouseup', { button: 0 }); await this.delay(50); dispatchMouseEvent('click', { button: 0 }); }, async waitForElement(selectorOrFunction, timeout = 5000) { return new Promise((resolve) => { if (typeof selectorOrFunction === 'function') { const element = selectorOrFunction(); if (element) return resolve(element); } else { const element = document.querySelector(selectorOrFunction); if (element) return resolve(element); } // 设置超时 const timeoutId = setTimeout(() => { observer.disconnect(); resolve(null); }, timeout); const observer = new MutationObserver(() => { let element; if (typeof selectorOrFunction === 'function') { element = selectorOrFunction(); } else { element = document.querySelector(selectorOrFunction); } if (element) { clearTimeout(timeoutId); observer.disconnect(); resolve(element); } }); observer.observe(document.body, { childList: true, subtree: true }); }); }, async delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }, resetCycle() { this.log('试用已结束,请开通会员'); state.currentIndex = 0; state.lastMessageTime = 0; }, log(message) { const logEntry = `[${new Date().toLocaleTimeString()}] ${message}`; console.log(logEntry); const logPanel = document.querySelector('#pro-log'); if (logPanel) { const logItem = document.createElement('div'); logItem.className = 'log-item'; logItem.textContent = logEntry; logPanel.appendChild(logItem); logPanel.scrollTop = logPanel.scrollHeight; } } }; function toggleProcess() { state.isRunning = !state.isRunning; if (state.isRunning) { state.filterKeyword = elements.filterInput.value.trim().toLowerCase(); elements.controlBtn.textContent = '停止海投'; elements.controlBtn.style.backgroundColor = CONFIG.COLORS.SECONDARY; Core.startProcessing(); } else { elements.controlBtn.textContent = '启动海投'; elements.controlBtn.style.backgroundColor = CONFIG.COLORS.PRIMARY; Core.state.isRunning = false; } } function init() { UI.createControlPanel(); UI.createMiniIcon(); document.body.style.position = 'relative'; if (location.pathname.includes('/jobs')) { window.open('https://www.zhipin.com/web/geek/chat', '_blank'); } else if (location.pathname.includes('/chat')) { toggleProcess(); } } window.addEventListener('load', init); })();