主版本-template
说明
张院每次登录网络都需要二次输入信息,非常麻烦而且手机号码也不一定记得住
最初版本使用
ScriptCat制作
将ScriptCat版本迁移为 Go 语言 + vue3 实现,并制作成模板方便拓展。
ScriptCat 版本
// ==UserScript==
// @name 校园网直连
// @namespace https://github.com/fromsko
// @version 0.1.1
// @description 用于张家界学院校园网直连的自动脚本
// @author Fromsko
// @match http://172.18.2.6/*
// @connect 172.18.2.6
// @connect https://unpkg.com
// @grant GM_xmlhttpRequest
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addValueChangeListener
// @grant GM_info
// @grant unsafeWindow
// @grant GM_log
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_openInTab
// @grant GM_notification
// @grant GM_updateNotification
// @grant GM_registerMenuCommand
// @run-at doc-start
// ==/UserScript==
let loginStateInfo = GM_getValue('loginState', {})
let userInfo = {
username: '10086',
password: '111',
client: '联通',
}
const defaultConfig = {
setLoginParams (userInfo) {
let random = function () {
return Math.floor(Math.random() * 10000 + 500)
}
const params = new URLSearchParams()
switch (userInfo.client) {
case '移动':
userInfo.client = '@cmcc'
break
case '联通':
userInfo.client = '@unicom'
break
case '电信':
userInfo.client = '@telecom'
case '教职工':
userInfo.client = ''
break
}
params.append('callback', 'dr1003')
params.append('DDDDD', userInfo.username + userInfo.client)
params.append('upass', userInfo.password)
params.append('0MKKey', '123456')
params.append('R1', '0')
params.append('R2', '')
params.append('R3', '0')
params.append('R6', '0')
params.append('para', '00')
params.append('v6ip', '')
params.append('terminal_type', '1')
params.append('lang', 'zh-cn')
params.append('jsVersion', '4.2')
params.append('v', random())
params.append('lang', 'zh')
return params.toString()
},
agent: 'Dalvik/2.1.0 (Linux; U; Android 11; M3121K1AB Build/SKQ1.211006.001) (device:M3121K1AB) Language/zh_CN',
host: '172.18.2.6'
}
const useRequest = (config = defaultConfig) => {
let { setLoginParams, host, agent } = config
let request = (url, data = '', type = 'get') => {
return new Promise((resolve, reject) => {
var headers = {
'User-Agent': agent,
}
GM_xmlhttpRequest({
method: type,
url: url,
data: data,
headers: headers,
onload: function (xhr) {
try {
resolve(obj)
} catch (err) {
resolve(xhr.responseText)
}
},
})
})
}
let parserResp = (response) => {
const jsonString = response.match(/\((.*)\)/)[1]
const jsonResponse = JSON.parse(jsonString)
let result = { code: 400, msg: '登录失败', data: { v46ip: '', uid: '' } }
if (jsonResponse.result === 1) {
result.code = 200
result.msg = '登录成功'
result.data.v46ip = jsonResponse.v46ip
result.data.uid = jsonResponse.uid
} else {
result.data.v46ip = jsonResponse.ss5
}
return result
}
return {
parserResp,
async login (userInfo) {
return await request(`http://${host}/drcom/login?${setLoginParams(userInfo)}`)
},
async logout () {
return await request(`http://${host}/drcom/logout?callback=dr1003&jsVersion=4.2&v=9074&lang=zh`)
},
}
}
// 日志封装
const useScriptLogger = (title, content, level = 'info') => {
const scriptName = GM_info.script.name
GM_log(scriptName, level, { script_log: { [title]: content } })
}
// 浏览器控制台颜色日志
const useWebColorLogger = () => {
const isProduction = false
const isEmpty = (value) => {
return value == null || value === undefined || value === ''
}
const prettyPrint = (title, text, color) => {
if (isProduction) return
console.log(
`%c ${title} %c ${text} %c`,
`background:${color};border:1px solid ${color}; padding: 1px; border-radius: 2px 0 0 2px; color: #fff;`,
`border:1px solid ${color}; padding: 1px; border-radius: 0 2px 2px 0; color: ${color};`,
'background:transparent'
)
}
const info = (textOrTitle, content = '') => {
const title = isEmpty(content) ? 'Info' : textOrTitle
const text = isEmpty(content) ? textOrTitle : content
prettyPrint(title, text, '#909399')
}
const error = (textOrTitle, content = '') => {
const title = isEmpty(content) ? 'Error' : textOrTitle
const text = isEmpty(content) ? textOrTitle : content
prettyPrint(title, text, '#F56C6C')
}
const warning = (textOrTitle, content = '') => {
const title = isEmpty(content) ? 'Warning' : textOrTitle
const text = isEmpty(content) ? textOrTitle : content
prettyPrint(title, text, '#E6A23C')
}
const success = (textOrTitle, content = '') => {
const title = isEmpty(content) ? 'Success ' : textOrTitle
const text = isEmpty(content) ? textOrTitle : content
prettyPrint(title, text, '#67C23A')
}
const table = () => {
const data = [
{ id: 1, name: 'Alice', age: 25 },
{ id: 2, name: 'Bob', age: 30 },
{ id: 3, name: 'Charlie', age: 35 }
]
console.log(
'%c id%c name%c age',
'color: white; background-color: black; padding: 2px 10px;',
'color: white; background-color: black; padding: 2px 10px;',
'color: white; background-color: black; padding: 2px 10px;'
)
data.forEach((row) => {
console.log(
`%c ${row.id} %c ${row.name} %c ${row.age} `,
'color: black; background-color: lightgray; padding: 2px 10px;',
'color: black; background-color: lightgray; padding: 2px 10px;',
'color: black; background-color: lightgray; padding: 2px 10px;'
)
})
}
const picture = (url, scale = 1) => {
if (isProduction) return
const img = new Image()
img.crossOrigin = 'anonymous'
img.onload = () => {
const c = document.createElement('canvas')
const ctx = c.getContext('2d')
if (ctx) {
c.width = img.width
c.height = img.height
ctx.fillStyle = 'red'
ctx.fillRect(0, 0, c.width, c.height)
ctx.drawImage(img, 0, 0)
const dataUri = c.toDataURL('image/png')
console.log(
`%c sup?`,
`font-size: 1px;
padding: ${Math.floor((img.height * scale) / 2)}px ${Math.floor((img.width * scale) / 2)}px;
background-image: url(${dataUri});
background-repeat: no-repeat;
background-size: ${img.width * scale}px ${img.height * scale}px;
color: transparent;
`
)
}
}
img.src = url
}
return {
info,
error,
warning,
success,
picture,
table
}
}
const SelectPage = () => {
let logger = useWebColorLogger()
let { login, logout, parserResp } = useRequest()
let handleEvent = (view, callback = () => { }) => {
let removeHtmlStyle = () => {
let htmlElement = document.documentElement
htmlElement.removeAttribute('style')
document.body.style.margin = '0'
document.body.style.display = 'flex'
document.body.style.alignItems = 'center'
document.body.style.justifyContent = 'center'
document.body.style.backgroundColor = 'skyblue'
}
let timer = setTimeout(function () {
document.body.innerHTML = view
callback()
removeHtmlStyle()
clearTimeout(timer)
}, 500)
}
switch (document.title) {
case '注销页':
handleEvent(
`
<div class="login-container" style="display: flex; align-items: center; justify-content: center; height: 100vh; width: 250px; min-height: 100vh; backdrop-filter: blur(10px);">
<div class="login-box" style="display: flex; flex-direction: column; background-color: rgba(255, 255, 255, 0.8); padding: 20px; border-radius: 10px; align-items: center;">
<div style="text-align: center; margin-bottom: 20px; font-size: 20px;">登录成功</div>
<div style="text-align: center; margin-bottom: 10px;">账户: ${loginStateInfo.data.uid}</div>
<div style="text-align: center; margin-bottom: 10px; margin-top: 5px;">IP地址: ${loginStateInfo.data.v46ip}</div>
<button type="button" id="loginBtn" style="padding: 10px 20px; border-radius: 5px; border: none; outline: none; background-color: #3498db; color: #fff; cursor: pointer;">注销</button>
</div>
</div>
`,
() => {
const loginBtn = document.getElementById('loginBtn')
loginBtn.onclick = async () => {
await logout()
GM_setValue('loginState', { code: 400, data: { v46ip: 'localhost', uid: 'null' } })
window.location.reload()
}
}
)
break
case '信息页':
handleEvent(
`
<div class="login-container" style="display: flex; align-items: center; justify-content: center; height: 100vh; width: 250px; min-height: 100vh; backdrop-filter: blur(10px);">
<div class="login-box" style="display: flex; background-color: rgba(255, 255, 255, 0.8); padding: 20px; border-radius: 10px; flex-wrap: wrap; align-content: center; justify-content: space-around;">
<img src="http://q1.qlogo.cn/g?b=qq&nk=1614355756&s=100" alt="Login" class="logo" style="width: 64px; height: 64px; margin-bottom: 5px; border-radius: 50%;">
<form>
<div class="form-group">
<label for="qqNumber" style="font-weight: bold;">账号</label>
<input type="text" id="qqNumber" placeholder="请输入账号" style="width: 100%; padding: 5px; border-radius: 5px; margin-top: 8px; border: 1px solid #ccc;">
</div>
<div class="form-group">
<label for="password" style="font-weight: bold;">密码</label>
<input type="password" id="password" placeholder="请输入密码" style="width: 100%; padding: 5px; border-radius: 5px; margin-top: 8px; border: 1px solid #ccc;">
</div>
<div class="button-group" style="display: flex; margin-top: 10px; margin-bottom: 8px; justify-content: center;">
<button type="button" onclick="javascript:wc()" style="padding: 10px 20px; border-radius: 5px; border: none; outline: none; background-color: #3498db; color: #fff; cursor: pointer; margin: 0 8px;">移动</button></button>
<button type="button" onclick="javascript:wc()" style="padding: 10px 20px; border-radius: 5px; border: none; outline: none; background-color: #3498db; color: #fff; cursor: pointer; margin: 0 8px;">联通</button>
<button type="button" onclick="javascript:wc()" style="padding: 10px 20px; border-radius: 5px; border: none; outline: none; background-color: #3498db; color: #fff; cursor: pointer; margin: 0 8px;">电信</button>
<button type="button" onclick="javascript:wc()" style="padding: 10px 20px; border-radius: 5px; border: none; outline: none; background-color: #3498db; color: #fff; cursor: pointer; margin: 0 8px;">教师</button>
</div>
</form>
</div>
</div>
`
)
break
case '上网登录页':
handleEvent(
`
<div class="login-container" style="display: flex; align-items: center; justify-content: center; height: 100vh; width: 250px; min-height: 100vh; backdrop-filter: blur(10px);">
<div class="login-box" style="display: flex; flex-direction: column; background-color: rgba(255, 255, 255, 0.8); padding: 20px; border-radius: 10px; align-items: center;">
<div style="text-align: center; margin-bottom: 20px;">校园网一键登录</div>
<button type="button" id="loginBtn" style="padding: 10px 20px; border-radius: 5px; border: none; outline: none; background-color: #3498db; color: #fff; cursor: pointer;">登录</button>
</div>
</div>
`,
() => {
const loginBtn = document.getElementById('loginBtn')
loginBtn.onclick = async () => {
let resp = await login(userInfo)
let data = parserResp(resp)
if (data.code === 200) {
GM_setValue('loginState', data)
window.location.reload()
} else {
alert(
`${data.msg} 请检查账户信息是否正确!`
)
logger.error(JSON.stringify(data))
}
}
}
)
break
}
GM_registerMenuCommand('登录信息', () => {
logger.success(JSON.stringify(loginStateInfo))
})
GM_registerMenuCommand('颜色日志', () => {
logger.info("这是一条测试信息")
logger.success("这是一条成功信息")
logger.error("这是一条错误信息")
logger.error("{'code': 200, 'msg': '错误!'}")
logger.table()
})
}
const InitResource = () => {
var link = document.createElement('link')
link.rel = 'stylesheet'
link.type = 'text/css'
link.href = 'https://unpkg.com/layui@2.9.16/dist/css/layui.css'
document.head.appendChild(link)
}
(async () => {
'use strict'
// InitResource()
SelectPage()
console.clear()
})()
/*
业务需求:
1. 登录 (username, password, clientModel)
2. 退出
界面设计:
1. 登录页
2. 注册页
3. 笔记展示
注意点:
用户数据能够存储 (本地)
设计:
- 请求封装 useRequest()
- 登录 login(userInfo): respData
- respData => {msg: string, data: any, code: number}
- userInfo => {userName, passWord, model}
- 退出 logout(): respData
- 数据存储 saveUserInfo(userInfo): boolean
- 数据解析 parserResp(resp: string): resultData
- 日志输出 webLogOutput(stdio)
*/