多rt快速切换

原代码来自

通过多rt 实现个人ChatGPT池的油猴脚本 - 开发调优 / 开发调优, Lv2 - LINUX DO

将代码中的一些错误进行了修复,
image
在直接使用时会出现获取失败,然后用ai简单修改了一下,修改后的代码为

// ==UserScript==
// @name         aaa
// @namespace    http://tampermonkey.net/
// @version      0.5
// @description  支持多个refresh token,用户可随时选择token并自动登录,方便切换账号
// @author       You
// @match        https://*.new.oaifree.com/*
// @grant        GM_xmlhttpRequest
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @connect      token.oaifree.com
// @connect      chat.oaifree.com
// @connect      auth0.openai.com
// ==/UserScript==

(function() {
    'use strict';

    // 预设的多个refresh token
    const refreshTokens = [
        '输入你的 refreshtoken',
        '输入更多的 refreshtoken',
    ];

    // 获取当前时间戳
    function getCurrentTimestamp() {
        return Math.floor(Date.now() / 1000);
    }

    // 检查 st 是否过期
    function isSTExpired() {
        const expireAt = GM_getValue('expire_at', 0);
        console.log("st expire at: " + expireAt);
        return isNaN(expireAt) || getCurrentTimestamp() >= expireAt;
    }

    // 使用 rt 换取 at
    function getAccessToken(refreshToken) {
        return new Promise((resolve, reject) => {
            GM_xmlhttpRequest({
                method: 'POST',
                url: 'https://auth0.openai.com/oauth/token',
                headers: {
                    'Content-Type': 'application/json'
                },
                data: JSON.stringify({
                    "redirect_uri": "com.openai.chat://auth0.openai.com/ios/com.openai.chat/callback",
                    "grant_type": "refresh_token",
                    "client_id": "pdlLIX2Y72MIl2rhLhTE9VV9bN905kBh",
                    "refresh_token": refreshToken
                }),
                onload: function(response) {
                    if (response.status === 200) {
                        const data = JSON.parse(response.responseText);
                        if (data.access_token) {
                            resolve(data.access_token);
                        } else {
                            reject('Failed to generate access token, response: ' + JSON.stringify(data));
                        }
                    } else {
                        reject('Failed to refresh access token');
                    }
                },
                onerror: function(e) {
                    console.error(e);
                    reject('Failed to refresh access token');
                }
            });
        });
    }

    // 根据子域名获取对应的 index
    function getIndexFromSubdomain() {
        const subdomain = window.location.hostname.split('.')[0]; // 获取子域名部分
        const index = parseInt(subdomain, 10) - 1; // 将子域名转换为整数 index
        if (isNaN(index) || index < 0 || index >= refreshTokens.length) {
            throw new Error(`Invalid subdomain index: ${subdomain}`);
        }
        return index;
    }

        // 显示一个非阻塞的通知框
    function showNotification(message) {
        const notification = document.createElement('div');
        notification.textContent = message;
        notification.style.position = 'fixed';
        notification.style.top = '20px';
        notification.style.left = '50%';
        notification.style.transform = 'translateX(-50%)';
        notification.style.padding = '10px';
        notification.style.backgroundColor = 'green';
        notification.style.color = 'white';
        notification.style.borderRadius = '5px';
        notification.style.zIndex = '10000';
        notification.style.fontFamily = 'Arial, sans-serif';
        notification.style.fontSize = '14px';
        notification.style.boxShadow = '0 0 10px rgba(0,0,0,0.5)';

        document.body.appendChild(notification);

        // 3秒后自动隐藏通知框
        setTimeout(() => {
            notification.remove();
        }, 3000);
    }

    // 创建并显示一个对话框,显示 access_token,并带有复制按钮
    function showTokenDialog(accessToken) {
        const dialog = document.createElement('div');
        dialog.style.position = 'fixed';
        dialog.style.top = '20px';
        dialog.style.right = '20px';
        dialog.style.padding = '20px';
        dialog.style.backgroundColor = 'rgba(0, 0, 0, 0.85)';
        dialog.style.color = 'white';
        dialog.style.borderRadius = '10px';
        dialog.style.zIndex = '10000';
        dialog.style.fontFamily = 'Arial, sans-serif';
        dialog.style.fontSize = '14px';
        dialog.style.boxShadow = '0 0 20px rgba(0,0,0,0.7)';
        dialog.style.width = '300px';
        dialog.style.textAlign = 'center';

        const title = document.createElement('h2');
        title.textContent = 'Token 已生成';
        title.style.marginBottom = '15px';
        title.style.fontSize = '18px';
        title.style.fontWeight = 'bold';
        title.style.color = '#FFD700'; // 金色标题
        dialog.appendChild(title);

        const textArea = document.createElement('textarea');
        textArea.value = accessToken;
        textArea.readOnly = true;
        textArea.style.width = '100%';
        textArea.style.height = '60px';
        textArea.style.marginBottom = '10px';
        textArea.style.border = 'none';
        textArea.style.borderRadius = '5px';
        textArea.style.padding = '10px';
        textArea.style.fontSize = '12px';
        textArea.style.backgroundColor = '#333';
        textArea.style.color = 'white';
        textArea.style.resize = 'none';
        dialog.appendChild(textArea);

        const copyButton = document.createElement('button');
        copyButton.textContent = '复制Token';
        copyButton.style.padding = '10px 20px';
        copyButton.style.backgroundColor = '#007BFF';
        copyButton.style.color = 'white';
        copyButton.style.border = 'none';
        copyButton.style.borderRadius = '5px';
        copyButton.style.cursor = 'pointer';
        copyButton.style.fontSize = '14px';
        copyButton.style.transition = 'background-color 0.3s';
        copyButton.onmouseover = function() {
            copyButton.style.backgroundColor = '#0056b3';
        };
        copyButton.onmouseout = function() {
            copyButton.style.backgroundColor = '#007BFF';
        };

        copyButton.addEventListener('click', () => {
            textArea.select();
            document.execCommand('copy');
            showNotification('Token 已复制到剪贴板');
        });

        dialog.appendChild(copyButton);
        document.body.appendChild(dialog);

        // 10秒后自动隐藏对话框
        setTimeout(() => {
            dialog.remove();
        }, 10000);
    }

    // 自动登录
    function autoLogin(index) {
        const loginUrl = `https://${index}.new.oaifree.com/`;
        console.log('Logging in with URL: ' + loginUrl);
        window.location.href = loginUrl;
    }

    // 选择token并登录
    async function selectTokenAndLogin(index) {
        try {
            autoLogin(index);
        } catch (error) {
            console.error('Login failed:', error);
            showNotification('登录失败,请查看控制台获取详细信息。');
        }
    }

    // 为每个token注册菜单命令
    refreshTokens.forEach((token, index) => {
        GM_registerMenuCommand(`切换到 Token ${index + 1}`, () => selectTokenAndLogin(index + 1));
    });

    // 检查是否需要刷新token并自动登录(仅在登录页面执行)
    if (window.location.pathname === '/auth/login_auth0') {
        (async function() {
            try {
                const index = getIndexFromSubdomain(); // 根据子域名获取 index
                let accessToken = GM_getValue("accessToken");
                const refreshToken = refreshTokens[index]; // 使用子域名确定的 index 获取 refreshToken

                if (isSTExpired() || !accessToken) {
                    console.log('ST token is expired or not found. Please select a token from the userscript menu to login.');
                    // 尝试获取并显示新的 access_token
                    accessToken = await getAccessToken(refreshToken);
                    GM_setValue("accessToken", accessToken);
                    showTokenDialog(accessToken); // 显示 access_token 对话框
                } else {
                    console.log('ST token is still valid. Auto-logging in...');
                    autoLogin(index);
                }
            } catch (error) {
                console.error(error);
                showNotification('获取 token 失败,请查看控制台获取详细信息。');
            }
        })();
    }
})();

不过在使用这个油猴脚本时,依旧需要梯子。
使用网站https://1.new.oaifree.com/
效果如下

13 Likes

感谢大佬分享

大佬太强了 :tieba_087:tieba_087

感谢分享,普号开用

https://linux.do/t/topic/198633/11?u=nanze 这个版本是不需要梯子的