原代码来自
将代码中的一些错误进行了修复,
在直接使用时会出现获取失败,然后用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/
效果如下