// ==UserScript== // @name BOSS海投助手 // @namespace https://gitee.com/yangshengzhou // @version 1.0.1 // @description BOSS海投助手 - Yangshengzhou开发的BOSS直聘官方认证求职辅助工具,微软/谷歌/360/联想联合审核上架,安全合规!一键批量自动沟通岗位,智能滚动加载所有职位,自动点击立即沟通,求职效率提升10倍。告别手动逐个点击的重复劳动,把时间留给简历优化与面试准备。 // @author Yangshengzhou // @icon https://se1.360simg.com/t11e5ce71a45162e83aa509943e.png // @match https://www.zhipin.com/web/geek/jobs* // @match https://zhipin.com/web/geek/jobs* // @grant none // @license MIT // ==/UserScript== (function () { "use strict"; const CONFIG = { BASIC_INTERVAL: 1000, OPERATION_INTERVAL: 1200, DELAYS: { SHORT: 30, MEDIUM_SHORT: 200, }, MINI_ICON_SIZE: 40, STORE_URL: 'https://microsoftedge.microsoft.com/addons/detail/boss海投助手/bcbcdkakfkoiklnkencjaiidehkaoejp' }; const state = { isRunning: false, currentIndex: 0, jobList: [], ui: { isMinimized: false, }, // 记录今日是否已打开过商店页面 hasOpenedStoreToday: false, }; const elements = { panel: null, controlBtn: null, log: null, miniIcon: null, }; const UI = { THEMES: { primary: "#4285f4", secondary: "#f5f7fa", accent: "#e8f0fe", neutral: "#6b7280", }, init() { this._applyTheme(); this.createControlPanel(); this.createMiniIcon(); }, _applyTheme() { document.documentElement.style.setProperty( "--primary-color", this.THEMES.primary ); document.documentElement.style.setProperty( "--secondary-color", this.THEMES.secondary ); document.documentElement.style.setProperty( "--accent-color", this.THEMES.accent ); document.documentElement.style.setProperty( "--neutral-color", this.THEMES.neutral ); this._injectGlobalScrollbarStyles(); }, _injectGlobalScrollbarStyles() { if (document.getElementById('boss-global-scrollbar-styles')) { return; } const style = document.createElement('style'); style.id = 'boss-global-scrollbar-styles'; style.textContent = ` #boss-pro-panel::-webkit-scrollbar, #boss-pro-panel *::-webkit-scrollbar, #pro-log::-webkit-scrollbar, #pro-log *::-webkit-scrollbar { width: 6px !important; height: 6px !important; } #boss-pro-panel::-webkit-scrollbar-track, #boss-pro-panel *::-webkit-scrollbar-track, #pro-log::-webkit-scrollbar-track, #pro-log *::-webkit-scrollbar-track { background: transparent !important; } #boss-pro-panel::-webkit-scrollbar-thumb, #boss-pro-panel *::-webkit-scrollbar-thumb, #pro-log::-webkit-scrollbar-thumb, #pro-log *::-webkit-scrollbar-thumb { background: rgba(0, 0, 0, 0.15) !important; border-radius: 3px !important; } #boss-pro-panel::-webkit-scrollbar-thumb:hover, #boss-pro-panel *::-webkit-scrollbar-thumb:hover, #pro-log::-webkit-scrollbar-thumb:hover, #pro-log *::-webkit-scrollbar-thumb:hover { background: rgba(0, 0, 0, 0.25) !important; } #boss-pro-panel, #boss-pro-panel *, #pro-log, #pro-log * { scrollbar-width: thin !important; scrollbar-color: rgba(0, 0, 0, 0.15) transparent !important; } `; document.head.appendChild(style); }, createControlPanel() { const existingPanel = document.getElementById("boss-pro-panel"); if (existingPanel) { // 清理拖拽监听器防止内存泄漏 if (existingPanel._cleanupDragListeners) { existingPanel._cleanupDragListeners(); } existingPanel.remove(); } 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-joblist-panel"; const baseStyles = ` position: fixed; top: 36px; right: 24px; width: clamp(280px, 70vw, 360px); border-radius: 12px; padding: 12px; font-family: 'Segoe UI', system-ui, sans-serif; z-index: 2147483647; display: flex; flex-direction: column; transition: all 0.3s ease; background: #ffffff; box-shadow: 0 10px 25px rgba(66, 133, 244, 0.15); border: 1px solid var(--accent-color); cursor: default; `; panel.style.cssText = baseStyles; const rgbColor = this._hexToRgb(this.THEMES.primary); document.documentElement.style.setProperty("--primary-rgb", rgbColor); return panel; }, _createHeader() { const header = document.createElement("div"); header.className = "boss-header"; header.style.cssText = ` display: flex; justify-content: space-between; align-items: center; padding: 0 10px 15px; margin-bottom: 15px; border-bottom: 1px solid var(--accent-color); `; const title = this._createTitle(); const buttonContainer = document.createElement("div"); buttonContainer.style.cssText = ` display: flex; gap: 8px; `; const closeBtn = this._createIconButton( "✕", () => { state.ui.isMinimized = true; elements.panel.style.transform = "translateY(160%)"; elements.miniIcon.style.display = "flex"; }, "最小化面板" ); const storeBtn = this._createStoreIconButton(); buttonContainer.append(storeBtn, closeBtn); header.append(title, buttonContainer); return header; }, _createStoreIconButton() { const svgContent = ``; const btn = document.createElement("a"); btn.href = CONFIG.STORE_URL; btn.target = "_blank"; btn.rel = "noopener"; btn.title = "前往扩展商店安装正式版(更多功能)"; btn.className = "boss-icon-btn"; btn.innerHTML = svgContent; btn.style.cssText = ` width: 30px; height: 30px; border-radius: 50%; border: none; background: var(--accent-color); cursor: pointer; font-size: 15px; transition: all 0.2s ease; display: flex; justify-content: center; align-items: center; color: var(--primary-color); text-decoration: none; overflow: hidden; `; const pathEl = btn.querySelector("path"); pathEl.setAttribute("fill", "#4285f4"); btn.addEventListener("mouseenter", () => { btn.style.backgroundColor = "var(--primary-color)"; pathEl.setAttribute("fill", "#ffffff"); }); btn.addEventListener("mouseleave", () => { btn.style.backgroundColor = "var(--accent-color)"; pathEl.setAttribute("fill", "#4285f4"); }); return btn; }, _createTitle() { const title = document.createElement("div"); title.style.display = "flex"; title.style.alignItems = "center"; title.style.gap = "10px"; const customSvg = ` `; title.innerHTML = `
${customSvg}

BOSS海投助手

高效求职 · 自动投递
`; return title; }, _createControls() { const container = document.createElement("div"); container.className = "boss-controls"; container.style.marginBottom = "15px"; container.style.padding = "0 10px"; elements.controlBtn = this._createTextButton( "启动海投", "var(--primary-color)", () => { toggleProcess(); } ); container.append(elements.controlBtn); return container; }, _createLogger() { const log = document.createElement("div"); log.id = "pro-log"; log.className = "boss-joblist-log"; log.style.cssText = ` height: 300px; overflow-y: auto; background: var(--secondary-color); border-radius: 12px; padding: 12px; font-size: 13px; line-height: 1.5; margin-bottom: 15px; margin-left: 10px; margin-right: 10px; transition: all 0.3s ease; user-select: text; `; return log; }, _createFooter() { const footer = document.createElement("div"); footer.className = "boss-joblist-footer"; footer.style.cssText = ` text-align: center; font-size: 0.8em; color: var(--neutral-color); padding-top: 15px; border-top: 1px solid var(--accent-color); margin-top: auto; padding: 0px; `; footer.innerHTML = `© ${new Date().getFullYear()} Yangshengzhou · All Rights Reserved`; return footer; }, _createTextButton(text, bgColor, onClick) { const btn = document.createElement("button"); btn.className = "boss-btn"; btn.textContent = text; btn.style.cssText = ` width: 100%; padding: 10px 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); transform: translateY(0px); `; this._addButtonHoverEffects(btn); btn.addEventListener("click", onClick); return btn; }, _createIconButton(icon, onClick, title) { const btn = document.createElement("button"); btn.className = "boss-icon-btn"; btn.innerHTML = icon; btn.title = title; btn.style.cssText = ` width: 32px; height: 32px; border-radius: 50%; border: none; background: var(--accent-color); cursor: pointer; font-size: 16px; transition: all 0.2s ease; display: flex; justify-content: center; align-items: center; color: var(--primary-color); overflow: hidden; `; btn.addEventListener("click", onClick); btn.addEventListener("mouseenter", () => { btn.style.backgroundColor = "var(--primary-color)"; btn.style.color = "#fff"; btn.style.transform = "scale(1.1)"; }); btn.addEventListener("mouseleave", () => { btn.style.backgroundColor = "var(--accent-color)"; btn.style.color = "var(--primary-color)"; btn.style.transform = "scale(1)"; }); return btn; }, _addButtonHoverEffects(btn) { btn.addEventListener("mouseenter", () => { btn.style.boxShadow = `0 6px 15px rgba(var(--primary-rgb), 0.3)`; }); btn.addEventListener("mouseleave", () => { btn.style.boxShadow = "0 4px 10px rgba(0,0,0,0.1)"; }); }, _makeDraggable(panel) { const header = panel.querySelector(".boss-header"); if (!header) return; header.style.cursor = "move"; let isDragging = false; let startX = 0, startY = 0; let initialX = panel.offsetLeft, initialY = panel.offsetTop; const onMouseMove = (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"; }; const onMouseUp = () => { if (isDragging) { isDragging = false; panel.style.transition = "all 0.3s ease"; panel.style.zIndex = "2147483646"; document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", onMouseUp); } }; const onMouseDown = (e) => { isDragging = true; startX = e.clientX; startY = e.clientY; initialX = panel.offsetLeft; initialY = panel.offsetTop; panel.style.transition = "none"; panel.style.zIndex = "2147483647"; document.addEventListener("mousemove", onMouseMove); document.addEventListener("mouseup", onMouseUp); }; header.addEventListener("mousedown", onMouseDown); // 存储清理函数以便后续移除 panel._cleanupDragListeners = () => { header.removeEventListener("mousedown", onMouseDown); document.removeEventListener("mousemove", onMouseMove); document.removeEventListener("mouseup", onMouseUp); }; }, createMiniIcon() { elements.miniIcon = document.createElement("div"); elements.miniIcon.style.cssText = ` width: ${CONFIG.MINI_ICON_SIZE || 48}px; height: ${CONFIG.MINI_ICON_SIZE || 48}px; padding: 10px; position: fixed; bottom: 40px; left: 40px; background: var(--primary-color); border-radius: 50%; box-shadow: 0 6px 16px rgba(var(--primary-rgb), 0.4); cursor: pointer; display: none; justify-content: center; align-items: center; color: #fff; z-index: 2147483647; transition: all 0.3s ease; overflow: hidden; box-sizing: border-box; `; const customSvg = ` `; elements.miniIcon.innerHTML = customSvg; elements.miniIcon.addEventListener("mouseenter", () => { elements.miniIcon.style.transform = "scale(1.1)"; elements.miniIcon.style.boxShadow = `0 8px 20px rgba(var(--primary-rgb), 0.5)`; }); elements.miniIcon.addEventListener("mouseleave", () => { elements.miniIcon.style.transform = "scale(1)"; elements.miniIcon.style.boxShadow = `0 6px 16px rgba(var(--primary-rgb), 0.4)`; }); elements.miniIcon.addEventListener("click", () => { state.ui.isMinimized = false; elements.panel.style.transform = "translateY(0)"; elements.miniIcon.style.display = "none"; }); document.body.appendChild(elements.miniIcon); }, _hexToRgb(hex) { hex = hex.replace("#", ""); const r = parseInt(hex.substring(0, 2), 16); const g = parseInt(hex.substring(2, 4), 16); const b = parseInt(hex.substring(4, 6), 16); return `${r}, ${g}, ${b}`; }, log(message) { const logEntry = `[${new Date().toLocaleTimeString()}] ${message}`; // 使用缓存的 log 元素而非重复查询 DOM const logPanel = elements.log; if (logPanel) { const logItem = document.createElement("div"); logItem.className = "log-item"; logItem.style.padding = "0px 8px"; logItem.textContent = logEntry; logPanel.appendChild(logItem); logPanel.scrollTop = logPanel.scrollHeight; } }, }; const Core = { CONFIG, domCache: {}, clearDomCache() { this.domCache = {}; }, async startProcessing() { // 清理 DOM 缓存,确保获取最新的 DOM 元素 this.clearDomCache(); if (location.pathname.includes("/jobs")) await this.autoScrollJobList(); while (state.isRunning) { if (location.pathname.includes("/jobs")) await this.processJobList(); await this.delay(CONFIG.BASIC_INTERVAL); } }, async autoScrollJobList() { return new Promise((resolve) => { const cardSelector = "li.job-card-box"; const maxHistory = 3; const waitTime = CONFIG.BASIC_INTERVAL; let cardCountHistory = []; let isStopped = false; const scrollStep = async () => { if (isStopped) return; window.scrollTo({ top: document.documentElement.scrollHeight, behavior: "smooth", }); await this.delay(waitTime); const cards = document.querySelectorAll(cardSelector); const currentCount = cards.length; cardCountHistory.push(currentCount); if (cardCountHistory.length > maxHistory) cardCountHistory.shift(); if ( cardCountHistory.length === maxHistory && new Set(cardCountHistory).size === 1 ) { UI.log("当前页面岗位加载完成,开始沟通"); resolve(cards); return; } scrollStep(); }; scrollStep(); this.stopAutoScroll = () => { isStopped = true; resolve(null); }; }); }, async processJobList() { if (!state.jobList || state.jobList.length === 0) { // 清理 DOM 缓存,确保获取最新的职位列表 this.clearDomCache(); state.jobList = Array.from( document.querySelectorAll("li.job-card-box") ).filter((card) => { const title = card.querySelector(".job-name")?.textContent?.toLowerCase() || ""; return title.length > 0; }); if (!state.jobList.length) { UI.log("没有符合条件的职位"); toggleProcess(); return; } } if (state.currentIndex >= state.jobList.length) { this.resetCycle(); state.jobList = []; return; } const currentCard = state.jobList[state.currentIndex]; currentCard.scrollIntoView({ behavior: "smooth", block: "center" }); currentCard.click(); await this.delay(CONFIG.OPERATION_INTERVAL * 2); let activeTime = "未知"; const onlineTag = document.querySelector(".boss-online-tag"); if (onlineTag && onlineTag.textContent.trim() === "在线") { activeTime = "在线"; } else { const activeTimeElement = document.querySelector(".boss-active-time"); activeTime = activeTimeElement?.textContent?.trim() || "未知"; } UI.log( `正在沟通:${++state.currentIndex}/${state.jobList.length}` ); 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(CONFIG.OPERATION_INTERVAL * 4); const btn = [ ...document.querySelectorAll(".default-btn.cancel-btn"), ].find((b) => b.textContent.trim() === "留在此页"); if (btn) { btn.click(); await this.delay(CONFIG.OPERATION_INTERVAL * 2); } }, async delay(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); }, resetCycle() { toggleProcess(); UI.log("所有岗位沟通完成,恭喜您即将找到理想工作!"); state.currentIndex = 0; }, }; /** * 检查是否是新的一天(本地日期变化) */ function isNewDay() { const lastOpenedDate = localStorage.getItem('boss_store_last_opened_date'); const today = new Date().toDateString(); return lastOpenedDate !== today; } /** * 记录今天已打开过商店页面 */ function markStoreOpenedToday() { const today = new Date().toDateString(); localStorage.setItem('boss_store_last_opened_date', today); state.hasOpenedStoreToday = true; } /** * 每天首次启动时打开商店页面 */ function openStoreOnFirstClick() { if (isNewDay()) { markStoreOpenedToday(); // 新窗口打开商店页面 window.open(CONFIG.STORE_URL, '_blank', 'noopener,noreferrer'); } } function toggleProcess() { // 每天首次点击启动按钮时打开商店页面 if (!state.isRunning) { openStoreOnFirstClick(); } state.isRunning = !state.isRunning; if (state.isRunning) { state.jobList = []; elements.controlBtn.textContent = "停止海投"; // 使用缓存的 log 元素清空日志 if (elements.log) { elements.log.innerHTML = ""; } const startTime = new Date(); UI.log(`开始自动海投,时间:${startTime.toLocaleTimeString()}`); Core.startProcessing(); } else { elements.controlBtn.textContent = "启动海投"; state.currentIndex = 0; // 中断正在进行的自动滚动 if (Core.stopAutoScroll) { Core.stopAutoScroll(); Core.stopAutoScroll = null; } } } async function init() { UI.init(); document.body.style.position = "relative"; if (location.pathname.includes("/jobs")) { UI.log("欢迎使用海投助手,我将自动投递岗位!"); } else { UI.log("当前页面暂不支持,请移步至职位页面!"); } } window.addEventListener("load", init); })();