// ==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);
})();