bboss-insights-collector

a plugin focused on the behavior research of Boss job seekers

// ==UserScript==
// @name         bboss-insights-collector
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  a plugin focused on the behavior research of Boss job seekers
// @author       abin
// @match        https://www.zhipin.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_xmlhttpRequest
// @grant        unsafeWindow
// @grant        GM_addStyle
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function() {
    'use strict';
const BISVERSION = "1.0.0"
const MAX_RECORDS = 1000; // 最大记录数量
const DELETE_COUNT = 50; // 超过最大记录数时删除的数据条数
const BEATNUM = 60000;
let proInerNet = "https://172.27.2.58:9097";
let proOutNet = "https://111.202.197.150:9099";
let timer;
let IS_BOSSPAGE = false;
let isTargetPage = false;
let data = { stuId: "", isDisconnectReconnect: 'N', pluginsContentList: [] };
let itemContent = { act_id: "", act_tool: "chrome" }
let bossName, jobName, isNodeList;

console.log(GM_getValue('isLoggedIn'),'isLoggedInisLoggedInisLoggedIn')
GM_setValue('proIP',proOutNet);
GM_setValue('proInerNet', proInerNet);
GM_setValue('proOutNet', proOutNet);
GM_setValue('operateStatus',false );

const formatTime = (
  time,
  fmt
) => {
  if (!time) { return ''; }
  const date = new Date(time);
  const o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'H+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds(),
    'q+': Math.floor((date.getMonth() + 3) / 3),
    S: date.getMilliseconds(),
  };

  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(
      RegExp.$1,
      (date.getFullYear() + '').substr(4 - RegExp.$1.length)
    );
  }
  for (const k in o) {
    if (new RegExp('(' + k + ')').test(fmt)) {
      fmt = fmt.replace(
        RegExp.$1,
        // @ts-ignore: Unreachable code error
        RegExp.$1.length === 1 ? o[k] : ('00' + o[k]).substr(('' + o[k]).length)
      );
    }
  }
  return fmt;
};


  const db = indexedDB.open('bossDatabase', 1); // 名称 + 版本
	db.onerror = (event) => {
		console.error('数据库打开失败:', event.target.error);
	};
	db.onsuccess = (event) => {
		const db = event.target.result;
		console.log('数据库已打开:', db.name);
	};
	db.onupgradeneeded = (event) => {
		const db = event.target.result;
		if (!db.objectStoreNames.contains('')) {
			const store = db.createObjectStore('action', { keyPath: 'id_', autoIncrement: true });
		}
	};

      // 检查用户是否已经登录
    function checkLoginStatus() {
         const isLoggedIn = GM_getValue('isLoggedIn');
        if (isLoggedIn) {
              const username = GM_getValue('username');
              const usernum = GM_getValue('usernum');
            // 显示学号和用户名
            document.querySelector(".boss-script-login")?.remove();
            displayUserInfo(username, usernum);
        } else {
             document.querySelector(".boss-script-info")?.remove();
            // 如果没有登录信息,显示登录表单
            console.log('1111111')
            displayLoginForm();
        }
    }

    // 显示用户信息(学号和用户名)
    function displayUserInfo(username, usernum) {
      const Html = `
      	<div id="floating-window" class="floating-window boss-script-info">
		<div class="floating-window-content">
			<button id="minimize-btn">-</button>
                <div style="text-align: right; margin-right: 20px;">
                    <button id="logout-btn">注销</button>
                </div>
                   <h3 style="margin-top:20px; font-size: 16px;">八维教育 ${BISVERSION}</h3>
                  <form id="loginForm">
                    <div class="layui-form-item">
                        <label class="layui-form-label">学号</label>
                        <div class="layui-input-block">
                        ${usernum}
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <label class="layui-form-label">姓名</label>
                        <div class="layui-input-block">
                        ${username}
                        </div>
                    </div>
                </form>
		</div>
	</div>
	<button id="show-btn" class="show-btn">BOSS</button>
        `;
        document.body.insertAdjacentHTML('beforeend', Html);
        const logoutBtn = document.getElementById('logout-btn');
        const floatingWindow = document.getElementById('floating-window');
		const minimizeBtn = document.getElementById('minimize-btn');
		const showBtn = document.getElementById('show-btn');
        console.log(minimizeBtn,'minimizeBtn')
		// 点击“-”按钮,浮窗逐渐变小,直到按钮大小并消失
		minimizeBtn.addEventListener('click', () => {
			// 设置缩小效果
			floatingWindow.style.width = '50px';
			floatingWindow.style.height = '50px';
			floatingWindow.style.opacity = '0';
			floatingWindow.style.right = '0'; // 确保它停在按钮的位置

			// 隐藏浮窗并显示按钮
			setTimeout(() => {
				floatingWindow.style.display = 'none';
				showBtn.style.display = 'block';
			}, 500); // 延迟,以便看到缩小的过程
		});

		// 点击右侧按钮,浮窗恢复原始状态并逐渐变大
		showBtn.addEventListener('click', () => {
			// 先设置为小尺寸和透明,确保没有显示
			floatingWindow.style.display = 'block';
			floatingWindow.style.width = '50px';
			floatingWindow.style.height = '50px';
			floatingWindow.style.opacity = '0';
			floatingWindow.style.right = '0';

			// 显示按钮隐藏
			showBtn.style.display = 'none';

			// 使用 setTimeout 延迟启动动画
			setTimeout(() => {
				// 恢复浮窗尺寸和透明度
				floatingWindow.style.transition = 'all 0.5s ease-out'; // 应用过渡效果
				floatingWindow.style.width = '220px';
                   floatingWindow.style.height = '190px';
				floatingWindow.style.opacity = '1'; // 恢复可见
				floatingWindow.style.right = '0'; // 确保浮窗停在右侧
			}, 10); // 延迟0.01s启动动画
		});




        if (!logoutBtn) return;
        logoutBtn.addEventListener('click', async function (e) {
            let proInerNet;
            // 读取本地存储的用户信息
            const token = GM_getValue('token');
            const userid = GM_getValue('userid');
            const proIP = GM_getValue('proIP');
            proInerNet = GM_getValue('proInerNet');
            try {
                const response = await fetch(`${proOutNet}/logout`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${token}` // 添加 Authorization 头
                    },
                });
                // 处理响应
                const data = await response.json();
                if (data.code == 200) {
                    let dta = {};
                    dta.act_type = "logout";
                    dta.act_id = userid;
                    dta.act_tool = "chrome";
                    dta.act_time = formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S');
                    sendData(dta,'logout')
                    GM_deleteValue('username');
                    GM_deleteValue('token');
                    GM_deleteValue('usernum');
                    GM_setValue('isLoggedIn', false);
                // 跳转回登录页面
                 checkLoginStatus();
                } else {
                    throw new Error(data.msg || '退出登录失败');
                }
            } catch (error) {
                console.error(error, 'error');
                GM_setValue('proIP', proInerNet); // 恢复 proIP 值
                alert(error || '退出登录过程中发生错误');
            } finally {
                // 重置按钮状态
            }
        });
    }


 GM_addStyle(`
             #loginForm{
                 padding:15px 10px 0;
             }

            .layui-form-item {
                margin-bottom: 20px;
                display:flex;
                align-items:center
            }
            .layui-form-label{
               margin-right:10px;
               font-size:14px;
               width:43px;

            }

            .layui-input {
                border-radius: 5px;
                border: 1px solid #ddd;
                height: 25px;
                font-size: 14px;
                width:120px;
            }

            .layui-btn {
                width: 100%;
                height: 30px;
                background-color: #007bff;
                border-radius: 5px;
                color: white;
                font-size: 14px;
                border:none;
            }


            .layui-btn:hover {
                background-color: #409eff;
               cursor: pointer;
            }
            .form-btn{
            display:flex;
            justify-content:center;
                margin-bottom:10px;

            }
            ` )
// 动画效果:使用CSS过渡来平滑显示或隐藏浮窗
const style = document.createElement('style');
style.textContent = `
	   .floating-window {
            z-index:2000;
			position: fixed;
			right: 0;
			top: 148px;
			transform: translateY(-50%);
			width: 220px;
            height:190px;
			background-color: #fff;
			box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
			display: block;
			transition: all 0.5s ease-out; /* 应用过渡 */
			opacity: 1;
		}
        .floating-window h3 {
           margint-top:10px;
           text-align:center;
        }
        #logout-btn{
           color:#007bff;
           border:none;
           background:none;
           margin-right:10px;
        }
         #logout-btn:hover {
              color: #409eff;
              cursor: pointer;
         }
		.floating-window-content {
			padding: 10px;
		}
		#minimize-btn {
			font-size: 20px;
			border: none;
			color: #888;
			cursor: pointer;
			transition: transform 0.3s ease-in-out;
            width: 15px;
            height: 15px;
            border-radius: 50%;
            display:flex;
            justify-content:center;
            line-height: 13px;
            position: absolute;
            right: 15px;
            top: 15px;
		}
		#minimize-btn:hover {
			transform: scale(1.2); /* 按钮悬停时放大 */
		}
		.show-btn {
			position: fixed;
			right: 0;
			top: 200px;
			width: 50px;
			height: 50px;
			background-color: #007bff;
			color: white;
			font-size: 14px;
			border: none;
			border-radius: 50%;
			display: none;
			cursor: pointer;
            font-weight:600;
		}
`;

    // 显示登录表单
    function displayLoginForm() {
        const loginForm = `
        	<div id="floating-window" class="floating-window boss-script-login">
		<div class="floating-window-content">
			<button id="minimize-btn">-</button>
                <h3 style="margin: 0; font-size: 16px;">八维教育 ${BISVERSION}</h3>
                  <form id="loginForm">
                    <div class="layui-form-item">
                        <label class="layui-form-label">用户名</label>
                        <div class="layui-input-block">
                            <input type="text" autocomplete="username" id="loginName" name="username" required lay-verify="required" placeholder="请输入用户名" class="layui-input" />
                        </div>
                    </div>
                    <div class="layui-form-item">
                        <label class="layui-form-label">密码</label>
                        <div class="layui-input-block">
                            <input type="password" autocomplete="current-password" name="password" id="loginPassword" required lay-verify="required" placeholder="请输入密码" class="layui-input" />
                        </div>
                    </div>
                    <div class="layui-form-item form-btn">
                          <button type="button" id="layui-btn" class="layui-btn">登录</button>
                    </div>
                </form>
		</div>
	</div>
	<button id="show-btn" class="show-btn">BOSS</button> `;
          document.body.insertAdjacentHTML('beforeend', loginForm);
        const floatingWindow = document.getElementById('floating-window');
		const minimizeBtn = document.getElementById('minimize-btn');
		const showBtn = document.getElementById('show-btn');
		// 点击“-”按钮,浮窗逐渐变小,直到按钮大小并消失
		minimizeBtn?.addEventListener('click', () => {
			// 设置缩小效果
			floatingWindow.style.width = '50px';
			floatingWindow.style.height = '50px';
			floatingWindow.style.opacity = '0';
			floatingWindow.style.right = '0'; // 确保它停在按钮的位置

			// 隐藏浮窗并显示按钮
			setTimeout(() => {
				floatingWindow.style.display = 'none';
				showBtn.style.display = 'block';
			}, 500); // 延迟,以便看到缩小的过程
		});

		// 点击右侧按钮,浮窗恢复原始状态并逐渐变大
		showBtn?.addEventListener('click', () => {
			// 先设置为小尺寸和透明,确保没有显示
			floatingWindow.style.display = 'block';
			floatingWindow.style.width = '50px';
			floatingWindow.style.height = '50px';
			floatingWindow.style.opacity = '0';
			floatingWindow.style.right = '0';
			// 显示按钮隐藏
			showBtn.style.display = 'none';
			// 使用 setTimeout 延迟启动动画
			setTimeout(() => {
				// 恢复浮窗尺寸和透明度
				floatingWindow.style.transition = 'all 0.5s ease-out'; // 应用过渡效果
				floatingWindow.style.width = '220px';
                floatingWindow.style.height = '190px';
				floatingWindow.style.opacity = '1'; // 恢复可见
				floatingWindow.style.right = '0'; // 确保浮窗停在右侧
			}, 10); // 延迟0.01s启动动画
		});

      document.getElementById('layui-btn').addEventListener('click', async (e)=> {
         e.preventDefault();
        // 获取表单数据
        const username = document.getElementById('loginName').value;
        const password = document.getElementById('loginPassword').value;
        // 简单的客户端验证
        if (!username || !password) {
            alert('请输入用户名和密码');
            return;
        }

        const loginBtn = document.querySelector('.login-btn button');
        let proInerNet;
        proInerNet = GM_getValue('proInerNet');
        const proIP = GM_getValue('proIP');

        try {
            // 发送登录请求
            const response = await fetch(`${proIP}/login`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    username,
                    password
                }),
            });
            if (!response) {
                // 登录失败
                alert('登录过程中发生错误');
                return;
            }
            const data = await response.json();
            if (data.code == 200) {
                let dta = {};
                dta.act_type = "login";
                dta.act_time = formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S');
                dta.act_id = data.userid;
                dta.act_tool = "tampermonkey";

                // 存储登录数据
                GM_setValue('token', data.token);
                GM_setValue('usernum', data.usernum);
                GM_setValue('userid', data.userid);
                GM_setValue('username', data.username);
                GM_setValue('isLoggedIn', true);
                checkLoginStatus();
                // 登录成功
                  sendData(dta,'login')
            } else {
                // 登录失败
                alert(data.msg || '登录过程中发生错误');
            }
        } catch (error) {
            GM_setValue('proIP', proInerNet);
            alert(error || '登录过程中发生错误');
        } finally {
            // 重置按钮状态

        }
    });
   }



document.head.append(style);

    // 检查登录状态
    checkLoginStatus();

// 获取当前页面的完整 URL
const currentUrl = window.location.href;
// 判断当前页面的域名和路径
if (currentUrl.includes('https://www.zhipin.com/web/geek/recommend')) {
	isTargetPage = true;
	// 在这里执行你希望的操作
} else {
	isTargetPage = false;
}

window.addEventListener('beforeunload', function (event) {
	if (isTargetPage) updateData();
});
// 获取数据并更新存储
function updateData() {
	// 提取各项数据
	const communicationCount = document.querySelector('[ka="personal_top_added"] .count');
	const applyCount = document.querySelector('[ka="personal_top_submitted"] .count');
	const interviewCountDom = document.querySelector('[ka="personal_top_interview"] .count');
	// 获取 .resume-refresh-hwslider 下的 <svg> 元素
	const svg = document.querySelector('.resume-refresh-hwslider svg');
	// 获取最后一个 <g> 标签
	const lastGElement = svg && svg.querySelector('g:last-child');
	// 获取 .my-series 元素
	const exposureCountDom = lastGElement && lastGElement.querySelector('.my-series');
	let exposureCount;
	let interviewCount;
	let data = {};
	if (isTargetPage) {
		if (interviewCountDom) {
			interviewCount = parseInt(interviewCountDom.textContent);
		}
		if (exposureCountDom) {
			exposureCount = parseInt(exposureCountDom.textContent);
		}
        let userid = GM_getValue('userid') || '';
		data = [{ act_id: userid, act_tool: "chrome", act_type: 'active', act_time: formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S'), act_value: exposureCount }, { act_id: userid, act_tool: "chrome", act_type: 'interview', act_time: formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S'), act_value: interviewCount }]
        sendData(data,'active')
	}
}

// 监听“投递”按钮点击事件
function setupApplyButtonListener() {
	const applyButton = document.querySelector('.choose-resume-dialog .btn-confirm') || document.querySelector('.toolbar-btn-content .btn-v2.btn-sure-v2');
	if (applyButton) {
		applyButton.addEventListener('click', () => {
			// 检查按钮是否被禁用
			if (applyButton.disabled) {
                // 如果按钮被禁用,直接返回
				return;
			}
			let data = {};
            let userid = GM_getValue('userid') || '';
			const bossName = document.querySelectorAll('.base-info > *')[1]?.textContent;
			const jobName = document.querySelector('.position-name')?.textContent;
			data.act_type = "deliver";
			data.act_time = formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S');
			data.act_value = "1";
			data.act_company_name = bossName;
			data.act_station_name = jobName;
			data.act_id = userid;
			data.act_tool = "chrome";
            sendData(data,'deliver')
		});
	}
}


// 监听“沟通”按钮点击事件
function setupButtonClickListener() {
	let dom = document.querySelector('.job-detail-header .op-btn.op-btn-chat') || document.querySelector('.btn-container .btn.btn-startchat') || document.querySelectorAll('button.btn.btn-startchat') ||document.querySelectorAll('a.btn.btn-startchat');
	// 移除已有的事件监听器,防止重复绑定
    if (dom instanceof NodeList) {
		isNodeList = true;
		dom.forEach(button => {
			button.addEventListener('click', handleButtonClick);
		});
	} else {
		isNodeList = false;
		jobName = document.querySelector('.job-detail-box .job-name')?.textContent || document.querySelector('.job-primary .info-primary .name h1')?.textContent;
		dom?.removeEventListener('click', handleButtonClick);
		// 绑定新的事件监听器
		dom?.addEventListener('click', handleButtonClick);
	}

}

// 在页面跳转前发送数据
function handleButtonClick(event) {
	if (isNodeList){
		jobName = event.target.closest('li').querySelector('.info-primary .name b').textContent || event.target.closest('li').querySelector('.info-primary .name .job-title').textContent;
	}
	bossName = document.querySelector('.job-card-wrap.active .boss-name')?.textContent || document.querySelector('.info-primary .info h1')?.childNodes[0].textContent.trim() || document.querySelector('.sider-company .company-info a')?.title;;
	let data = {};
      let userid = GM_getValue('userid') || '';
	data.act_type = "communication";
	data.act_time = formatTime(new Date(), 'yyyy-MM-dd HH:mm:ss.S');
	data.act_value = "1";
	data.act_company_name = bossName;
	data.act_station_name = jobName;
	data.act_id = userid;
	data.act_tool = "chrome";
    sendData(data,'communication')
}

    // 初始化
    function init() {
         startTimer();
    }
    // 等待页面加载完毕后初始化
    window.addEventListener('load', init);

// 设置 MutationObserver 来确保页面加载后添加监听器
let tempTimer = null;
function setupObserver() {
	const observer = new MutationObserver(() => {
		// 检查监听器是否已经设置,如果没有设置,就设置它们
		// 每次 MutationObserver 回调触发时,清除之前的定时器
		clearTimeout(tempTimer);
		// 设置新的定时器,延迟 200 毫秒后执行某些逻辑
		tempTimer = setTimeout(() => {
			// 如果有多个变动,只有最后一次触发时才执行这里的逻辑
			setupApplyButtonListener();
			setupButtonClickListener();
		}, 0); // 延迟时间可以根据实际需要调整
	});
	const targetNode = document.body; // 观察整个页面
	if (targetNode) {
		observer.observe(targetNode, {
			childList: true,
			subtree: true
		});
	}
}
// 启动 MutationObserver 以确保动态加载的按钮也能正确绑定事件
setupObserver();
    // 发送数据
    async function request(info, isReconnect, type,isold) {
        try {
            const token = GM_getValue('token');
            const userid = GM_getValue('userid');
            const proIP = GM_getValue('proIP');
            let data = {};
            if (userid) {
                data.stuId = userid;
                data.isDisconnectReconnect = isReconnect;
                if (info instanceof Array) {
                    info.forEach(item => {
                        item.act_id = userid;
                    });
                    data.pluginsContentList = info;
                } else {
                    data.pluginsContentList = [{ ...info }];
                }
                // 发送请求
                const response = await fetch(`${proIP}/boos/plugins/saveJsonContent`, {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': `Bearer ${token}` // 添加 Authorization 头
                    },
                    body: JSON.stringify(data),
                });

                const res = await response.json();
                if (res.code === 200) {
                    GM_setValue('operateStatus', true);
                    if (type === 'login') {
                        startTimer();
                    }
                    if (type === 'logout') {
                        GM_setValue('token', null);
                        GM_setValue('usernum', '');
                        GM_setValue('userid', null);
                        GM_setValue('username', null);
                        GM_setValue('isLoggedIn', false);
                    }
                    if(isold){
                        // 打开数据库
                    const dbRequest = indexedDB.open('bossDatabase', 1);
                    dbRequest.onsuccess = (event) => {
                        const db = event.target.result;
                        const transaction = db.transaction('action', 'readwrite');
                        const store = transaction.objectStore('action');
                        const clearRequest = store.clear();
                        clearRequest.onsuccess = () => {
                        };
                        clearRequest.onerror = (event) => {
                        };
                      };
                    }

                }
            } else {
            }
        } catch (error) {
             GM_setValue('proIP', proInerNet);
        }
    }

     // 发送数据前处理
    async function sendData(data,type) {
        // 获取存储的 token 和 userid
        const token = GM_getValue('token');
        const userid = GM_getValue('userid');
        if (userid) {
            // 使用 request 替换为你自己的请求函数
            await request(data, 'N', type);
            if (type === 'login') {
                data.isDisconnectReconnect = 'Y';
                const dbRequest = indexedDB.open('bossDatabase', 1);
                dbRequest.onsuccess = async (event) => {
                    const db = event.target.result;
                    await getAllUsers(db);
                };
            }
        } else {
            const dbRequest = indexedDB.open('bossDatabase', 1);
            dbRequest.onsuccess = (event) => {
                const db = event.target.result;
                addAction(db, data);
            };
        }
        return true;
    }


    // 添加数据到 IndexedDB
    function addAction(db, data) {
        const transaction = db.transaction('action', 'readwrite');
        const store = transaction.objectStore('action');
        const countRequest = store.count();
        countRequest.onsuccess = () => {
            const currentCount = countRequest.result;
            if (currentCount >= MAX_RECORDS) {
                deleteOldRecords(store, DELETE_COUNT, () => {
                    insertData(store, data);
                });
            } else {
                insertData(store, data);
            }
        };
        countRequest.onerror = (event) => {
            console.error('获取记录数失败:', event.target.error);
        };
    }

    // 删除旧记录
    function deleteOldRecords(store, deleteCount) {
        const cursorRequest = store.openCursor();
        let deletedCount = 0;
        cursorRequest.onsuccess = (event) => {
            const cursor = event.target.result;
            if (cursor && deletedCount < deleteCount) {
                cursor.delete();
                deletedCount++;
                cursor.continue();
            }
        };
        cursorRequest.onerror = (event) => {
            console.error('删除记录失败:', event.target.error);
        };
    }

    // 插入新数据
    function insertData(store, data) {
        if (Array.isArray(data)) {
            data.forEach(item => store.add(item));
        } else {
            const addRequest = store.add(data);
            addRequest.onsuccess = () => {
                  const getAllRequest = store.getAll();
            getAllRequest.onsuccess = () => {
            };
            getAllRequest.onerror = (event) => {
                console.error('获取数据失败:', event.target.error);
            };
           };
            addRequest.onerror = (event) => {
                console.error('插入数据失败:', event.target.error);
            };
        }
    }
    // 获取所有离线数据
    async function getAllUsers(db) {
       const transaction = db.transaction('action', 'readonly');
	   const store = transaction.objectStore('action');
	   const arr = store.getAll();
	   arr.onsuccess = async (event) => {
		  const cursor = event.target.result;
		if (cursor.length) {
			await request(cursor, 'Y', 'login','old')
		 }
	  };
	  arr.onerror = function (event) {
	  	console.error('获取数据失败:', event.target.error);
	  };
    }
     //心跳
     function startTimer() {
        if (!timer) {
           timer = setInterval(async () => {
                 // 获取存储的值
                 const active = GM_getValue('active');
                 const token = GM_getValue('token');
                 const userid = GM_getValue('userid');
                 const proIP = GM_getValue('proIP');
                 const operateStatus = GM_getValue('operateStatus');

                 if (userid) {
                     try {
                         if (token) {
                             // 使用 fetch 发送心跳请求
                             const response = await fetch(`${proIP}/boos/plugins/heartbeat`, {
                                 method: 'POST',
                                 headers: {
                                     'Content-Type': 'application/json',
                                     'Authorization': `Bearer ${token}` // 添加 Authorization 头
                                 },
                                 body: JSON.stringify({
                                     stuId: userid,
                                     operateStatus
                                 })
                             });
                             if (response.code==200) {
                                 // 请求成功后的处理
                                 GM_setValue('operateStatus', false); // 将 operateStatus 设置为 false
                             }
                         }
                     } catch (error) {
                          GM_setValue('proIP', proInerNet);
                   }
               }

           }, BEATNUM);

        }
    }


})();