// ==UserScript== // @name Boss直聘销售牛人精准匹配(v11.0全国商场奥莱+全大牌版) // @namespace http://tampermonkey.net/ // @version 11.0 // @description 囊括比斯特/杉杉/砂之船/佛罗伦萨/首创/万象天地/in77/金鹰/北国等全国商场;全大牌覆盖;排除店长及纯收银陈列;单轮50人循环 // @author Assistant // @match https://www.zhipin.com/* // @grant none // ==/UserScript== (function () { 'use strict'; // ================= 1. 全国商场奥莱与大牌映射库 ================= const BRAND_MAPPING = { '全国奥莱集团': [ // 比斯特 / 奕欧来 '比斯特', '上海比斯特', '苏州比斯特', '奕欧来', // 杉杉 / 杉井体系 '杉杉', '杉杉奥莱', '无锡杉杉', '南昌杉杉', '沈阳杉杉', '杉井', '宁波杉井', '杉井奥莱', // 砂之船体系 '砂之船', '长春砂之船', '杭州砂之船', '南京砂之船', '江北砂之船', '江宁砂之船', '重庆砂之船', '西安砂之船', '贵阳砂之船', // 佛罗伦萨小镇体系 '佛罗伦萨', '佛罗伦萨小镇', '广佛佛罗伦萨', '天津佛罗伦萨', '上海佛罗伦萨', '成都佛罗伦萨', '武汉佛罗伦萨', // 首创 / 百联 / 其他奥莱 '首创', '首创奥莱', '北京首创', '百联奥莱', '武汉百联', '青浦百联', '无锡百联', '东郊奥莱', '南京东郊', '奥莱', '奥特莱斯', 'OUTLETS', 'Outlets' ], '全国核心高端商场/购物中心': [ // 万象体系 '万象天地', '佛山万象天地', '深圳万象天地', '南京万象天地', '万象城', '杭州万象城', '上海万象城', '深圳湾万象城', '万象汇', // 华东重点百货/商圈 'in77', '湖滨in77', '湖滨 in77', 'IN77', '杭州大厦', '武林银泰', '西湖银泰', '银泰百货', '银泰城', '银泰', '金鹰', '盐城金鹰', '南京金鹰', '新街口金鹰', '景枫', '南京景枫', '景枫中心', '德基', '德基广场', '北国', '北国商城', '北国奥莱', '石家庄北国', '壹方城', '深圳壹方城', '壹方天地', '嘉里中心', '静安嘉里', '浦东嘉里', '杭州嘉里', '恒隆', '恒隆广场', '港汇', '港汇恒隆', '国金', 'IFC', 'ifc', '环贸', 'iapm', '太古里', '太古汇', '前滩太古里', '兴业太古汇', '三里屯太古里', 'SKP', 'skp', 'K11', 'k11', '新天地', '老佛爷', '连卡佛', '八佰伴', '第一八佰伴', '久光', '久光百货', '芮欧', '大悦城', '日上免税', '日上', '杜福睿', '中服免税', '中免' ], '轻奢时装/皮具/设计师品牌': [ 'Michael Kors', 'MK', 'mk', '迈克高仕', 'Coach', '蔻驰', 'Tory Burch', '汤丽柏琦', 'Kate Spade', 'MCM', 'Furla', '芙拉', 'Longchamp', '珑骧', '宝姿', 'Ports', 'PORTS', '联宇宝姿', '联合宝姿', 'APM', 'apm', 'APM Monaco', '优雅益', 'Sandro', 'Maje', 'SMCP', '晟慕', 'Theory', '思优俪', 'IRO', 'AllSaints', 'Ami', 'AMI', 'Ami Paris', 'Ganni', 'Self-Portrait', 'Acne', 'Acne Studios', 'we11done', 'We11done', 'Tommy', 'TOMMY', 'Tommy Hilfiger', '汤美费格', 'Calvin Klein', 'CK', 'CalvinKlein', '凯文克莱', 'Ralph Lauren', '拉夫劳伦', 'POLO', 'Hugo Boss', '雨果博斯', 'Lacoste', '鳄鱼', 'AIGLE', '艾高', '微可多', 'Dazzle', 'DAZZLE', '地素', 'd\'zzit', '雅莹', 'EP雅莹', 'EP', '玛丝菲尔', 'Marisfrolg', '歌力思', '朗姿', '玖姿', '安正', '卓雅', 'JORYA', '欣贺', '恩曼琳', 'Mo&Co', 'MO&Co', 'EPO', '爱帛', 'Edition', 'JNBY', '江南布衣', 'Less', '播', 'broadcast', '哥弟', '阿玛施', '比音勒芬', 'Bebe', '朗浩', '麦姝', 'Moussy', 'SLY', 'Chuu', 'Emis', 'BM', 'Brandy Melville', 'Aape', 'Bape', 'PHANTACI', 'Gentle Monster', '三宅一生', 'Issey Miyake', 'Jott', '沙涓', 'Bally', '巴丽', 'Clarks', '克履仕', '其乐', '飒拉', 'ZARA', 'Zara' ], '顶级奢品': [ 'LV', 'Louis Vuitton', '路易威登', '路威酩轩', 'LVMH', 'Chanel', '香奈儿', 'Hermes', '爱马仕', 'Gucci', '古驰', '开云', 'Kering', 'Dior', '迪奥', 'Prada', '普拉达', 'Miu Miu', 'Miumiu', '缪缪', 'Saint Laurent', 'YSL', '圣罗兰', 'Celine', '思琳', '赛琳', 'Loewe', '罗意威', 'Balenciaga', '巴黎世家', 'Bottega Veneta', 'BV', '葆蝶家', 'Burberry', '博柏利', '巴宝莉', 'Fendi', '芬迪', 'Valentino', '华伦天奴', 'Givenchy', '纪梵希', 'Ferragamo', '菲拉格慕', 'Zegna', '杰尼亚', 'Armani', '阿玛尼', '乔治阿玛尼', 'Giorgio Armani', 'Tom Ford', '汤姆福特', 'Thom Browne', '瑭姆步朗', 'Loro Piana', '诺悠翩雅', 'Brunello Cucinelli', 'Moschino' ], '头部运动/高端户外': [ '始祖鸟', 'Arc\'teryx', 'ARCTERYX', 'lululemon', 'Lululemon', '露露柠檬', 'LULU', 'Salomon', 'SALOMON', '萨洛蒙', 'Mammut', '猛犸象', 'Kolon', 'KOLON', '可隆', 'Descente', 'DESCENTE', '迪桑特', 'Amer', '亚玛芬', 'On', '昂跑', 'On昂跑', 'Hoka', 'HOKA', '戴珂', 'Deckers', 'UGG', 'ugg', 'Peak Performance', 'Patagonia', '巴塔哥尼亚', 'The North Face', '北面', '威富', 'VF', 'Timberland', '添柏岚', 'Columbia', '哥伦比亚', 'Vans', 'VANS', '丰梵', 'Birkenstock', '勃肯', 'Dr. Martens', '马丁靴', 'Nike', 'NIKE', '耐克', 'Adidas', 'ADIDAS', '阿迪达斯', 'Puma', '彪马', 'Asics', '亚瑟士', 'Fila', 'FILA', '斐乐', 'Skechers', '斯凯奇', '滔搏', '滔博', '宝胜', '李宁' ], '高级珠宝名表/美妆': [ '劳力士', 'Rolex', '卡地亚', 'Cartier', '宝格丽', 'Bulgari', '蒂芙尼', 'Tiffany', '梵克雅宝', '萧邦', 'Chopard', '欧米茄', 'Omega', '浪琴', 'Longines', '施华洛世奇', 'Swarovski', '潘多拉', 'Pandora', '周大福', '周生生', '六福', '潮宏基', '老凤祥', '溥仪眼镜', 'Glasstique', '丝芙兰', 'Sephora', '雅诗兰黛', '兰蔻', '海蓝之谜', '赫莲娜', '资生堂' ] }; const CONFIG = { // 允许并优先匹配的一线销售岗位(吸纳店助、高销、资深、领班、带班) SALES_ROLES: [ '店助', '资深销售', '高销', '领班', '带班', '高级销售', '导购', '店员', '营业员', '销售', '顾问', 'SA', 'sales', '零售', '专员', '买手' ], // 排除职位抬头为收银或陈列 ROLE_SPECIFIC_EXCLUDES: [ '收银员', '专职收银', '收银主管', '收银', '陈列师', '陈列专员', '视觉陈列', 'VM', '陈列员', '陈列主管' ], // 严格排除门店店长/店经理 MANAGER_EXCLUDE: ['门店店长', '店长', '店经理', '区域经理', '督导', '总监'], // 杂类排除词 EXCLUDE_WORDS: ['保安', '保洁', '骑手', '前台', '文员', '行政', '普工', '操作工', '发传单'], MIN_DELAY: 4000, MAX_DELAY: 9000, MAX_GREETINGS_PER_SESSION: 50 // 单次打招呼50人 }; let isRunning = false; let greetedCount = 0; let timer = null; const getRandomDelay = (min, max) => Math.floor(Math.random() * (max - min + 1)) + min; const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms)); // ================= 2. 跨框架上下文检测与按钮抓取 ================= function getWorkingContext() { const iframes = document.querySelectorAll('iframe'); for (const f of iframes) { try { const doc = f.contentDocument || (f.contentWindow && f.contentWindow.document); if (doc && doc.body) { const hasGreet = Array.from(doc.querySelectorAll('button, .btn-greet')) .some(el => (el.textContent || '').trim().includes('打招呼')); if (hasGreet) return { doc, win: f.contentWindow }; } } catch (e) {} } return { doc: document, win: window }; } function getCandidateButtons(doc) { const allBtns = Array.from(doc.querySelectorAll('button.btn-greet, .btn-greet, button.btn, button, a')); return allBtns.filter(el => { const txt = (el.textContent || '').trim(); const valid = txt === '打招呼' || (txt.includes('打招呼') && txt.length <= 6); const clickable = !el.disabled && !el.classList.contains('disabled'); return valid && clickable; }); } // 单人卡片精确隔离(严禁回溯跨入列表容器) function getSingleCard(btn, doc) { let curr = btn.parentElement; let singleCard = null; while (curr && curr !== doc.body) { const txt = curr.textContent || ''; if (txt.includes('岁') && (txt.includes('期望') || txt.includes('最近关注') || txt.includes('年'))) { const innerBtns = Array.from(curr.querySelectorAll('button, .btn-greet, a')) .filter(b => (b.textContent || '').trim().includes('打招呼')); if (innerBtns.length === 1 && curr.offsetHeight < 380) { singleCard = curr; } else if (innerBtns.length > 1) { break; } } curr = curr.parentElement; } return singleCard || btn.closest('li') || btn.parentElement.parentElement; } // ================= 3. 业务精准判定 ================= function evaluateCandidate(card) { const text = card.textContent || ''; // 1. 无关杂岗排除 for (const ex of CONFIG.EXCLUDE_WORDS) { if (text.includes(ex)) { return { pass: false, reason: `排除词: ${ex}` }; } } // 2. 排除真正的门店店长 const isTrueManager = /期望[^\n]*·\s*(门店店长|店长|店经理)/.test(text) || /(\d{4}|\至今)[^\n]*·\s*(门店店长|店长|店经理)/.test(text); if (isTrueManager) { return { pass: false, reason: '管理岗排除(店长)' }; } // 3. 排除纯收银或纯陈列岗位 for (const role of CONFIG.ROLE_SPECIFIC_EXCLUDES) { const regExpect = new RegExp(`期望[^\n]*·[^\n]*${role}`); const regRecent = new RegExp(`(\\d{4}|至今)[^\n]*·\\s*${role}`); if (regExpect.test(text) || regRecent.test(text)) { return { pass: false, reason: `岗位属性排除(${role})` }; } } // 4. 必须具备对口一线销售岗位 const matchedRole = CONFIG.SALES_ROLES.find(r => text.includes(r)); if (!matchedRole) { return { pass: false, reason: '无对口一线销售岗位' }; } // 5. 命中全国大牌 / 奥莱 / 知名商场 const matched = []; for (const [group, keywords] of Object.entries(BRAND_MAPPING)) { for (const kw of keywords) { if (new RegExp(kw, 'i').test(text)) { matched.push(`${group}(${kw})`); break; } } } if (matched.length > 0) { return { pass: true, reason: `🔥 岗位:${matchedRole} | 渠道/品牌:${matched[0]}` }; } return { pass: false, reason: '未命中目标大牌/商圈' }; } // ================= 4. 自动化主循环 ================= async function runAutoLoop() { if (!isRunning) return; if (greetedCount >= CONFIG.MAX_GREETINGS_PER_SESSION) { logStatus(`已完成本轮 ${greetedCount} 人打招呼!\n点击下方按钮重置计数开启下一轮。`); toggleRun(false, true); return; } const { doc, win } = getWorkingContext(); if (doc.querySelector('.dialog-wrap, .nc_wrapper, [class*="captcha"]')) { logStatus('⚠️ 检测到滑块验证码,已自动安全熔断!'); toggleRun(false); return; } const greetButtons = getCandidateButtons(doc); if (greetButtons.length === 0) { logStatus('当前屏无可沟通人选,正在向下滑动...'); scrollDown(doc, win); await sleep(2500); runAutoLoop(); return; } let foundTarget = false; for (const btn of greetButtons) { if (!isRunning) return; const card = getSingleCard(btn, doc); if (!card || card.dataset.scriptDone === 'true') continue; card.dataset.scriptDone = 'true'; const evaluation = evaluateCandidate(card); const name = (card.textContent.split('\n')[0] || '候选人').trim().slice(0, 8); console.log(`[牛人助手v11] 审核: ${name} -> ${evaluation.reason}`); if (evaluation.pass) { foundTarget = true; card.scrollIntoView({ behavior: 'smooth', block: 'center' }); card.style.outline = '3px solid #ff4d4f'; card.style.background = '#fffbe6'; const waitMs = getRandomDelay(CONFIG.MIN_DELAY, CONFIG.MAX_DELAY); logStatus(`目标: ${name}\n${evaluation.reason}\n⏳ 拟人等待 ${(waitMs / 1000).toFixed(1)} 秒...`); await sleep(waitMs); if (!isRunning) return; btn.click(); greetedCount++; updateCounter(); logStatus(`[成功] 已和 ${name} 打招呼 (第 ${greetedCount} 人)`); await sleep(2000); break; } else { card.style.opacity = '0.35'; } } if (!foundTarget) { scrollDown(doc, win); await sleep(2000); } timer = setTimeout(runAutoLoop, 1200); } function scrollDown(doc, win) { win.scrollBy({ top: 650, behavior: 'smooth' }); const scrollBoxes = doc.querySelectorAll('div, ul, section'); scrollBoxes.forEach(el => { if (el.scrollHeight > el.clientHeight + 100) { el.scrollTop += 650; } }); } // ================= 5. Pointer Events 平滑拖拽 ================= function makePanelDraggable(panel, handle) { let isDragging = false; let startX, startY, initialLeft, initialTop; handle.addEventListener('pointerdown', (e) => { if (e.button !== 0) return; isDragging = true; startX = e.clientX; startY = e.clientY; const rect = panel.getBoundingClientRect(); initialLeft = rect.left; initialTop = rect.top; panel.style.left = `${initialLeft}px`; panel.style.top = `${initialTop}px`; panel.style.right = 'auto'; panel.style.bottom = 'auto'; handle.setPointerCapture(e.pointerId); e.preventDefault(); }); handle.addEventListener('pointermove', (e) => { if (!isDragging) return; const deltaX = e.clientX - startX; const deltaY = e.clientY - startY; let newLeft = initialLeft + deltaX; let newTop = initialTop + deltaY; newLeft = Math.max(10, Math.min(window.innerWidth - panel.offsetWidth - 10, newLeft)); newTop = Math.max(10, Math.min(window.innerHeight - panel.offsetHeight - 10, newTop)); panel.style.left = `${newLeft}px`; panel.style.top = `${newTop}px`; }); const stopDrag = (e) => { if (!isDragging) return; isDragging = false; try { handle.releasePointerCapture(e.pointerId); } catch (err) {} }; handle.addEventListener('pointerup', stopDrag); handle.addEventListener('pointercancel', stopDrag); } // ================= 6. 控制面板 ================= function createPanel() { if (window !== window.top || document.getElementById('boss-auto-panel')) return; const panel = document.createElement('div'); panel.id = 'boss-auto-panel'; panel.innerHTML = `
🎯 销售牛人匹配 v11.0 按住拖拽
本轮进度: 0 / ${CONFIG.MAX_GREETINGS_PER_SESSION} 人
全国商场/奥莱大牌库已加载,待命中
`; Object.assign(panel.style, { position: 'fixed', top: '90px', right: '25px', width: '260px', background: 'rgba(255, 255, 255, 0.98)', boxShadow: '0 4px 18px rgba(0,0,0,0.18)', borderRadius: '8px', padding: '12px', zIndex: '999999', border: '1px solid #e0e0e0' }); document.body.appendChild(panel); const dragHandle = document.getElementById('boss-panel-drag-header'); makePanelDraggable(panel, dragHandle); document.getElementById('script-toggle').addEventListener('click', () => { toggleRun(!isRunning); }); } function toggleRun(state, isCompleted = false) { isRunning = state; const btn = document.getElementById('script-toggle'); if (isRunning) { if (greetedCount >= CONFIG.MAX_GREETINGS_PER_SESSION) { greetedCount = 0; updateCounter(); logStatus('已重置计数,开启新一轮 50 人沟通...'); } btn.innerText = '暂停运行'; btn.style.background = '#ff4d4f'; runAutoLoop(); } else { clearTimeout(timer); if (isCompleted) { btn.innerText = '开始下一轮 (重置为0)'; btn.style.background = '#fa8c16'; } else { btn.innerText = '启动精准沟通'; btn.style.background = '#00bebd'; } } } function logStatus(text) { const el = document.getElementById('script-status'); if (el) el.innerText = text; } function updateCounter() { const el = document.getElementById('script-count'); if (el) el.innerText = greetedCount; } window.addEventListener('load', () => setTimeout(createPanel, 1200)); })();