ClaudeToken切换(十七优化版)

动态切换claude的token

// ==UserScript==
// @name         ClaudeToken切换(十七优化版)
// @namespace    https://www.violet17.com
// @version      1.7.1
// @description  动态切换claude的token
// @author       xiaohan17(author), ethan-j
// @match        https://claude.ai/*
// @match        https://demo.fuclaude.com/*
// @match        https://claude.asia/*
// @grant        GM_xmlhttpRequest
// @connect      ipapi.co
// @license      GNU GPLv3
// ==/UserScript==

(function() {
    'use strict';

    // 配置
    const config = {
        storageKey: 'claudeTokens',
        ipApiUrl: 'https://ipapi.co/country_code',
        defaultToken: {
            name: 'xiaohan17',
            key: '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'
        }
    };

    // 样式
    const getStyles = (isDarkMode) => `
        .claude-modal {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: ${isDarkMode ? 'rgba(0, 0, 0, 0.7)' : 'rgba(0, 0, 0, 0.5)'};
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 10000;
        }
        .claude-modal-content {
            background-color: ${isDarkMode ? '#2c2b28' : '#fff'};
            padding: 20px;
            border-radius: 8px;
            box-shadow: 0 2px 10px ${isDarkMode ? 'rgba(0, 0, 0, 0.3)' : 'rgba(0, 0, 0, 0.1)'};
            width: 300px;
            max-width: 90%;
        }
        .claude-modal h2 {
            margin-top: 0;
            margin-bottom: 15px;
            color: ${isDarkMode ? '#f5f4ef' : '#333'};
            font-size: 18px;
        }
        .claude-modal input {
            width: 100%;
            padding: 8px;
            margin-bottom: 10px;
            border: 1px solid ${isDarkMode ? '#3f3f3c' : '#ddd'};
            border-radius: 4px;
            font-size: 14px;
            background-color: ${isDarkMode ? '#2f2f2c' : '#fff'};
            color: ${isDarkMode ? '#f5f4ef' : '#333'};
        }
        .claude-modal button {
            padding: 8px 12px;
            margin-right: 10px;
            border: none;
            border-radius: 4px;
            background-color: ${isDarkMode ? '#3f3f3c' : '#4CAF50'};
            color: ${isDarkMode ? '#f5f4ef' : 'white'};
            cursor: pointer;
            transition: background-color 0.3s;
        }
        .claude-modal button:hover {
            background-color: ${isDarkMode ? '#4a4a47' : '#45a049'};
        }
        .claude-modal button.cancel {
            background-color: ${isDarkMode ? '#3a3935' : '#f44336'};
        }
        .claude-modal button.cancel:hover {
            background-color: ${isDarkMode ? '#454540' : '#da190b'};
        }
        .claude-token-list {
            max-height: 200px;
            overflow-y: auto;
            margin-bottom: 15px;
        }
        .claude-token-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 8px 0;
            border-bottom: 1px solid ${isDarkMode ? '#3f3f3c' : '#eee'};
        }
        .claude-token-item:last-child {
            border-bottom: none;
        }
        .claude-token-manager {
            background-color: ${isDarkMode ? '#2c2b28' : '#fcfaf5'};
            color: ${isDarkMode ? '#f5f4ef' : '#333'};
        }
        .claude-token-manager select,
        .claude-token-manager button {
            background-color: ${isDarkMode ? '#2f2f2c' : '#f5f1e9'};
            color: ${isDarkMode ? '#f5f4ef' : '#333'};
            border-color: ${isDarkMode ? '#3f3f3c' : '#ccc'};
        }
        .claude-token-manager button:hover {
            background-color: ${isDarkMode ? '#3a3935' : '#e5e1d9'};
        }
    `;

    // UI 组件
    const UI = {
        createElem(tag, styles) {
            const elem = document.createElement(tag);
            Object.assign(elem.style, styles);
            return elem;
        },

        createButton(text, styles) {
            const button = this.createElem('button', styles);
            button.innerText = text;
            return button;
        },

        createTokenSelect(isDarkMode) {
            return this.createElem('select', {
                marginRight: '4px',
                fontSize: '12px',
                width: '150px',
                backgroundColor: isDarkMode ? '#2f2f2c' : '#f5f1e9',
                color: isDarkMode ? '#f5f4ef' : '#333',
                height: '24px',
                padding: '0 4px',
                lineHeight: '24px',
                border: `1px solid ${isDarkMode ? '#3f3f3c' : '#ccc'}`,
                borderRadius: '3px',
                appearance: 'none',
                WebkitAppearance: 'none',
                MozAppearance: 'none',
                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")',
                backgroundRepeat: 'no-repeat',
                backgroundPosition: 'right .5em top 50%',
                backgroundSize: '.65em auto',
            });
        },

        createModal(title, content) {
            const modal = document.createElement('div');
            modal.className = 'claude-modal';

            const modalContent = document.createElement('div');
            modalContent.className = 'claude-modal-content';

            const titleElem = document.createElement('h2');
            titleElem.textContent = title;
            modalContent.appendChild(titleElem);

            modalContent.appendChild(content);
            modal.appendChild(modalContent);

            document.body.appendChild(modal);

            return {
                modal,
                close: () => document.body.removeChild(modal)
            };
        }
    };

    // 主要功能
    const App = {
        init() {
            this.isDarkMode = document.documentElement.getAttribute('data-mode') === 'dark';
            this.injectStyles();
            this.tokens = this.loadTokens();
            this.createUI();
            this.setupEventListeners();
            this.updateTokenSelect();
            this.fetchIPCountryCode();
            this.observeThemeChanges();
        },

        injectStyles() {
            this.styleElem = document.createElement('style');
            this.styleElem.textContent = getStyles(this.isDarkMode);
            document.head.appendChild(this.styleElem);
        },

        updateStyles() {
            this.styleElem.textContent = getStyles(this.isDarkMode);
            this.updateUIColors();
        },

        updateUIColors() {
            Object.assign(this.container.style, {
                backgroundColor: this.isDarkMode ? '#2c2b28' : '#fcfaf5',
                boxShadow: `0 1px 3px ${this.isDarkMode ? 'rgba(0,0,0,0.3)' : 'rgba(0,0,0,0.2)'}`,
            });

            Object.assign(this.tokenSelect.style, {
                backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
                color: this.isDarkMode ? '#f5f4ef' : '#333',
                border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
            });

            [this.switchButton, this.addButton, this.manageButton].forEach(button => {
                Object.assign(button.style, {
                    backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
                    color: this.isDarkMode ? '#f5f4ef' : '#333',
                    border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
                });
            });

            this.toggleButton.style.color = this.isDarkMode ? '#f5f4ef' : '#29261b';
        },

        loadTokens() {
            const savedTokens = JSON.parse(localStorage.getItem(config.storageKey) || '[]');
            return savedTokens.length > 0 ? savedTokens : [config.defaultToken];
        },

        saveTokens() {
            localStorage.setItem(config.storageKey, JSON.stringify(this.tokens));
        },

        createUI() {
            this.tokenSelect = UI.createTokenSelect(this.isDarkMode);
            this.toggleButton = UI.createButton('...', {
                position: 'fixed',
                top: '10px',
                right: '145px',
                zIndex: '9998',
                width: '36px',
                height: '36px',
                backgroundColor: 'transparent',
                border: 'none',
                borderRadius: '0.375rem',
                fontSize: '12px',
                cursor: 'pointer',
                display: 'flex',
                justifyContent: 'center',
                alignItems: 'center',
                transition: 'background-color 0.3s ease, color 0.3s ease',
                color: this.isDarkMode ? '#f5f4ef' : '#29261b',
            });
            this.switchButton = UI.createButton('切换', {
                fontSize: '12px',
                height: '24px',
                padding: '0 8px',
                lineHeight: '22px',
                border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
                borderRadius: '3px',
                backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
                color: this.isDarkMode ? '#f5f4ef' : '#333',
                cursor: 'pointer',
            });
            this.addButton = UI.createButton('添加', {
                fontSize: '12px',
                height: '24px',
                padding: '0 8px',
                lineHeight: '22px',
                border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
                borderRadius: '3px',
                backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
                color: this.isDarkMode ? '#f5f4ef' : '#333',
                cursor: 'pointer',
            });
            this.manageButton = UI.createButton('管理', {
                fontSize: '12px',
                height: '24px',
                padding: '0 8px',
                lineHeight: '22px',
                border: `1px solid ${this.isDarkMode ? '#3f3f3c' : '#ccc'}`,
                borderRadius: '3px',
                backgroundColor: this.isDarkMode ? '#2f2f2c' : '#f5f1e9',
                color: this.isDarkMode ? '#f5f4ef' : '#333',
                cursor: 'pointer',
            });

            this.container = UI.createElem('div', {
                position: 'fixed',
                top: '50px',
                right: '77px',
                zIndex: '9999',
                backgroundColor: this.isDarkMode ? '#2c2b28' : '#fcfaf5',
                padding: '8px',
                borderRadius: '3px',
                boxShadow: `0 1px 3px ${this.isDarkMode ? 'rgba(0,0,0,0.3)' : 'rgba(0,0,0,0.2)'}`,
                display: 'none',
                fontSize: '12px',
                width: 'auto',
            });

            const buttonContainer = UI.createElem('div', {
                display: 'flex',
                flexDirection: 'row',
                alignItems: 'center',
                gap: '4px',
            });

            buttonContainer.appendChild(this.tokenSelect);
            buttonContainer.appendChild(this.switchButton);
            buttonContainer.appendChild(this.addButton);
            buttonContainer.appendChild(this.manageButton);
            this.container.appendChild(buttonContainer);

            document.body.appendChild(this.container);
            document.body.appendChild(this.toggleButton);
        },

        setupEventListeners() {
            this.toggleButton.addEventListener('click', () => this.toggleContainer());
            this.toggleButton.addEventListener('mouseover', () => {
                this.toggleButton.style.backgroundColor = this.isDarkMode ? '#1a1915' : '#ded8c4';
                this.toggleButton.style.color = this.isDarkMode ? '#f5f4ef' : '#29261b';
            });
            this.toggleButton.addEventListener('mouseout', () => {
                this.toggleButton.style.backgroundColor = 'transparent';
                this.toggleButton.style.color = this.isDarkMode ? '#f5f4ef' : '#29261b';
            });
            this.switchButton.addEventListener('click', () => this.switchToken());
            this.addButton.addEventListener('click', () => this.showAddTokenModal());
            this.manageButton.addEventListener('click', () => this.showManageTokensModal());
        },

        observeThemeChanges() {
            const observer = new MutationObserver((mutations) => {
                mutations.forEach((mutation) => {
                    if (mutation.type === 'attributes' && mutation.attributeName === 'data-mode') {
                        this.isDarkMode = document.documentElement.getAttribute('data-mode') === 'dark';
                        this.updateStyles();
                    }
                });
            });

            observer.observe(document.documentElement, {
                attributes: true,
                attributeFilter: ['data-mode']
            });
        },

        toggleContainer() {
            this.container.style.display = this.container.style.display === 'none' ? 'block' : 'none';
        },

        updateTokenSelect() {
            this.tokenSelect.innerHTML = '';
            this.tokens.forEach((token, index) => {
                const option = document.createElement('option');
                option.value = index;
                option.text = token.name;
                this.tokenSelect.appendChild(option);
            });
        },

        switchToken() {
            const selectedToken = this.tokens[this.tokenSelect.value];
            this.applyToken(selectedToken.key);
        },

        applyToken(token) {
            const currentURL = window.location.href;

            if (currentURL.startsWith('https://claude.ai/')) {
                document.cookie = `sessionKey=${token}; path=/; domain=.claude.ai`;
                window.location.reload();
            } else {
                let loginUrl;
                if (currentURL.startsWith('https://demo.fuclaude.com/')) {
                    loginUrl = `https://demo.fuclaude.com/login_token?session_key=${token}`;
                } else if (currentURL.startsWith('https://claude.asia/')) {
                    loginUrl = `https://claude.asia/login_token?session_key=${token}`;
                }

                if (loginUrl) {
                    window.location.href = loginUrl;
                }
            }
        },

        showAddTokenModal() {
            const content = document.createElement('div');
            const nameInput = document.createElement('input');
            nameInput.placeholder = 'Token名称';
            const keyInput = document.createElement('input');
            keyInput.placeholder = 'Token密钥';
            const addButton = document.createElement('button');
            addButton.textContent = '添加';
            const cancelButton = document.createElement('button');
            cancelButton.textContent = '取消';
            cancelButton.className = 'cancel';

            content.appendChild(nameInput);
            content.appendChild(keyInput);
            content.appendChild(addButton);
            content.appendChild(cancelButton);

            const { close } = UI.createModal('添加新Token', content);

            addButton.addEventListener('click', () => {
                if (nameInput.value && keyInput.value) {
                    this.tokens.push({ name: nameInput.value, key: keyInput.value });
                    this.saveTokens();
                    this.updateTokenSelect();
                    close();
                }
            });

            cancelButton.addEventListener('click', close);
        },

        showManageTokensModal() {
            const content = document.createElement('div');
            const tokenList = document.createElement('div');
            tokenList.className = 'claude-token-list';

            this.tokens.forEach((token, index) => {
                const tokenItem = document.createElement('div');
                tokenItem.className = 'claude-token-item';
                tokenItem.textContent = token.name;
                const deleteButton = document.createElement('button');
                deleteButton.textContent = '删除';
                deleteButton.className = 'cancel';
                deleteButton.addEventListener('click', () => {
                    this.tokens.splice(index, 1);
                    this.saveTokens();
                    this.updateTokenSelect();
                    tokenItem.remove();
                });
                tokenItem.appendChild(deleteButton);
                tokenList.appendChild(tokenItem);
            });

            content.appendChild(tokenList);
            const closeButton = document.createElement('button');
            closeButton.textContent = '关闭';
            content.appendChild(closeButton);

            const { close } = UI.createModal('管理Tokens', content);
            closeButton.addEventListener('click', close);
        },

        fetchIPCountryCode() {
            GM_xmlhttpRequest({
                method: "GET",
                url: config.ipApiUrl,
                onload: (response) => {
                    if (response.status === 200) {
                        this.toggleButton.innerText = response.responseText.trim();
                    } else {
                        this.toggleButton.innerText = 'ERR';
                    }
                },
                onerror: () => {
                    this.toggleButton.innerText = 'ERR';
                }
            });
        }
    };

    // 初始化应用
    App.init();
})();