// ==UserScript== // @name 亳州学院校园网自动登录 // @namespace http://scriptcat.org/ // @version 2.2 // @description ✨ 自动检测校园网登录页面,自动填充账号密码并登录,支持多账号、断网自动重连、后台定时检测 | 🎯 专为亳州学院gWiFi打造 | 💬 微信:daishuawangke88 // @author 微信:daishuawangke88 // @match http://10.15.1.4/* // @match http://10.15.1.4/gportal/* // @match *://*.baidu.com/* // @match *://*.google.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @grant GM_openInTab // @grant GM_notification // @grant GM_xmlhttpRequest // @connect * // @run-at document-start // @license MIT // ==/UserScript== /** * ╔══════════════════════════════════════════════════════════════╗ * ║ 🎓 亳州学院校园网自动登录脚本 v2.2 ║ * ╠══════════════════════════════════════════════════════════════╣ * ║ ✨ 功能特性: ║ * ║ ✅ 自动检测登录页面并登录 ║ * ║ ✅ 支持保存多个账号(最多9个) ║ * ║ ✅ 断网自动打开登录页面并登录 ║ * ║ ✅ 支持脚本猫后台定时任务 ║ * ║ ✅ 登录状态准确检测 ║ * ╠══════════════════════════════════════════════════════════════╣ * ║ 💖 觉得好用?欢迎请作者喝杯奶茶! ║ * ║ 📱 微信:daishuawangke88 ║ * ║ 🌟 您的支持是我持续更新的动力! ║ * ╚══════════════════════════════════════════════════════════════╝ * * 📢 【脚本猫用户】设置后台定时任务: * 1️⃣ 点击脚本猫图标 → 找到本脚本 → 点击设置 * 2️⃣ 添加定时任务:选择"后台脚本" * 3️⃣ 设置执行频率(建议每5-10分钟) * 4️⃣ 脚本会自动检测网络并在断网时打开登录页 * * 💬 问题反馈/功能建议/技术交流:微信 daishuawangke88 */ (function () { 'use strict'; // ============================================= // 🎯 常量配置 // ============================================= const CONFIG = { LOGIN_URL: 'http://10.15.1.4/gportal/web/login', MAX_ACCOUNTS: 9, CHECK_TIMEOUT: 5000, LOGIN_WAIT: 3000, PAGE_LOAD_WAIT: 2000, AUTHOR_WECHAT: 'daishuawangke88', }; // ============================================= // 💬 鼓励语录库 // ============================================= const QUOTES = { success: [ '🎉 太棒了!登录成功!今天也要元气满满哦~', '✨ 哇塞!顺利连网!你的运气正在加载中...', '🚀 登录成功!冲鸭!今天又是充满希望的一天!', '🌟 搞定!网络已连接,你的梦想也在同步加载!', '💪 登录成功!每一次连接都是新的开始!', '🎊 恭喜!顺利上线!今天也会是幸运的一天!', '🔥 完美!网络畅通无阻,你也是!', '💖 登录成功!愿你的每一天都像网络一样顺畅!', ], failure: [ '💪 跌倒了就爬起来!人生如此,网络也如此~再试一次!', '🌈 别灰心!风雨过后总会有彩虹,断网之后总会有连接!', '🔥 失败是成功之母,这次不行下次一定行!', '🌟 没关系!每个困难都是成长的机会!', '🦋 蝴蝶破茧前也要挣扎,成功前的困难都是礼物!', '💫 黑暗之后是黎明,断网之后是连接!', '🌻 向日葵也会遇到阴天,但总会等到阳光!', '⚡ 每一次尝试都是进步,继续加油!', ], networkOk: [ '🌐 网络畅通无阻!今天也要好好上网哦~', '✅ 网络状态良好!冲冲冲!', '📶 信号满格!你的好心情也是!', ], networkFail: [ '📡 网络好像开小差了...正在帮你召唤登录页面!', '🔌 哎呀,网络走丢了!别急,马上帮你找回!', '🤔 网络君似乎在休息,马上叫醒它!', ], }; // 随机获取语录 function getRandomQuote(type) { const quotes = QUOTES[type] || QUOTES.success; return quotes[Math.floor(Math.random() * quotes.length)]; } // 生成带联系信息的完整消息 function makeMessage(quote, showContact = true) { if (showContact) { return `${quote}\n\n💖 觉得脚本好用?欢迎微信联系:${CONFIG.AUTHOR_WECHAT}\n🍵 请作者喝杯奶茶,支持持续更新!`; } return quote; } // ============================================= // 📦 存储管理 // ============================================= const Storage = { get(key, defaultValue = null) { try { return GM_getValue(key, defaultValue); } catch (e) { console.error('[存储读取失败]', e); return defaultValue; } }, set(key, value) { try { GM_setValue(key, value); return true; } catch (e) { console.error('[存储写入失败]', e); return false; } }, getAccounts() { return this.get('accounts', []) || []; }, saveAccounts(accounts) { return this.set('accounts', accounts); }, getCurrentIndex() { return this.get('currentAccountIndex', 0); }, setCurrentIndex(index) { return this.set('currentAccountIndex', index); }, getCurrentAccount() { const accounts = this.getAccounts(); const index = this.getCurrentIndex(); return accounts[index] || null; }, getStats() { return this.get('stats', { loginCount: 0, lastLogin: '从未', lastCheck: '从未' }); }, updateStats() { const stats = this.getStats(); stats.loginCount++; stats.lastLogin = new Date().toLocaleString(); this.set('stats', stats); return stats; }, }; // ============================================= // 📝 日志工具 // ============================================= const Log = { prefix: '[亳州学院校园网]', info(msg) { console.log(`${this.prefix} ℹ️ ${new Date().toLocaleTimeString()} ${msg}`); }, success(msg) { console.log(`${this.prefix} ✅ ${new Date().toLocaleTimeString()} ${msg}`); }, warn(msg) { console.warn(`${this.prefix} ⚠️ ${new Date().toLocaleTimeString()} ${msg}`); }, error(msg) { console.error(`${this.prefix} ❌ ${new Date().toLocaleTimeString()} ${msg}`); }, }; // ============================================= // 🌐 网络检测工具 // ============================================= const Network = { async check() { if (typeof GM_xmlhttpRequest !== 'undefined') { return this.checkWithGM(); } return this.checkWithFetch(); }, checkWithGM() { return new Promise((resolve) => { const timeout = setTimeout(() => resolve(false), CONFIG.CHECK_TIMEOUT); try { GM_xmlhttpRequest({ method: 'GET', url: 'http://www.baidu.com/favicon.ico', timeout: CONFIG.CHECK_TIMEOUT, onload: () => { clearTimeout(timeout); resolve(true); }, onerror: () => { clearTimeout(timeout); resolve(false); }, ontimeout: () => { clearTimeout(timeout); resolve(false); }, }); } catch (e) { clearTimeout(timeout); resolve(false); } }); }, checkWithFetch() { return new Promise((resolve) => { const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), CONFIG.CHECK_TIMEOUT); fetch('http://www.baidu.com/favicon.ico', { mode: 'no-cors', signal: controller.signal, cache: 'no-store', }).then(() => { clearTimeout(timeout); resolve(true); }).catch(() => { clearTimeout(timeout); resolve(false); }); }); }, openLoginPage() { Log.info('🚪 正在打开登录页面...'); try { if (typeof GM_openInTab !== 'undefined') { GM_openInTab(CONFIG.LOGIN_URL, { active: true }); } else { window.open(CONFIG.LOGIN_URL, '_blank'); } const quote = getRandomQuote('networkFail'); this.sendNotification('🔌 网络断开了', `${quote}\n\n💬 微信:${CONFIG.AUTHOR_WECHAT}`); return true; } catch (e) { Log.error('打开登录页面失败: ' + e.message); return false; } }, sendNotification(title, text) { try { if (typeof GM_notification !== 'undefined') { GM_notification({ title: title, text: text, timeout: 8000, }); } } catch (e) { } }, }; // ============================================= // 🔐 登录功能 // ============================================= const Login = { usernameSelectors: [ '#username', 'input[name="username"]', 'input[name="user"]', 'input[name="account"]', 'input[name="userName"]', 'input[name="user_name"]', 'input[type="text"][placeholder*="用户"]', 'input[type="text"][placeholder*="学号"]', 'input[type="text"][placeholder*="账号"]', 'input[type="text"][placeholder*="手机"]', 'input[id*="user" i]', 'input[id*="account" i]', '.username-input', '.user-input', 'input[type="text"]', ], passwordSelectors: [ '#password', 'input[name="password"]', 'input[name="pass"]', 'input[name="pwd"]', 'input[name="userPwd"]', 'input[type="password"]', 'input[id*="pass" i]', 'input[id*="pwd" i]', '.password-input', ], buttonSelectors: [ '#login', '#btnLogin', '#login-btn', '#submit', 'button[type="submit"]', 'input[type="submit"]', 'button.login-btn', 'a.login-btn', '.login-btn', 'button.btn-primary', 'button[class*="login" i]', 'input[value*="登录"]', 'input[value*="Login"]', ], isOnLoginPage() { return location.href.includes('10.15.1.4'); }, async checkLoginStatus() { if (!this.isOnLoginPage()) { return true; } const bodyText = document.body?.innerText || ''; const successKeywords = ['登录成功', '上线成功', '已在线', '欢迎', '成功登', '在线时长']; for (const keyword of successKeywords) { if (bodyText.includes(keyword)) { return true; } } await this.sleep(500); const hasPasswordInput = document.querySelector('input[type="password"]'); if (!hasPasswordInput && document.readyState === 'complete') { return await Network.check(); } return false; }, async execute(showAlert = false) { const account = Storage.getCurrentAccount(); if (!account) { Log.warn('❌ 未配置账号'); if (showAlert) { alert(`⚠️ 请先添加账号!\n\n点击油猴图标 → 添加账号\n\n💬 微信:${CONFIG.AUTHOR_WECHAT}`); } return { success: false, reason: 'no_account' }; } if (!this.isOnLoginPage()) { Log.info('📍 不在登录页面'); return { success: false, reason: 'not_login_page' }; } if (document.readyState !== 'complete') { await new Promise(r => window.addEventListener('load', r)); } await this.sleep(CONFIG.PAGE_LOAD_WAIT); // 检查是否已登录 if (await this.checkLoginStatus()) { Log.success('✅ 已经登录'); this.updateUI('✅ 已登录'); if (showAlert) { const quote = getRandomQuote('success'); alert(makeMessage(quote)); } return { success: true, reason: 'already_logged_in' }; } // 查找表单元素 const usernameInput = this.findElement(this.usernameSelectors); const passwordInput = this.findElement(this.passwordSelectors); const loginButton = this.findLoginButton(); Log.info(`🔍 表单检测: 用户名=${!!usernameInput}, 密码=${!!passwordInput}, 按钮=${!!loginButton}`); if (!usernameInput || !passwordInput) { Log.error('❌ 未找到登录表单'); this.updateUI('❌ 未找到表单'); if (showAlert) { const quote = getRandomQuote('failure'); alert(`❌ 未找到登录表单\n\n${quote}\n\n💬 问题反馈微信:${CONFIG.AUTHOR_WECHAT}`); } return { success: false, reason: 'form_not_found' }; } // 填充账号密码 this.setInputValue(usernameInput, account.username); this.setInputValue(passwordInput, account.password); Log.info(`📝 已填充账号: ${account.name} (${account.username.substring(0, 3)}***)`); // 提交登录 if (loginButton) { loginButton.click(); Log.info('🔘 已点击登录按钮'); } else { const form = usernameInput.closest('form') || document.querySelector('form'); if (form) { form.submit(); Log.info('📋 已提交表单'); } else { passwordInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter', keyCode: 13, bubbles: true })); Log.info('⌨️ 已模拟回车提交'); } } this.updateUI('🔄 登录中...'); // 等待登录结果 await this.sleep(CONFIG.LOGIN_WAIT); // 验证结果 const success = await this.checkLoginStatus(); if (success) { Log.success('🎉 登录成功!'); this.updateUI('🎉 登录成功'); Storage.updateStats(); if (showAlert) { const quote = getRandomQuote('success'); alert(makeMessage(quote)); } return { success: true, reason: 'login_success' }; } else { Log.warn('⏳ 登录状态待确认'); this.updateUI('⏳ 请手动确认'); if (showAlert) { const quote = getRandomQuote('failure'); alert(`⏳ 登录状态待确认,请手动检查\n\n${quote}\n\n💬 微信:${CONFIG.AUTHOR_WECHAT}`); } return { success: true, reason: 'pending_confirm' }; } }, findElement(selectors) { for (const sel of selectors) { try { const el = document.querySelector(sel); if (el) { const style = window.getComputedStyle(el); if (style.display !== 'none' && style.visibility !== 'hidden') { return el; } } } catch (e) { } } return null; }, findLoginButton() { let btn = this.findElement(this.buttonSelectors); if (btn) return btn; const elements = document.querySelectorAll('button, input[type="submit"], input[type="button"], a, [role="button"], .btn'); for (const el of elements) { const text = (el.textContent || el.value || '').trim(); if (text.includes('登录') || text.includes('Login') || text === '登 录') { return el; } } return null; }, setInputValue(input, value) { const nativeSetter = Object.getOwnPropertyDescriptor(HTMLInputElement.prototype, 'value')?.set; if (nativeSetter) { nativeSetter.call(input, value); } else { input.value = value; } ['input', 'change', 'blur'].forEach(event => { input.dispatchEvent(new Event(event, { bubbles: true })); }); }, updateUI(status) { Storage.set('lastStatus', status); Storage.set('lastCheck', new Date().toLocaleString()); const statusEl = document.getElementById('campus-login-status-text'); if (statusEl) { statusEl.textContent = status; } }, sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }, }; // ============================================= // 🎨 状态指示器UI // ============================================= function addStatusIndicator() { if (!Login.isOnLoginPage()) return; if (document.getElementById('campus-login-status-box')) return; const div = document.createElement('div'); div.id = 'campus-login-status-box'; div.innerHTML = `