您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
to show hr lastest login time,help you deliver your resume efficiently.
当前为
// ==UserScript== // @name showBossActiveTime // @namespace http://www.chensong.cc/ // @version 0.4.3 // @description to show hr lastest login time,help you deliver your resume efficiently. // @author chensong // @match https://www.zhipin.com/web/geek/job // @match https://www.zhipin.com/web/geek/job* // @match https://www.zhipin.com/web/geek/recommend // @match https://www.zhipin.com/web/geek/recommend* // @exclude https://www.zhipin.com/web/geek/map/jobs* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant GM_xmlhttpRequest // @license MIT //失业中,欢迎来扰:) [email protected] // ==/UserScript== (function () { 'use strict'; class ShowBossActiveTime { static MAXLIMIT = 5; static TIME = 2000; constructor(options) { this.queryQueue = []; //记录需要查询的队列 this.currentDomList = []; //当前列表的dom this.secondQueryList = []; //需要二次查询的队列 this.maxLimit = ShowBossActiveTime.MAXLIMIT; this.timer = null; this.frame = null; let bossActiveStatusList; try { bossActiveStatusList = JSON.parse( localStorage.getItem('bossActiveStatusList') ) || [ '半年前活跃', '近半年活跃', '4月前活跃', '2月内活跃', '2周内活跃' ]; } catch (error) { // 转换之前客户端存在的数据格式 bossActiveStatusList = localStorage .getItem('bossActiveStatusList') .split(','); } this.statusOptions = bossActiveStatusList.filter((option)=> option && option!== ''); this.removeStatusList = []; this.options = Object.assign( { listElement: '.job-card-wrapper', onlineElement: '.boss-online-tag', chatElement: '.start-chat-btn', hunterElement: '.job-tag-icon', linkElement: '.job-card-left', paginationElement: '.options-pages', hideChated: false }, options ) this.list = []; //数据列表 //添加过滤条件,因为要保存选择数据,所以这个不能切换时清空 this.addStatusFilter(); this.addStyleSheet(); this.ready(); // 监听请求数据事件 this.observeLoadingData(); this.init(); } // 获取节点列表 getList() { Array.from(document.querySelectorAll(this.options.listElement)).forEach( (node) => { const status = node.querySelector(this.options.onlineElement); // 保存所有list的dom this.list.push(node); let hunter = node.querySelector(this.options.hunterElement); hunter && (hunter = hunter.alt === '猎头') // 不在线且不是猎头 if (!status && !hunter) { // 需要查询的dom this.queryQueue.push(node); }else{ //如果是猎头 if (hunter) { const chat = node.querySelector(this.options.chatElement).textContent; this.setText(node, '猎头,没有活跃状态', chat); return; } // 先给在线的数据设置状态 const chat = node.querySelector(this.options.chatElement).textContent; const online = node.querySelector(this.options.onlineElement); if (online) { this.setText(node, '在线', chat); } } } ); } getListStatus() { // 每两秒下一个请求 // 保存请求的条数 let requestNum = this.queryQueue.length; let requestedNum = 0; this.timer = setInterval(() => { if (this.queryQueue.length === 0) { clearInterval(this.timer); this.timer = null; this.maxLimit = ShowBossActiveTime.MAXLIMIT; return; } if (this.maxLimit > 0) { this.maxLimit--; let node = this.queryQueue.shift(); let link = node.querySelector(this.options.linkElement).href; let chat = node.querySelector(this.options.chatElement).textContent; this.getStatusByXHR(link, node, chat).then(({ text, type }) => { // 设置文字 if(text===''){ text ='没有找到hr登录(不可用)状态' } this.setText(node, text, chat); this.toggleDom(node); console.log(type, text, link); // 增加一个请求 this.maxLimit++; requestedNum++; if (requestedNum === requestNum) { this.alertBox('查询完毕'); } }); } }, ShowBossActiveTime.TIME); } async getStatusByXHR(url, node, chat) { return new Promise((resolve) => { // 第一次获取 GM_xmlhttpRequest({ method: 'GET', url, headers: { Cookie: document.cookie }, onload: (response) => { if (/security-check.html/.test(response.finalUrl)) { // 出现重定向后,使用返回的callbackUrl生成目标链接,二次获取 this.getHtmlByFormatUrl(response, 1).then((res) => { resolve(res); }); } else { const doc = this.parseHtml(response); const text = this.getStatusText(doc); resolve({ text, type: '第一次就获取到的数据' }); } } }); }); } getHtmlByFormatUrl(response, repeatCount) { return new Promise((resolve) => { // 二次获取,说明触发302,需要延时 setTimeout(() => { getFinalUrl(response.finalUrl, (url) => { GM_xmlhttpRequest({ method: 'GET', url, onload: (res) => { if (!/security-check.html/.test(res.finalUrl)) { const doc = this.parseHtml(res); const text = this.getStatusText(doc); resolve({ text, type: '二次请求的数据' }); } else { // 二次请求不成功,继续使用GM_xmlhttpRequest成功率低,转为其它形式,先fetch,fetch不行,用iframe return fetch(url, { redirect: 'error' }) .then((res) => { return res.text(); }) .then(async (data) => { const doc = document.createElement('div'); doc.insertAdjacentHTML('afterbegin', data); const text = this.getStatusText(doc); resolve({ text, type: 'fetch的数据' }); }) .catch(async (error) => { /*请求被302临时重定向了,无法获取到数据,需要用iframe来获取了*/ this.getStatusByIframe(url).then((text) => { resolve({ text, type: 'iframe的数据' }); }); }); } } }); }); }, 1000 * repeatCount); }); } parseHtml(res) { const html = res.responseText; const parser = new DOMParser(); return parser.parseFromString(html, 'text/html'); } ready() { var frame = document.createElement('iframe'); frame.style.height = 0; frame.style.width = 0; frame.style.margin = 0; frame.style.padding = 0; frame.style.border = '0 none'; frame.name = 'zhipinMyFrame'; frame.src = 'about:blank'; (document.body || document.documentElement).appendChild(frame); this.frame = frame; } checkIsSecurityCheckPage(tempIframe) { return tempIframe.contentWindow.securityPageName === 'securityCheck'; } async getStatusByIframe(url) { let iframe = document.createElement('iframe'); let match = url.match(/job_detail\/([^\/]+)\.html/); iframe.src = url; iframe.id = 'tempIframe' + match[1]; iframe.style.cssText = 'width:0;height:0;'; document.body.appendChild(iframe); return await new Promise((resolve) => { // 定时检测iframe中的某个节点是否已经渲染 let timer = setInterval(() => { // 获取iframe中的document对象 let iframeDoc = iframe.contentDocument || iframe.contentWindow.document; let hasFooter = iframeDoc.querySelector('#footer'); let status = this.getStatusText(iframeDoc); let verify = iframeDoc.querySelector('page-verify-slider') || document.querySelector('body').textContent.match(/您的账号可能存在异常访问行为,完成验证后即可正常使用/g) if(verify){ this.alertBox('更新过于频繁,触发认证操作了'); this.clear(); } if (hasFooter && iframeDoc.title !== '请稍候' && status) { resolve(status); iframe.remove(); clearInterval(timer); } }, 200); }); } observeLoadingData() { const container = document.querySelector('.search-job-result'); const observer = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList') { const addNode = mutation.addedNodes; const removedNode = mutation.removedNodes; if ( addNode.length && addNode[0].className === 'job-loading-wrapper' ) { console.log('触发了请求列表数据'); } if ( removedNode.length && removedNode[0].className === 'job-loading-wrapper' ) { console.log('页面加载完成,开始执行查询状态'); this.clear(); this.init(); } } }); }); const config = { attributes: false, childList: true, subtree: false }; observer.observe(container, config); // 监听是否是搜索列表页,不是就移除dom const listContainer = document.querySelector('#wrap'); const listObserver = new MutationObserver((mutations) => { mutations.forEach((mutation) => { if (mutation.type === 'childList') { const wrapper = document.querySelector('.job-list-wrapper'); const removeNode = document.querySelector( '#removeFilterDataContainer' ); if (!wrapper && removeNode) { document.body.removeChild(removeNode); listObserver.disconnect(); // 清除查询 this.clear(); } } }); }); listObserver.observe(listContainer, config); } alertBox(msg) { let div = document.createElement('div'); div.id = 'alertBox'; div.innerHTML = msg; document.body.appendChild(div); setTimeout(function () { document.body.removeChild(div); }, 2000); } getStatusText(doc) { const timeNode = doc.querySelector('.boss-active-time'); if (timeNode) { return timeNode.textContent; } else { let status = null; // 没有获取到状态,但页面是已经加载到的了 if (doc.querySelector('.job-boss-info')) { status = '获取到数据了,但不知道是什么数据'; } const isHunter = ['.certification-tags', '.boss-info-attr'].filter( (name) => { const node = doc.querySelector(name); return /猎头|人力|人才|经纪/.test(node?.textContent); } ); isHunter && (status = '猎头,没有活跃状态'); return status; } } toggleDom(node) { const status = node.querySelector('.status')?.textContent; const chat = node.querySelector(this.options.chatElement).textContent; // 先显示全部 node.style.display = 'block'; // 首先判断是否隐藏已沟通 if (this.options.hideChated && chat === '继续沟通') { node.style.display = 'none'; } // 状态数据已经获取了 if (status && chat) { if (this.removeStatusList.includes(status)) { node.style.display = 'none'; } if (this.options.hideChated && chat === '继续沟通') { node.style.display = 'none'; } } } toggleDoms() { this.list.forEach((node) => { this.toggleDom(node); }); } addStatusFilter() { const container = document.createElement('div'); container.id = 'removeFilterDataContainer'; const html = ` <label><input type="checkbox" name="hideChated" value="1">过滤已经沟通过的</label> <div id="boss-active-time-arrow"><svg stroke="currentColor" fill="none" stroke-width="2" viewBox="0 0 24 24" aria-hidden="true" height="1em" width="1em" xmlns="http://www.w3.org/2000/svg"><path stroke-linecap="round" stroke-linejoin="round" d="M19 9l-7 7-7-7"></path></svg></div> `; const title = document.createElement('div'); title.className = 'title'; title.innerHTML = html; const tips = document.createElement('div'); tips.innerHTML = '过滤掉勾选的数据'; tips.className = 'tips'; container.appendChild(title); container.appendChild(tips); container .querySelector('#boss-active-time-arrow') .addEventListener('click', function () { container.classList.contains('hide') ? container.classList.remove('hide') : container.classList.add('hide'); }); this.statusOptions.forEach((option) => { const label = document.createElement('label'); const el = document.createElement('input'); el.type = 'checkbox'; el.name = option; el.value = option; el.className = 'status-checkbox'; label.appendChild(el); label.appendChild(document.createTextNode(option)); container.appendChild(label); }); container.addEventListener('change', () => { const selectedValues = Array.from( container.querySelectorAll('.status-checkbox:checked') ).map((el) => el.value); this.removeStatusList = selectedValues; const hideNode = document.querySelector('input[name="hideChated"]'); this.options.hideChated = hideNode?.checked; this.toggleDoms(); }); document.body.appendChild(container); } // 清空查询列表,清除缓存的dom clear() { this.queryQueue.length = 0; this.list.length = 0; this.currentDomList.length = 0; this.maxLimit = ShowBossActiveTime.MAXLIMIT; clearInterval(this.timer); this.timer = null; } ready() { let frame = document.createElement('iframe'); frame.style.height = 0; frame.style.width = 0; frame.style.margin = 0; frame.style.padding = 0; frame.style.border = '0 none'; frame.name = 'zhipinMyFrame'; frame.src = 'about:blank'; document.body.appendChild(frame); } addStyleSheet() { const style = ` .show-active-status{display:flex;padding:5px 10px;background:#e1f5e3;color:green;width:80%;border-radius:4px;margin-top:10px;margin-bottom:5px;} .show-active-status .status{} .show-active-status .chat{} #alertBox{position: fixed; top: 20%; left: 50%; transform: translate(-50%, -50%); background-color: rgb(0 190 189); border-radius: 5px; color: #fff; z-index: 9999; padding: 20px 40px; font-size: 20px; box-shadow: 0px 0px 10px rgba(0,0,0,.2);} #removeFilterDataContainer{ position: fixed;right: 70px;top: 70px;z-index: 20000;background: #00bebd; color: #fff;display: flex;flex-direction: column;padding-bottom:10px } #removeFilterDataContainer.hide{height:28px;overflow:hidden} #removeFilterDataContainer .title {display:flex;justify-content: space-around;} #removeFilterDataContainer .title label{align-items:center;padding:0 15px;} #removeFilterDataContainer.hide #boss-active-time-arrow svg{transform: rotate(180deg);} #removeFilterDataContainer #boss-active-time-arrow {cursor: pointer;font-size: 24px;background: #009796;padding:2px 10px;line-height:1;} #removeFilterDataContainer .tips{font-size:16px;margin:5px 20px;} #removeFilterDataContainer label{display:flex;padding:0 20px;} #removeFilterDataContainer label input{margin-right:5px;} `; const styleEle = document.createElement('style'); styleEle.id = 'show-boss-active-time-css'; styleEle.innerHTML = style; document.head?.appendChild(styleEle); } // 设置文本内容 setText(node, text, status) { const html = ` <div class="show-active-status"> <p class="status">${text}</p> <p class="chat">${status}</p> </div> `; node.querySelector('.job-info').insertAdjacentHTML('afterend', html); let aEle = node.querySelector('a'); aEle.style.height = 'auto'; aEle.style.paddingBottom = '0'; // 如果是新的状态值,加入到状态列表中 if (!this.statusOptions.includes(text) && text !== '在线') { this.statusOptions.push(text); localStorage.setItem( 'bossActiveStatusList', JSON.stringify(this.statusOptions) ); } } // 获取列表数据 init() { this.getList(); this.alertBox('开始更新状态....,网站安全策略问题,更新会比较缓慢。'); this.getListStatus(); // 判断是否要隐藏已经沟通过的数据 this.toggleDoms(); } } function start() { const Lis = document.querySelectorAll('.job-card-wrapper'); if (Lis.length) { new ShowBossActiveTime(); } else { console.log('no start'); setTimeout(start, 2000); } } start(); // 获取302后详情页的最终地址 function getFinalUrl(url, cb) { let securityPageName = 'securityCheck'; const urlObject = new URL(url); const search = urlObject.search; (function () { var image = new Image(); var lenSrcReferer = url.split('srcReferer').length - 1; image.src = 'https://t.zhipin.com/f.gif?pk=' + securityPageName + '&len=' + lenSrcReferer + '&r=' + document.referrer; })(); (function () { var pageInterNum = 0; var pageStartTime = new Date().getTime(); var UA = window.navigator.userAgent; var isIE; if (UA.indexOf('MSIE ') > -1) { isIE = true; } let frame = document.querySelector('#zhipinMyFrame'); init(frame, url); function init(frame) { var COOKIE_DOMAIN = (function () { var hostName = location.hostname; if (hostName === 'localhost' || /^(\d+\.){3}\d+$/.test(hostName)) { return hostName; } return '.' + hostName.split('.').slice(-2).join('.'); })(); var seriesLoadScript = function (scriptUrl, callback) { var url = scriptUrl; var script = document.createElement('script'); script.setAttribute('type', 'text/javascript'); script.setAttribute('charset', 'UTF-8'); debugger; script.onload = script.onreadystatechange = function () { if ( !isIE || this.readyState == 'loaded' || this.readyState == 'complete' ) { callback(); } }; script.setAttribute('src', scriptUrl); if (frame.tagName != 'IFRAME') { frame.appendChild(script); } else if (frame.contentDocument) { if (frame.contentDocument.body) { frame.contentDocument.body.appendChild(script); } else { frame.contentDocument.documentElement.appendChild(script); } } else if (frame.document) { if (frame.document.body) { frame.document.body.appendChild(script); } else { frame.document.documentElement.appendChild(script); } } }; var getQueryString = function (name) { var reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)'); var r = search.substr(1).match(reg); if (r != null) return unescape(r[2]); return null; }; var Cookie = { get: function (name) { var arr, reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); if ((arr = document.cookie.match(reg))) { return unescape(arr[2]); } else { return null; } }, set: function (name, value, time, domain, path) { var str = name + '=' + encodeURIComponent(value); if (time) { var date = new Date(time).toGMTString(); str += ';expires=' + date; } str = domain ? str + ';domain=' + domain : str; str = path ? str + ';path=' + path : str; document.cookie = str; } }; var urlFilter = { config: { url: '', whiteHostList: [ 'm.zhipin.com', 'www.zhipin.com', 'pre-www.zhipin.com' ], blackPathList: ['security-check.html', 'security-check1.html'] }, setStrategy: function () { var url = urlFilter.config.url; switch (true) { case urlFilter.isBlackHost(url) || urlFilter.hasBlackPath(url): urlFilter.config.url = '/'; break; } return urlFilter.config.url; }, isAbsolutePathStartable: function (url) { return url.indexOf('//') < 0 && url.indexOf('/') === 0; }, isBlackHost: function (url) { var isBlackHost = false; var rule = /^(https?)?(:?\/\/+)([^\/?]*)(.*)?$/; url.replace(rule, function (res, $1, $2, $3, $4) { isBlackHost = !urlFilter.isHostInWhiteList($3); console.error('hostname', $3, 'isBlackHost', isBlackHost); return isBlackHost ? '/' : url; }); return isBlackHost; }, hasBlackPath: function (url) { var isBlackPath = false; var blackPathList = urlFilter.config.blackPathList; for (var i = 0; i < blackPathList.length; i++) { if (url.indexOf(blackPathList[i]) > -1) { isBlackPath = true; break; } } return isBlackPath; }, isHostInWhiteList: function (hostname) { return urlFilter.config.whiteHostList.indexOf(hostname) > -1; }, filter: function (url) { urlFilter.config.url = url || '/'; return urlFilter.setStrategy(); } }; var jumpReplace = function (url) { var filterUrl = urlFilter.filter(url); if ( filterUrl.indexOf('napi') > -1 && filterUrl.indexOf('zpssr') > -1 ) { filterUrl = filterUrl.replace( /\/napi\/zpssr(site|youle|tools)/, '' ); } // console.log(filterUrl); cb(filterUrl); }; var jumpPage = function (srcReferer, callbackUrl) { if (callbackUrl || srcReferer.indexOf('security-check.html') > -1) { jumpReplace(callbackUrl); } else { jumpReplace(srcReferer); } var image = new Image(); image.src = 'https://t.zhipin.com/f.gif?pk=' + securityPageName + '&ca=securityCheckJump_' + Math.round((new Date().getTime() - pageStartTime) / 1000) + '&r=' + document.referrer; }; var seed = getQueryString('seed') || ''; var ts = getQueryString('ts') || ''; var fileName = getQueryString('name') || ''; var callbackUrl = getQueryString('callbackUrl') || ''; var srcReferer = getQueryString('srcReferer') || ''; if (fileName === 'null' || !seed || !fileName || !callbackUrl) { var image = new Image(); var paramsStr = ''; var lenSrcReferer = url.split('srcReferer').length - 1; var reportData = { appKey: url.indexOf('weizhipin') > -1 ? 'ed8d1b9d40a89f30ba721' : 'd071323e4304ae2931f11', errorType: 'collectData', errorCode: 'security_check_error', json: JSON.stringify({ url: url, fileName: fileName, seed: seed, ts: ts, callbackUrl: callbackUrl, srcReferer: srcReferer }) }; for (var key in reportData) { paramsStr += '&' + key + '=' + reportData[key]; } paramsStr = paramsStr.substr(1); image.src = 'https://t.kanzhun.com/z.json?' + paramsStr; } if (seed && ts && fileName) { jumpPage(srcReferer, callbackUrl); } } var ie = !!(window.attachEvent && !window.opera); var wk = /webkit\/(\d+)/i.test(navigator.userAgent) && RegExp.$1 < 525; var fn = []; var run = function () { for (var i = 0; i < fn.length; i++) fn[i](); }; })(); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址