// ==UserScript== // @name BOSS海投助手 // @namespace https://github.com/yangshengzhou03 // @version 1.2.2 // @description 🚀 求职者自己的神器!🧑‍💻Yangshengzhou,提升BOSS直聘的简历投递效率,自动批量发送简历,高效求职 💼 // @author Yangshengzhou // @match https://www.zhipin.com/web/* // @grant GM_xmlhttpRequest // @run-at document-idle // @supportURL https://github.com/yangshengzhou03 // @homepageURL https://gitee.com/yangshengzhou // @license AGPL-3.0-or-later // 更多详情请参见: https://www.gnu.org/licenses/agpl-3.0.html // @icon https://static.zhipin.com/favicon.ico // @connect zhipin.com // @connect spark-api-open.xf-yun.com // @noframes // @require https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.1.1/crypto-js.min.js // @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: '', locationKeyword: '', jobList: [], isMinimized: false, processedHRs: new Set(JSON.parse(localStorage.getItem('processedHRs') || '[]')), currentTopHRKey: null, aiReplyCount: JSON.parse(localStorage.getItem('aiReplyCount') || '0'), lastAiDate: localStorage.getItem('lastAiDate') || '' }; // DOM 元素引用 const elements = { panel: null, controlBtn: null, log: null, filterInput: null, miniIcon: null }; // UI 模块:负责创建和操作界面组件 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); if (!document.getElementById('boss-settings-dialog')) { const settingsDialog = this._createSettingsDialog(); document.body.appendChild(settingsDialog); } }, // 创建面板容器 _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; margin-bottom: 1.5rem; `; const title = document.createElement('div'); title.style.display = 'flex'; title.style.flexDirection = 'column'; title.style.gap = '0.3em'; title.innerHTML = `

BOSS海投助手

v1.2-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; closeBtn.style.marginTop = '0.2rem'; closeBtn.title = '最小化海投面板'; header.append(title, closeBtn); return header; }, // 创建控制区域(按钮 + 输入框) _createControls() { const container = document.createElement('div'); container.className = 'boss-controls'; container.style.marginBottom = '0.8rem'; 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 = `
请在BOSS中展示岗位列表,我将自动与这些岗位沟通。
`; }); clearLogBtn.title = '清空日志'; const settingsBtn = this._createIconButton('⚙', () => { const dialog = document.getElementById('boss-settings-dialog'); if (dialog) { dialog.style.display = 'flex'; } else { const newDialog = this._createSettingsDialog(); document.body.appendChild(newDialog); newDialog.style.display = 'flex'; } }); settingsBtn.title = '插件设置'; const joinGroupBtn = this._createIconButton('✉', () => { window.open('https://qun.qq.com/universal-share/share?ac=1&authKey=twqkV%2Fu9zC7%2FrSMJOOxD%2FbM4rXDqkE2zWg%2FmR6DdVI9BJImtoBFqvNnE9F3KyW3P&busi_data=eyJncm91cENvZGUiOiIxMDQ3ODQyNDczIiwidG9rZW4iOiJBTGF3eEVwLzYvRCtHRkR2WnVDYUVFTDdJQWY0cGVDODl4amlkTkVaL2R4bTU1WE9CSTUzSzRYTkhvdW9DTGVKIiwidWluIjoiMzU1NTg0NDY3OSJ9&data=WOu9pw6Y-e2H4ebyGMMgxV75yzExdNk2LrecdfSDL1RVZ9TbUOvXDVzo3qIShDAzhK5PjDYx5o-uXH-GdA0FfQ&svctype=4&tempid=h5_group_info', '_blank'); }); joinGroupBtn.title = '加入粉丝交流群'; utilGroup.append(clearLogBtn, settingsBtn, joinGroupBtn); 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(); } ); container.append( jobLabel, elements.filterInput, locationLabel, elements.locationInput, elements.controlBtn, utilGroup ); 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 = `
请在BOSS中展示岗位列表,我将自动与这些岗位沟通。
`; 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); `; 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; 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); }, _createSettingsDialog() { const dialog = document.createElement('div'); dialog.id = 'boss-settings-dialog'; dialog.style.cssText = ` position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); width: 420px; background: #fff; border-radius: 12px; box-shadow: 0 10px 30px rgba(0,0,0,0.15); padding: 20px; z-index: 999999; display: none; flex-direction: column; gap: 12px; font-family: 'Segoe UI', sans-serif; `; // 标题 const title = document.createElement('h4'); title.textContent = '海投设置'; title.style.marginTop = 0; title.style.color = CONFIG.COLORS.PRIMARY; // AI 人设输入框 const roleLabel = document.createElement('label'); roleLabel.textContent = 'AI 人设(如:“你是一位正在求职的资深全栈工程师”):'; const roleInput = document.createElement('textarea'); roleInput.id = 'ai-role-input'; roleInput.rows = 4; roleInput.value = localStorage.getItem('aiRole') || '你是有工作经验的求职者,擅长交流。你会用一些口语化的表达(如“嗯”、“呃”)和语气词(如“啊”、“呢”),使对话听起来自然。遇到不清楚的问题,你会请求对方解释,确保准确理解。回复简洁明了,避免冗长复杂的句子结构。'; roleInput.style.cssText = ` width: 100%; padding: 10px; border-radius: 8px; border: 1px solid #ccc; resize: vertical; font-size: 13px; margin-bottom: 10px; `; // 保存按钮 const saveBtn = this._createTextButton('保存设置', `linear-gradient(45deg, ${CONFIG.COLORS.PRIMARY}, #4db6ac)`, () => { const aiRole = roleInput.value.trim(); if (aiRole) { localStorage.setItem('aiRole', aiRole); alert('设置保存成功!'); dialog.style.display = 'none'; } }); // 取消按钮 const cancelBtn = this._createTextButton('取消', '#ccc', () => { dialog.style.display = 'none'; }); cancelBtn.style.marginLeft = '10px'; const btnGroup = document.createElement('div'); btnGroup.style.cssText = ` display: flex; justify-content: flex-end; gap: 10px; margin-top: 10px; `; btnGroup.append(saveBtn, cancelBtn); // 前往 Gitee 按钮 const giteeBtn = this._createTextButton('前往 Gitee 看源码', '#68130d', () => { window.open('https://gitee.com/Yangshengzhou', '_blank'); }); giteeBtn.style.width = '100%'; giteeBtn.style.marginTop = '15px'; // 插入元素 dialog.append(title, roleLabel, roleInput, btnGroup, giteeBtn); return dialog; } }; const Core = { async startProcessing() { if (location.pathname.includes('/jobs')) await this.autoScrollJobList(); while (state.isRunning) { if (location.pathname.includes('/jobs')) await this.processJobList(); else if (location.pathname.includes('/chat')) await this.handleChatPage(); await this.delay(CONFIG.INTERVAL); } }, async autoScrollJobList() { return new Promise((resolve) => { const cardSelector = 'li.job-card-box'; // 岗位卡片的选择器 const maxHistory = 3; // 最大历史记录数,用于判断是否加载完成 const waitTime = 550; // 等待时间(ms),每次滚动后等待的时间以便新内容加载 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(); // 维护历史记录长度 // 判断如果最近maxHistory次记录中的值都相同(即没有新岗位卡片加载进来) if (cardCountHistory.length === maxHistory && new Set(cardCountHistory).size === 1) { this.log("当前页面岗位加载完成,开始沟通"); resolve(cards); // 完成Promise,返回所有岗位卡片 return; } scrollStep(); // 递归调用自身,继续滚动 }; scrollStep(); // 开始首次滚动 // 提供一个方法来从外部停止滚动 this.stopAutoScroll = () => { isStopped = true; resolve(null); }; }); }, // 点击Job页面的立即沟通按钮 async processJobList() { state.jobList = Array.from(document.querySelectorAll('li.job-card-box')) .filter(card => { const title = card.querySelector('.job-name')?.textContent?.toLowerCase() || ''; const location = card.querySelector('.company-location')?.textContent?.toLowerCase().trim() || ''; // 岗位名称匹配 const jobMatch = state.filterKeyword ? state.filterKeyword.split(',').some(kw => title.includes(kw.trim())) : true; // 地区匹配(模糊包含) const locationMatch = state.locationKeyword ? state.locationKeyword.split(',').some(kw => location.includes(kw.trim())) : true; return jobMatch && locationMatch; }); 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(250); 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(200); const btn = [...document.querySelectorAll('.default-btn.cancel-btn')] .find(b => b.textContent.trim() === '留在此页'); if (btn) { btn.click(); await this.delay(200); } }, 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; const nameEl = latestLi.querySelector('.name-text'); const companyEl = latestLi.querySelector('.name-box span:nth-child(2)'); const name = (nameEl?.textContent || '未知').trim().replace(/\s+/g, ' '); const company = (companyEl?.textContent || '').trim().replace(/\s+/g, ' '); const hrKey = `${name}-${company}`.toLowerCase(); if (state.currentTopHRKey === hrKey) return; state.currentTopHRKey = hrKey; if (state.processedHRs.has(hrKey)) { this.log(`发过简历: ${name}${company ? ' - ' + company : ''}`); const avatar = latestLi.querySelector('.figure'); await this.simulateClick(avatar); latestLi.classList.add('last-clicked'); await this.aiReply(); return; } if (latestLi.classList.contains('last-clicked')) return; this.log(`开始沟通 ${name}${company ? ', 公司名: ' + company : ''}`); const avatar = latestLi.querySelector('.figure'); await this.simulateClick(avatar); latestLi.classList.add('last-clicked'); const isResumeSent = await this.processChatContent(); if (isResumeSent) { state.processedHRs.add(hrKey); localStorage.setItem('processedHRs', JSON.stringify([...state.processedHRs])); } } catch (error) { this.log(`沟通出错: ${error.message}`); } }, async aiReply() { try { await this.delay(100); const lastMessage = await this.getLastFriendMessageText(); if (!lastMessage) return false; this.log(`对方: ${lastMessage}`); // AI回复 const maxReplies = [5, 10].reduce((a, b) => a + b); if (state.aiReplyCount >= maxReplies) { this.log('Ai回复已达上限,请开通会员') return false; } const aiReplyText = await this.requestAi(lastMessage); if (!aiReplyText) return false; this.log(`AI回复: ${aiReplyText.slice(0, 30)}...`); state.aiReplyCount++; localStorage.setItem('aiReplyCount', state.aiReplyCount); localStorage.setItem('lastAiDate', state.lastAiDate); const inputBox = await this.waitForElement('#chat-input'); if (!inputBox) return false; inputBox.textContent = ''; inputBox.focus(); document.execCommand('insertText', false, aiReplyText); await this.delay(120); const sendButton = document.querySelector('.btn-send'); if (sendButton) { await this.simulateClick(sendButton); } else { const enterKeyEvent = new KeyboardEvent('keydown', { key: 'Enter', keyCode: 13, code: 'Enter', which: 13, bubbles: true }); inputBox.dispatchEvent(enterKeyEvent); } return true; } catch (error) { this.log(`AI回复出错: ${error.message}`); return false; } }, async requestAi(a) { // 解码 Authorization Token const b = (function () { const c = [ 0x73, 0x64, 0x56, 0x45, 0x44, 0x41, 0x42, 0x6a, 0x5a, 0x65, 0x49, 0x6b, 0x77, 0x58, 0x4e, 0x42, 0x46, 0x4e, 0x42, 0x73, 0x3a, 0x43, 0x71, 0x4d, 0x58, 0x6a, 0x71, 0x65, 0x50, 0x56, 0x43, 0x4a, 0x62, 0x55, 0x59, 0x4a, 0x50, 0x63, 0x69, 0x70, 0x4a ]; return c.map(d => String.fromCharCode(d)).join(''); })(); // 解码 API 请求地址 const d = (function () { const e = '68747470733a2f2f737061726b2d6170692d6f70656e2e78662d79756e2e636f6d2f76312f636861742f636f6d706c6574696f6e73'; return e.replace(/../g, f => String.fromCharCode(parseInt(f, 16))); })(); // 构建请求体 const g = { model: 'lite', messages: [ { role: 'system', content: localStorage.getItem('aiRole') || '你是有工作经验的求职者,擅长交流。你会用一些口语化的表达(如“嗯”、“呃”)和语气词(如“啊”、“呢”),使对话听起来自然。遇到不清楚的问题,你会请求对方解释,确保准确理解。回复简洁明了,避免冗长复杂的句子结构。' }, { role: 'user', content: a } ], temperature: 0.9, top_p: 0.8, max_tokens: 512 }; return new Promise((h, i) => { GM_xmlhttpRequest({ method: 'POST', url: d, headers: { 'Content-Type': 'application/json', 'Authorization': 'Bearer ' + b }, data: JSON.stringify(g), onload: (j) => { console.log('API响应:', j.responseText); try { const k = JSON.parse(j.responseText); if (k.code !== 0) { throw new Error('API错误: ' + k.message + '(Code: ' + k.code + ')'); } h(k.choices[0].message.content.trim()); } catch (l) { i(new Error('响应解析失败: ' + l.message + '\n原始响应: ' + j.responseText)); } }, onerror: (m) => { i(new Error('网络请求失败: ' + m)); } }); }); }, async getLastFriendMessageText() { try { await this.delay(120); const chatContainer = document.querySelector('.chat-message .im-list'); if (!chatContainer) return null; const messageItems = Array.from(chatContainer.querySelectorAll('li.message-item')); const friendMessages = messageItems.filter(item => item.classList.contains('item-friend') ); if (friendMessages.length === 0) return null; const lastFriendMessage = friendMessages[friendMessages.length - 1]; const spanEl = lastFriendMessage.querySelector('.text span'); if (!spanEl) return null; const textContent = spanEl.textContent.trim(); return textContent; } catch (error) { return null; } }, async processChatContent() { try { await this.delay(100); // 点击“常用语”按钮 const dictBtn = await this.waitForElement('.btn-dict'); if (!dictBtn) { this.log('未找到常用语按钮'); return false; } await this.simulateClick(dictBtn); await this.delay(100); // 查找常用语列表 const dictList = await this.waitForElement('ul[data-v-8e790d94=""]'); if (!dictList) { this.log('未找到常用语列表'); return false; } const dictItems = dictList.querySelectorAll('li'); if (!dictItems || dictItems.length === 0) { this.log('常用语列表为空'); return false; } // 遍历并点击每条常用语 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(100); } // 查找“发简历”按钮 const resumeBtn = await this.waitForElement(() => { return [...document.querySelectorAll('.toolbar-btn')].find( el => el.textContent.trim() === '发简历' ); }); if (!resumeBtn) { this.log('无法发送简历'); return false; } if (resumeBtn.classList.contains('unable')) { this.log('对方未回复,您无权发送简历'); return false; } // 点击“发简历” await this.simulateClick(resumeBtn); await this.delay(160); // 查找确认发送按钮 const confirmBtn = await this.waitForElement('span.btn-sure-v2'); if (!confirmBtn) { this.log('未找到发送按钮'); return false; } await this.simulateClick(confirmBtn); return true; } catch (error) { this.log(`处理出错: ${error.message}`); return false; } }, 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: document.defaultView, clientX: x, clientY: y, ...options }); element.dispatchEvent(event); }; dispatchMouseEvent('mouseover'); await this.delay(30); dispatchMouseEvent('mousemove'); await this.delay(30); dispatchMouseEvent('mousedown', { button: 0 }); await this.delay(30); dispatchMouseEvent('mouseup', { button: 0 }); await this.delay(30); dispatchMouseEvent('click', { button: 0 }); }, async waitForElement(selectorOrFunction, timeout = 5000) { return new Promise((resolve) => { let element; if (typeof selectorOrFunction === 'function') { element = selectorOrFunction(); } else { element = document.querySelector(selectorOrFunction); } if (element) { return resolve(element); } const timeoutId = setTimeout(() => { observer.disconnect(); resolve(null); }, timeout); const observer = new MutationObserver(() => { 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() { toggleProcess(); this.log('所有岗位沟通完成,恭喜你即将找到理想工作!'); state.currentIndex = 0; state.lastMessageTime = 0; }, log(message) { const logEntry = `[${new Date().toLocaleTimeString()}] ${message}`; 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(); state.locationKeyword = elements.locationInput.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; // 强制将运行状态设为 false(虽然上面已经反转过) state.isRunning = false; // 清除本地存储中已处理的 HR 记录 localStorage.removeItem('processedHRs'); // 重置内存中的已处理记录集合 state.processedHRs = new Set(); } } /** * 显示一个自定义的弹窗提示框 * @param {string} message - 要显示的消息内容(支持换行) */ function showCustomAlert(message) { // 创建遮罩层 const overlay = document.createElement('div'); overlay.id = 'custom-alert-overlay'; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); display: flex; justify-content: center; align-items: center; z-index: 9999; backdrop-filter: blur(3px); animation: fadeIn 0.3s ease-out; `; // 创建弹窗主体容器 const dialog = document.createElement('div'); dialog.id = 'custom-alert-dialog'; dialog.style.cssText = ` background: white; border-radius: 16px; box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); width: 90%; max-width: 400px; overflow: hidden; transform: scale(0.95); animation: scaleIn 0.3s ease-out forwards; `; // 创建标题栏 const header = document.createElement('div'); header.style.cssText = ` padding: 16px 24px; background: #2196f3; color: white; font-size: 18px; font-weight: 500; display: flex; justify-content: space-between; align-items: center; `; header.innerHTML = `BOSS海投助手`; // 创建内容区域 const content = document.createElement('div'); content.style.cssText = ` padding: 24px; font-size: 16px; line-height: 1.8; color: #333; `; content.innerHTML = message.replace(/\n/g, '
'); // 支持换行符转换为
// 创建底部按钮区域 const footer = document.createElement('div'); footer.style.cssText = ` padding: 12px 24px; display: flex; justify-content: center; border-top: 1px solid #eee; `; // 创建确认按钮 const confirmBtn = document.createElement('button'); confirmBtn.style.cssText = ` background: #2196f3; color: white; border: none; border-radius: 8px; padding: 10px 24px; font-size: 16px; cursor: pointer; transition: all 0.3s; box-shadow: 0 4px 12px rgba(33, 150, 243, 0.4); `; confirmBtn.textContent = '开始使用'; // 点击确认按钮后关闭弹窗 confirmBtn.addEventListener('click', () => { overlay.remove(); }); // 悬浮动画效果 confirmBtn.addEventListener('mouseenter', () => { confirmBtn.style.transform = 'translateY(-2px)'; confirmBtn.style.boxShadow = '0 6px 16px rgba(33, 150, 243, 0.5)'; }); // 移出鼠标恢复样式 confirmBtn.addEventListener('mouseleave', () => { confirmBtn.style.transform = 'translateY(0)'; confirmBtn.style.boxShadow = '0 4px 12px rgba(33, 150, 243, 0.4)'; }); // 将按钮加入底部区域 footer.appendChild(confirmBtn); // 组装弹窗结构 dialog.appendChild(header); dialog.appendChild(content); dialog.appendChild(footer); // 将弹窗加入遮罩层 overlay.appendChild(dialog); // 插入到页面中 document.body.appendChild(overlay); // 添加关键帧动画样式 const style = document.createElement('style'); style.textContent = ` @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes scaleIn { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } } `; document.head.appendChild(style); } function showCustomAlert() { // 创建遮罩层 const overlay = document.createElement("div"); overlay.id = "custom-alert-overlay"; overlay.style.cssText = ` position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.7); display: flex; justify-content: center; align-items: center; z-index: 9999; backdrop-filter: blur(5px); animation: fadeIn 0.3s ease-out; `; // 创建信封容器 const envelopeContainer = document.createElement("div"); envelopeContainer.id = "envelope-container"; envelopeContainer.style.cssText = ` position: relative; width: 90%; max-width: 500px; height: 350px; perspective: 1000px; `; // 创建信封主体 const envelope = document.createElement("div"); envelope.id = "envelope"; envelope.style.cssText = ` position: absolute; width: 100%; height: 100%; transform-style: preserve-3d; transition: transform 0.6s ease; `; // 创建信封背面(封面) const envelopeBack = document.createElement("div"); envelopeBack.id = "envelope-back"; envelopeBack.style.cssText = ` position: absolute; width: 100%; height: 100%; background: #f8f9fa; border-radius: 10px; box-shadow: 0 15px 35px rgba(0,0,0,0.2); backface-visibility: hidden; display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 30px; cursor: pointer; transition: all 0.3s; `; envelopeBack.innerHTML = `
致海投用户的一封信
点击加速您的求职之旅
© 2025 BOSS海投助手 | Yangshengzhou 版权所有
`; envelopeBack.addEventListener("click", () => { envelope.style.transform = "rotateY(180deg)"; setTimeout(() => { document.getElementById("letter-content").style.display = "block"; document.getElementById("letter-content").style.animation = "fadeInUp 0.5s ease-out forwards"; }, 300); }); // 创建信封正面(打开后的内容页) const envelopeFront = document.createElement("div"); envelopeFront.id = "envelope-front"; envelopeFront.style.cssText = ` position: absolute; width: 100%; height: 100%; background: #fff; border-radius: 10px; box-shadow: 0 15px 35px rgba(0,0,0,0.2); transform: rotateY(180deg); backface-visibility: hidden; display: flex; flex-direction: column; `; // 创建标题栏 const titleBar = document.createElement("div"); titleBar.style.cssText = ` padding: 20px 30px; background: #2196f3; color: white; font-size: 20px; font-weight: 600; border-radius: 10px 10px 0 0; display: flex; align-items: center; `; titleBar.innerHTML = `致海投助手用户:`; // 创建信件内容区域 const letterContent = document.createElement("div"); letterContent.id = "letter-content"; letterContent.style.cssText = ` flex: 1; padding: 15px; overflow-y: auto; font-size: 16px; line-height: 1.8; color: #333; background: url('https://picsum.photos/id/1068/1000/1000') center / cover no-repeat; background-blend-mode: overlay; background-color: rgba(255,255,255,0.95); display: none; `; letterContent.innerHTML = `

亲爱的朋友:

  见字如面,展信佳。

  我是Yangshengzhou,一个找实习近崩溃的学生,也是海投插件的作者。 还记得我第一次投简历的场景:期待、不安和憧憬。 后来,我在无数深夜里反复打磨简历、通勤路上刷招聘App, 效率低到崩溃————这便是海投助手诞生的初衷。

  经济下行,求职像在漫漫黑夜里跑马拉松。 但请相信,你从来不是孤军奋战。 这个免费的小工具,愿能成为你路上的烛光:

  求职路上的每一次开始,都是向理想靠近的脚印。 也许现在的你正迷茫,但那些看似波澜不惊的日复一日, 终将某一天让你看见坚持的意义。

  点击"开始使用",把焦虑交给代码,把希望留给自己。 愿你能收到心仪的offer,让所有努力都有回响。

2025年5月11日凌晨
Yangshengzhou
`; // 创建按钮区域 const buttonArea = document.createElement("div"); buttonArea.style.cssText = ` padding: 20px 30px; display: flex; justify-content: center; border-top: 1px solid #eee; background: #f8f9fa; border-radius: 0 0 10px 10px; `; // 创建“开始使用”按钮 const startButton = document.createElement("button"); startButton.style.cssText = ` background: linear-gradient(135deg, #2196f3, #1976d2); color: white; border: none; border-radius: 8px; padding: 12px 30px; font-size: 16px; font-weight: 500; cursor: pointer; transition: all 0.3s; box-shadow: 0 6px 16px rgba(33,150,243,0.3); outline: none; display: flex; align-items: center; `; startButton.innerHTML = `开始使用`; startButton.addEventListener("click", () => { envelopeContainer.style.animation = "scaleOut 0.3s ease-in forwards"; overlay.style.animation = "fadeOut 0.3s ease-in forwards"; setTimeout(() => { overlay.remove(); if (location.pathname.includes("/chat")) { UI.createControlPanel(); UI.createMiniIcon(); } }, 300); }); // 将按钮插入按钮区域 buttonArea.appendChild(startButton); // 构建信封正面结构 envelopeFront.appendChild(titleBar); envelopeFront.appendChild(letterContent); envelopeFront.appendChild(buttonArea); // 把封面和内页加入信封 envelope.appendChild(envelopeBack); envelope.appendChild(envelopeFront); // 把信封放入信封容器中 envelopeContainer.appendChild(envelope); // 把信封容器放入遮罩层 overlay.appendChild(envelopeContainer); // 把遮罩层插入页面 document.body.appendChild(overlay); // 添加动画样式 const style = document.createElement("style"); style.textContent = ` @keyframes fadeIn { from { opacity: 0 } to { opacity: 1 } } @keyframes fadeOut { from { opacity: 1 } to { opacity: 0 } } @keyframes scaleOut { from { transform: scale(1); opacity: 1 } to { transform: scale(.9); opacity: 0 } } @keyframes fadeInUp { from { opacity: 0; transform: translateY(20px) } to { opacity: 1; transform: translateY(0) } } #envelope-back:hover { transform: translateY(-5px); box-shadow: 0 20px 40px rgba(0,0,0,0.25); } #envelope-front button:hover { transform: translateY(-2px); box-shadow: 0 8px 20px rgba(33,150,243,0.4); } #envelope-front button:active { transform: translateY(1px); } `; document.head.appendChild(style); } function init() { // 获取当前时间 const now = new Date(); // 计算到午夜的时间 const night = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0); const msToMidnight = night - now; // 设置定时器,在午夜清除 localStorage 中的特定项 setTimeout(() => { localStorage.removeItem('aiReplyCount'); localStorage.removeItem('lastAiDate'); localStorage.removeItem('hasShownLetterToday'); localStorage.removeItem('letterLastShown'); }, msToMidnight); // 创建控制面板和最小化图标 UI.createControlPanel(); UI.createMiniIcon(); // 设置 body 的 position 属性为 relative document.body.style.position = 'relative'; // 检查是否今天已显示过信件 const today = new Date().toISOString().split('T')[0]; // 格式:YYYY-MM-DD const lastShown = localStorage.getItem('letterLastShown'); function showLetterOncePerDay() { if (lastShown !== today) { // 第一次运行或不是今天 showCustomAlert(); // 更新本地存储记录 localStorage.setItem('letterLastShown', today); } } // 根据当前页面路径执行不同操作 if (location.pathname.includes('/jobs')) { // 如果在 /jobs 页面,询问是否打开chat窗口 const confirmOpen = confirm("海投插件可以自动回复HR信息,是否需要打开聊天页面?"); if (confirmOpen) { window.open('https://www.zhipin.com/web/geek/chat', '_blank'); } } else if (location.pathname.includes('/chat')) { // 如果在 /chat 页面: // 1. 显示至用户的一封信 showLetterOncePerDay(); // 2. 禁用输入框 if (elements.filterInput) { elements.filterInput.disabled = true; elements.filterInput.placeholder = '聊天页面岗位筛选功能无效'; elements.filterInput.title = "当前页面该功能无效"; } if (elements.locationInput) { elements.locationInput.disabled = true; elements.locationInput.placeholder = '聊天页面地点筛选功能无效'; elements.locationInput.title = "当前页面该功能无效"; } Core.log('在聊天页面,海投插件能自动发送您的自我介绍(常用语)和简历,之后用AI智能回复HR的消息。'); } } // 当页面加载完成后调用 init 函数 window.addEventListener('load', init); })();