ChatGPT AccessToken 自动更新

根据token过期时间自动获取accessToken并发送POST请求后自动跳转到new.oaifree.com

目前為 2024-08-28 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         ChatGPT AccessToken 自动更新
// @namespace    http://tampermonkey.net/
// @version      2.9
// @description  根据token过期时间自动获取accessToken并发送POST请求后自动跳转到new.oaifree.com
// @author       AMT
// @match        *://new.oaifree.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// @connect      chatgpt.com
// @connect      new.oaifree.com
// @license      MIT
// ==/UserScript==


(function() {
    'use strict';

    // 定义ChatGPT登录页面的URL
    const chatgptLoginUrl = "https://chatgpt.com";
    // 定义获取accessToken的URL
    const tokenUrl = "https://chatgpt.com/api/auth/session";
    // 定义POST请求的目标URL
    const postUrl = "https://new.oaifree.com/auth/login_token";
    // 定义跳转的目标URL
    const redirectUrl = "https://new.oaifree.com";
    // 定义GM存储的key
    const expiresKey = 'tokenExpires'; // 保存token过期时间的key
    // 获取当前时间的时间戳(毫秒)
    let currentTime = new Date().getTime();
    // 将延迟时间存储到GM存储中
    let delay = GM_getValue('delay', 60000);
    // 从GM存储获取token过期时间
    let expires = GM_getValue(expiresKey, 0);

    // 计算距离token过期的时间
    function calculateTimeUntilExpiry() {
        currentTime = new Date().getTime();
        const timeUntilExpiry = expires - currentTime;

        // 计算剩余时间的各个部分
        const daysUntilExpiry = Math.floor(timeUntilExpiry / (24 * 60 * 60 * 1000));
        const hoursUntilExpiry = Math.floor((timeUntilExpiry % (24 * 60 * 60 * 1000)) / (60 * 60 * 1000));
        const minutesUntilExpiry = Math.floor((timeUntilExpiry % (60 * 60 * 1000)) / (60 * 1000));
        const secondsUntilExpiry = Math.floor((timeUntilExpiry % (60 * 1000)) / 1000);

        // 返回各个部分的时间和秒数
        return { daysUntilExpiry, hoursUntilExpiry, minutesUntilExpiry, secondsUntilExpiry };
    }
    // Base64URL 解码
    function base64UrlDecode(str) {
        return decodeURIComponent(atob(str.replace(/-/g, '+').replace(/_/g, '/')).split('').map(function(c) {
            return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
        }).join(''));
    }

    // 解析 JWT
    function parseJWT(token) {
        const parts = token.split('.');
        if (parts.length !== 3) {
            console.error('Invalid JWT token');
            return null;
        }

        const payload = JSON.parse(base64UrlDecode(parts[1])); // 仅解析payload部分
        return payload;
    }
    //刷新时间
    // 创建可视化窗口
    const panel = document.createElement('div');
    panel.id = 'script-panel';
    const { daysUntilExpiry, hoursUntilExpiry, minutesUntilExpiry } = calculateTimeUntilExpiry();
    panel.innerHTML = `
    <div id="panel-content">
        <p>距离AccessToken过期还有:<br>
        <span id="time-until-expiry"></span></p>
        <button id="run-script-button">立即获取AccessToken并POST</button>
    </div>
    `;
    document.body.appendChild(panel);
    updateDisplay()
    // 添加样式
    GM_addStyle(`
        #script-panel {
            position: fixed;
            top: 10%;
            right: 0;
            width: 300px;
            background-color: rgba(0, 0, 0, 0.7);
            color: white;
            padding: 15px;
            border-radius: 10px 0 0 10px;
            box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5);
            z-index: 10000;
            transform: translateX(98%);
            transition: transform 0.5s ease-in-out;
            cursor: move;
        }
        #panel-content {
            display: block;
            text-align: center;
        }
        #script-panel:hover {
            transform: translateX(0);
        }
        #run-script-button {
            background-color: #4CAF50;
            color: white;
            border: none;
            padding: 10px 15px;
            text-align: center;
            text-decoration: none;
            display: inline-block;
            font-size: 14px;
            margin: 10px 0;
            cursor: pointer;
            border-radius: 5px;
            transition: background-color 0.3s, box-shadow 0.1s;
        }
        #run-script-button:hover {
            background-color: #45a049;
        }
        #run-script-button:active {
            box-shadow: inset 0px 3px 5px rgba(0, 0, 0, 0.2);
            background-color: #39843b;
        }
    `);

    // 添加拖动功能
    let isDragging = false;
    let startY = 0;
    let startTop = 0;

    panel.addEventListener('mousedown', function(e) {
        isDragging = true;
        startY = e.clientY;
        startTop = panel.offsetTop;
        document.addEventListener('mousemove', onMouseMove);
        document.addEventListener('mouseup', onMouseUp);
    });

    function onMouseMove(e) {
        if (isDragging) {
            const deltaY = e.clientY - startY;
            panel.style.top = `${startTop + deltaY}px`;
        }
    }

    function onMouseUp() {
        isDragging = false;
        document.removeEventListener('mousemove', onMouseMove);
        document.removeEventListener('mouseup', onMouseUp);
    }
    function updateDisplay() {
        const { daysUntilExpiry, hoursUntilExpiry, minutesUntilExpiry, secondsUntilExpiry } = calculateTimeUntilExpiry();

        let displayText = "";
        if (daysUntilExpiry > 0) {
            displayText += `${daysUntilExpiry}天`;
        }
        if (hoursUntilExpiry > 0 ) {
            displayText += `${hoursUntilExpiry}小时`;
        }
        if (minutesUntilExpiry > 0 ) {
            displayText += `${minutesUntilExpiry}分钟`;
        }
        if (daysUntilExpiry === 0 && hoursUntilExpiry === 0 && minutesUntilExpiry === 0) {
            displayText = `${secondsUntilExpiry}秒`;
            if (delay !=1000) {
                delay = 1000; // 设置为每秒刷新一次
                GM_setValue('delay', delay);
            }
        }

        document.getElementById('time-until-expiry').textContent = displayText;
    }
    // 添加时间自动更新功能
    setInterval(() => {
        updateDisplay();
        if (shouldFetchToken()) {
            fetchAndPostToken();
        }
    }, delay);

    // 添加按钮点击事件
    document.getElementById('run-script-button').addEventListener('click', function() {
        fetchAndPostToken();
    });
    showAlert();
    // 检查是否需要获取token
    if (shouldFetchToken()) {
        fetchAndPostToken();
    } else {
        console.log("Script not run: Token is still valid.");
    }
    // 判断是否需要获取token
    function shouldFetchToken() {
        currentTime = new Date().getTime();
        return currentTime > expires;
    }

    // 获取token并发送POST请求的函数
    function fetchAndPostToken() {
        GM_xmlhttpRequest({
            method: "GET",
            url: tokenUrl,
            onload: function(response) {
                if (response.status === 200) {
                    // 解析返回的JSON
                    const responseData = JSON.parse(response.responseText);
                    // 提取accessToken
                    const accessToken = responseData.accessToken;

                    // 解析JWT获取过期时间
                    const parsedToken = parseJWT(accessToken);
                    if (parsedToken && parsedToken.exp) {
                        const tokenExpires = parsedToken.exp * 1000; // 将exp转换为毫秒
                        // 更新过期时间
                        GM_setValue(expiresKey, tokenExpires);
                        currentTime = new Date().getTime();
                        GM_setValue('delay', 60000);
                        if (currentTime > tokenExpires){
                            showAlert();
                        }
                        else{
                            // 发送POST请求
                            sendPostRequest(accessToken, tokenExpires);
                        }
                    } else {
                        console.error("Failed to parse JWT token or exp not found.");
                    }
                } else {
                    console.error("Failed to fetch accessToken. Status:", response.status);
                }
            }
        });
    }

    // 弹出过期提示并在1秒后自动跳转
    function showAlert() {
        // 创建一个div作为自定义弹窗
        let alertBox = document.createElement("div");
        alertBox.innerHTML = `
        <div style="
            position: fixed;
            top: 0;
            left: 50%;
            transform: translateX(-50%);
            background-color: white;
            color: black;
            border: 2px solid #007BFF; /* 蓝色边框 */
            padding: 20px;
            z-index: 10000;
            border-radius: 10px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.3);
            font-family: Arial, sans-serif;
            font-size: 14px;
            text-align: center;
            animation: fadeIn 0.3s ease;
        ">
            <p>Access Token已过期,系统将自动跳转以重新登录chatgpt.com</p>
        </div>`;
        // 添加淡入效果的动画
        const style = document.createElement("style");
        style.innerHTML = `
            @keyframes fadeIn {
                from { opacity: 0; }
                to { opacity: 1; }
            }
        `;
        document.head.appendChild(style);
        // 将自定义弹窗添加到页面中
        document.body.appendChild(alertBox);

        // 1.2秒后自动跳转并移除自定义弹窗
        setTimeout(function() {
            document.body.removeChild(alertBox); // 移除弹窗
            window.open(chatgptLoginUrl, '_blank'); // 在新标签页中打开 chatgpt.com
        }, 1200); // 延迟1.2秒后跳转
    }


    // 发送POST请求的函数
    function sendPostRequest(accessToken, tokenExpires) {
        const data = {
            action: "token",
            access_token: accessToken
        };

        GM_xmlhttpRequest({
            method: "POST",
            url: postUrl,
            headers: {
                "Content-Type": "application/x-www-form-urlencoded"
            },
            data: Object.keys(data).map(key => `${encodeURIComponent(key)}=${encodeURIComponent(data[key])}`).join('&'),
            onload: function(response) {
                console.log("Status Code:", response.status);
                console.log("Response:", response.responseText);

                // 成功发送POST请求后自动跳转
                if (response.status === 200) {
                    window.location.href = redirectUrl;
                }
            },
            onerror: function(error) {
                console.error("Error in POST request:", error);
            }
        });
    }
})();