Skip to content

主版本-template

Choose a tag to compare

@Fromsko Fromsko released this 24 Sep 12:53
· 57 commits to main since this release

说明

张院每次登录网络都需要二次输入信息,非常麻烦而且手机号码也不一定记得住

最初版本使用 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)
*/