ClaudeToken 切换

动态切换claude的token

目前为 2024-07-24 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name ClaudeToken 切换
  3. // @version 1.8.0
  4. // @description 动态切换claude的token
  5. // @author ethan-j, xiaohan17
  6. // @match https://claude.ai/*
  7. // @match https://demo.fuclaude.com/*
  8. // @match https://claude.asia/*
  9. // @grant GM_xmlhttpRequest
  10. // @grant GM_getValue
  11. // @grant GM_setValue
  12. // @connect ipapi.co
  13. // @license GNU GPLv3
  14. // @namespace https://gf.qytechs.cn/users/1338998
  15. // ==/UserScript==
  16.  
  17. (function() {
  18. 'use strict';
  19.  
  20. // 配置
  21. const config = {
  22. storageKey: 'claudeTokens',
  23. ipApiUrl: 'https://ipapi.co/country_code'
  24. };
  25.  
  26. // 样式
  27. const getStyles = (isDarkMode) => `
  28. .claude-modal {
  29. position: fixed;
  30. top: 0;
  31. left: 0;
  32. width: 100%;
  33. height: 100%;
  34. background-color: ${isDarkMode ? 'rgba(0, 0, 0, 0.7)' : 'rgba(0, 0, 0, 0.5)'};
  35. display: flex;
  36. justify-content: center;
  37. align-items: center;
  38. z-index: 10000;
  39. }
  40. .claude-modal-content {
  41. background-color: ${isDarkMode ? '#2c2b28' : '#faf9f7'};
  42. padding: 20px;
  43. border-radius: 8px;
  44. box-shadow: 0 2px 10px ${isDarkMode ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.1)'};
  45. width: 400px;
  46. max-width: 90%;
  47. max-height: 80vh;
  48. overflow-y: auto;
  49. }
  50. .claude-modal h2 {
  51. margin-top: 0;
  52. margin-bottom: 15px;
  53. color: ${isDarkMode ? '#f5f4ef' : '#333'};
  54. font-size: 18px;
  55. font-weight: 600;
  56. }
  57. .claude-token-list {
  58. max-height: 60vh;
  59. overflow-y: auto;
  60. margin-bottom: 15px;
  61. }
  62. .claude-token-item {
  63. display: flex;
  64. justify-content: space-between;
  65. align-items: center;
  66. padding: 8px 0;
  67. border-bottom: 1px solid ${isDarkMode ? '#3f3f3c' : '#eee'};
  68. }
  69. .claude-token-item:last-child {
  70. border-bottom: none;
  71. }
  72. .claude-token-item input {
  73. flex-grow: 1;
  74. margin-right: 10px;
  75. padding: 5px;
  76. border: 1px solid ${isDarkMode ? '#3f3f3c' : '#ddd'};
  77. border-radius: 4px;
  78. font-size: 14px;
  79. background-color: ${isDarkMode ? '#1f1e1b' : '#fff'};
  80. color: ${isDarkMode ? '#f5f4ef' : '#333'};
  81. }
  82. .claude-token-item button {
  83. padding: 5px 10px;
  84. margin-left: 5px;
  85. border: none;
  86. border-radius: 4px;
  87. background-color: ${isDarkMode ? '#3f3f3c' : '#f0f0f0'};
  88. color: ${isDarkMode ? '#f5f4ef' : '#333'};
  89. font-size: 12px;
  90. cursor: pointer;
  91. transition: background-color 0.3s;
  92. }
  93. .claude-token-item button:hover {
  94. background-color: ${isDarkMode ? '#4a4a47' : '#e0e0e0'};
  95. }
  96. .claude-token-item button.cancel {
  97. background-color: ${isDarkMode ? '#3a3935' : '#ffebee'};
  98. color: ${isDarkMode ? '#ff9b9b' : '#d32f2f'};
  99. }
  100. .claude-token-item button.cancel:hover {
  101. background-color: ${isDarkMode ? '#454540' : '#ffcdd2'};
  102. }
  103. .claude-modal button.close {
  104. display: block;
  105. width: 100%;
  106. padding: 8px;
  107. margin-top: 10px;
  108. border: none;
  109. border-radius: 4px;
  110. background-color: ${isDarkMode ? '#5a5a5a' : '#4a4a4a'};
  111. color: ${isDarkMode ? '#f5f4ef' : 'white'};
  112. font-size: 14px;
  113. cursor: pointer;
  114. transition: background-color 0.3s;
  115. }
  116. .claude-modal button.close:hover {
  117. background-color: ${isDarkMode ? '#666' : '#333'};
  118. }
  119. `;
  120.  
  121. // UI 组件
  122. const UI = {
  123. createElem(tag, styles) {
  124. const elem = document.createElement(tag);
  125. Object.assign(elem.style, styles);
  126. return elem;
  127. },
  128.  
  129. createButton(text, styles) {
  130. const button = this.createElem('button', styles);
  131. button.innerText = text;
  132. return button;
  133. },
  134.  
  135. createTokenSelect(isDarkMode) {
  136. return this.createElem('select', {
  137. marginRight: '4px',
  138. fontSize: '12px',
  139. width: '150px',
  140. backgroundColor: isDarkMode ? '#2f2f2c' : '#f5f1e9',
  141. color: isDarkMode ? '#f5f4ef' : '#333',
  142. height: '24px',
  143. padding: '0 4px',
  144. lineHeight: '24px',
  145. border: `1px solid ${isDarkMode ? '#3f3f3c' : '#ccc'}`,
  146. borderRadius: '3px',
  147. appearance: 'none',
  148. WebkitAppearance: 'none',
  149. MozAppearance: 'none',
  150. backgroundImage: 'url("data:image/svg+xml;charset=US-ASCII,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%22292.4%22%20height%3D%22292.4%22%3E%3Cpath%20fill%3D%22%23007CB2%22%20d%3D%22M287%2069.4a17.6%2017.6%200%200%200-13-5.4H18.4c-5%200-9.3%201.8-12.9%205.4A17.6%2017.6%200%200%200%200%2082.2c0%205%201.8%209.3%205.4%2012.9l128%20127.9c3.6%203.6%207.8%205.4%2012.8%205.4s9.2-1.8%2012.8-5.4L287%2095c3.5-3.5%205.4-7.8%205.4-12.8%200-5-1.9-9.2-5.5-12.8z%22%2F%3E%3C%2Fsvg%3E")',
  151. backgroundRepeat: 'no-repeat',
  152. backgroundPosition: 'right .5em top 50%',
  153. backgroundSize: '.65em auto',
  154. });
  155. },
  156.  
  157. createModal(title, content) {
  158. const modal = document.createElement('div');
  159. modal.className = 'claude-modal';
  160.  
  161. const modalContent = document.createElement('div');
  162. modalContent.className = 'claude-modal-content';
  163.  
  164. const titleElem = document.createElement('h2');
  165. titleElem.textContent = title;
  166. modalContent.appendChild(titleElem);
  167.  
  168. modalContent.appendChild(content);
  169. modal.appendChild(modalContent);
  170.  
  171. document.body.appendChild(modal);
  172.  
  173. return {
  174. modal,
  175. close: () => document.body.removeChild(modal)
  176. };
  177. }
  178. };
  179.  
  180. // 主要功能
  181. const App = {
  182. init() {
  183. this.isDarkMode = document.documentElement.getAttribute('data-mode') === 'dark';
  184. this.injectStyles();
  185. this.tokens = this.loadTokens();
  186. this.createUI();
  187. this.setupEventListeners();
  188. this.updateTokenSelect();
  189. this.fetchIPCountryCode();
  190. this.observeThemeChanges();
  191. },
  192.  
  193. injectStyles() {
  194. this.styleElem = document.createElement('style');
  195. this.styleElem.textContent = getStyles(this.isDarkMode);
  196. document.head.appendChild(this.styleElem);
  197. },
  198.  
  199. updateStyles() {
  200. this.styleElem.textContent = getStyles(this.isDarkMode);
  201. this.updateUIColors();
  202. },
  203.  
  204. updateUIColors() {
  205. Object.assign(this.container.style, {
  206. backgroundColor: this.isDarkMode ? '#2c2b28' : '#fcfaf5',
  207. boxShadow: `0 1px 3px ${this.isDarkMode ? 'rgba(0,0,0,0.3)' : 'rgba(0,0,0,0.2)'}`,
  208. });
  209.  
  210. Object.assign(this.tokenSelect.style, {
  211. backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
  212. color: this.isDarkMode ? '#f5f4ef' : '#333',
  213. border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
  214. });
  215.  
  216. [this.switchButton, this.addButton, this.manageButton].forEach(button => {
  217. Object.assign(button.style, {
  218. backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
  219. color: this.isDarkMode ? '#f5f4ef' : '#333',
  220. border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
  221. });
  222. });
  223.  
  224. this.toggleButton.style.color = this.isDarkMode ? '#f5f4ef' : '#29261b';
  225. },
  226.  
  227. // 加载令牌
  228. loadTokens() {
  229. const defaultTokens = [
  230. { name: 'Token 1', key: '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' }
  231. ];
  232. const savedTokens = JSON.parse(GM_getValue(config.storageKey, '[]'));
  233. return savedTokens.length > 0 ? savedTokens : defaultTokens;
  234. },
  235.  
  236. // 保存令牌
  237. saveTokens() {
  238. GM_setValue(config.storageKey, JSON.stringify(this.tokens));
  239. },
  240.  
  241. createUI() {
  242. this.tokenSelect = UI.createTokenSelect(this.isDarkMode);
  243. this.toggleButton = UI.createButton('...', {
  244. position: 'fixed',
  245. top: '10px',
  246. right: '95px',
  247. zIndex: '9998',
  248. width: '36px',
  249. height: '36px',
  250. backgroundColor: 'transparent',
  251. border: 'none',
  252. borderRadius: '0.375rem',
  253. fontSize: '12px',
  254. cursor: 'pointer',
  255. display: 'flex',
  256. justifyContent: 'center',
  257. alignItems: 'center',
  258. transition: 'background-color 0.3s ease, color 0.3s ease',
  259. color: this.isDarkMode ? '#f5f4ef' : '#29261b',
  260. });
  261. this.switchButton = UI.createButton('切换', {
  262. fontSize: '12px',
  263. height: '24px',
  264. padding: '0 8px',
  265. lineHeight: '22px',
  266. border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
  267. borderRadius: '3px',
  268. backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
  269. color: this.isDarkMode ? '#f5f4ef' : '#333',
  270. cursor: 'pointer',
  271. });
  272. this.addButton = UI.createButton('添加', {
  273. fontSize: '12px',
  274. height: '24px',
  275. padding: '0 8px',
  276. lineHeight: '22px',
  277. border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
  278. borderRadius: '3px',
  279. backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
  280. color: this.isDarkMode ? '#f5f4ef' : '#333',
  281. cursor: 'pointer',
  282. });
  283. this.manageButton = UI.createButton('管理', {
  284. fontSize: '12px',
  285. height: '24px',
  286. padding: '0 8px',
  287. lineHeight: '22px',
  288. border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
  289. borderRadius: '3px',
  290. backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
  291. color: this.isDarkMode ? '#f5f4ef' : '#333',
  292. cursor: 'pointer',
  293. });
  294.  
  295. this.container = UI.createElem('div', {
  296. position: 'fixed',
  297. top: '50px',
  298. right: '77px',
  299. zIndex: '9999',
  300. backgroundColor: this.isDarkMode ? '#2c2b28' : '#fcfaf5',
  301. padding: '8px',
  302. borderRadius: '3px',
  303. boxShadow: `0 1px 3px ${this.isDarkMode ? 'rgba(0,0,0,0.3)' : 'rgba(0,0,0,0.2)'}`,
  304. display: 'none',
  305. fontSize: '12px',
  306. width: 'auto',
  307. });
  308.  
  309. const buttonContainer = UI.createElem('div', {
  310. display: 'flex',
  311. flexDirection: 'row',
  312. alignItems: 'center',
  313. gap: '4px',
  314. });
  315.  
  316. buttonContainer.appendChild(this.tokenSelect);
  317. buttonContainer.appendChild(this.switchButton);
  318. buttonContainer.appendChild(this.addButton);
  319. buttonContainer.appendChild(this.manageButton);
  320. this.container.appendChild(buttonContainer);
  321.  
  322. document.body.appendChild(this.container);
  323. document.body.appendChild(this.toggleButton);
  324. },
  325.  
  326. setupEventListeners() {
  327. this.toggleButton.addEventListener('click', () => this.toggleContainer());
  328. this.toggleButton.addEventListener('mouseover', () => {
  329. this.toggleButton.style.backgroundColor = this.isDarkMode ? '#1a1915' : '#ded8c4';
  330. this.toggleButton.style.color = this.isDarkMode ? '#f5f4ef' : '#29261b';
  331. });
  332. this.toggleButton.addEventListener('mouseout', () => {
  333. this.toggleButton.style.backgroundColor = 'transparent';
  334. this.toggleButton.style.color = this.isDarkMode ? '#f5f4ef' : '#29261b';
  335. });
  336. this.switchButton.addEventListener('click', () => this.switchToken());
  337. this.addButton.addEventListener('click', () => this.showAddTokenModal());
  338. this.manageButton.addEventListener('click', () => this.showManageTokensModal());
  339. },
  340.  
  341. observeThemeChanges() {
  342. const observer = new MutationObserver((mutations) => {
  343. mutations.forEach((mutation) => {
  344. if (mutation.type === 'attributes' && mutation.attributeName === 'data-mode') {
  345. this.isDarkMode = document.documentElement.getAttribute('data-mode') === 'dark';
  346. this.updateStyles();
  347. }
  348. });
  349. });
  350.  
  351. observer.observe(document.documentElement, {
  352. attributes: true,
  353. attributeFilter: ['data-mode']
  354. });
  355. },
  356.  
  357. toggleContainer() {
  358. this.container.style.display = this.container.style.display === 'none' ? 'block' : 'none';
  359. },
  360.  
  361. updateTokenSelect() {
  362. this.tokenSelect.innerHTML = '';
  363. this.tokens.forEach((token, index) => {
  364. const option = document.createElement('option');
  365. option.value = index;
  366. option.text = token.name;
  367. this.tokenSelect.appendChild(option);
  368. });
  369. },
  370.  
  371. switchToken() {
  372. const selectedToken = this.tokens[this.tokenSelect.value];
  373. this.applyToken(selectedToken.key);
  374. },
  375.  
  376. applyToken(token) {
  377. const currentURL = window.location.href;
  378.  
  379. if (currentURL.startsWith('https://claude.ai/')) {
  380. document.cookie = `sessionKey=${token}; path=/; domain=.claude.ai`;
  381. window.location.reload();
  382. } else {
  383. let loginUrl;
  384. if (currentURL.startsWith('https://demo.fuclaude.com/')) {
  385. loginUrl = `https://demo.fuclaude.com/login_token?session_key=${token}`;
  386. } else if (currentURL.startsWith('https://claude.asia/')) {
  387. loginUrl = `https://claude.asia/login_token?session_key=${token}`;
  388. }
  389.  
  390. if (loginUrl) {
  391. window.location.href = loginUrl;
  392. }
  393. }
  394. },
  395.  
  396. showAddTokenModal() {
  397. const content = document.createElement('div');
  398. const nameInput = document.createElement('input');
  399. nameInput.placeholder = 'Token名称';
  400. const keyInput = document.createElement('input');
  401. keyInput.placeholder = 'Token密钥';
  402. const addButton = document.createElement('button');
  403. addButton.textContent = '添加';
  404. const cancelButton = document.createElement('button');
  405. cancelButton.textContent = '取消';
  406. cancelButton.className = 'cancel';
  407.  
  408. content.appendChild(nameInput);
  409. content.appendChild(keyInput);
  410. content.appendChild(addButton);
  411. content.appendChild(cancelButton);
  412.  
  413. const { close } = UI.createModal('添加新Token', content);
  414.  
  415. addButton.addEventListener('click', () => {
  416. if (nameInput.value && keyInput.value) {
  417. this.tokens.push({ name: nameInput.value, key: keyInput.value });
  418. this.saveTokens();
  419. this.updateTokenSelect();
  420. close();
  421. }
  422. });
  423.  
  424. cancelButton.addEventListener('click', close);
  425. },
  426.  
  427. showManageTokensModal() {
  428. const content = document.createElement('div');
  429. const tokenList = document.createElement('div');
  430. tokenList.className = 'claude-token-list';
  431.  
  432. this.tokens.forEach((token, index) => {
  433. const tokenItem = document.createElement('div');
  434. tokenItem.className = 'claude-token-item';
  435.  
  436. const nameInput = document.createElement('input');
  437. nameInput.value = token.name;
  438. nameInput.placeholder = 'Token名称';
  439.  
  440. const saveButton = document.createElement('button');
  441. saveButton.textContent = '保存';
  442. saveButton.addEventListener('click', () => {
  443. this.tokens[index].name = nameInput.value;
  444. this.saveTokens();
  445. this.updateTokenSelect();
  446. });
  447.  
  448. const deleteButton = document.createElement('button');
  449. deleteButton.textContent = '删除';
  450. deleteButton.className = 'cancel';
  451. deleteButton.addEventListener('click', () => {
  452. if (confirm('确定要删除这个Token吗?')) {
  453. this.tokens.splice(index, 1);
  454. this.saveTokens();
  455. this.updateTokenSelect();
  456. tokenItem.remove();
  457. }
  458. });
  459.  
  460. tokenItem.appendChild(nameInput);
  461. tokenItem.appendChild(saveButton);
  462. tokenItem.appendChild(deleteButton);
  463. tokenList.appendChild(tokenItem);
  464. });
  465.  
  466. content.appendChild(tokenList);
  467. const closeButton = document.createElement('button');
  468. closeButton.textContent = '关闭';
  469. closeButton.className = 'close';
  470. content.appendChild(closeButton);
  471.  
  472. const { modal, close } = UI.createModal('管理Tokens', content);
  473. closeButton.addEventListener('click', close);
  474.  
  475. // 添加键盘事件监听器
  476. modal.addEventListener('keydown', (e) => {
  477. if (e.key === 'Escape') {
  478. close();
  479. }
  480. });
  481. },
  482.  
  483. fetchIPCountryCode() {
  484. GM_xmlhttpRequest({
  485. method: "GET",
  486. url: config.ipApiUrl,
  487. onload: (response) => {
  488. if (response.status === 200) {
  489. this.toggleButton.innerText = response.responseText.trim();
  490. } else {
  491. this.toggleButton.innerText = 'ERR';
  492. }
  493. },
  494. onerror: () => {
  495. this.toggleButton.innerText = 'ERR';
  496. }
  497. });
  498. }
  499. };
  500.  
  501. // 初始化应用
  502. App.init();
  503. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址