ChatGPT AccessToken 自动更新

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

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

您需要先安裝使用者腳本管理器擴展,如 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.6
// @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';

    // 定义获取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">${daysUntilExpiry}天${hoursUntilExpiry}小时${minutesUntilExpiry}分钟</span></p>
        <button id="run-script-button">立即获取AccessToken并POST</button>
    </div>
    `;
    document.body.appendChild(panel);

    // 添加样式
    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);
    }

    // 添加时间自动更新功能
    setInterval(() => {
        const { daysUntilExpiry, hoursUntilExpiry, minutesUntilExpiry, secondsUntilExpiry } = calculateTimeUntilExpiry();

        // 更新界面显示
        document.getElementById('time-until-expiry').textContent = `${daysUntilExpiry}天${hoursUntilExpiry}小时${minutesUntilExpiry}分钟`;

        // 检查是否需要获取token
        if (shouldFetchToken()) {
            fetchAndPostToken();
        }
        // 如果剩余时间小于1分钟,则强制更新token
        else if (daysUntilExpiry === 0 && hoursUntilExpiry === 0 && minutesUntilExpiry === 0 && secondsUntilExpiry < 60) {
            delay = secondsUntilExpiry * 1000; // 将延迟设置为剩余的秒数(毫秒)
            GM_setValue('delay', delay); // 将新延迟值保存到GM存储中
        }
    }, delay); // 每delay更新一次

    // 添加按钮点击事件
    document.getElementById('run-script-button').addEventListener('click', function() {
        fetchAndPostToken();
    });

    // 检查是否需要获取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);
                        GM_setValue('delay', 60000);
                        // 发送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);
                }
            }
        });
    }

    // 发送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);
            }
        });
    }
})();