币安网站备注助手 (GitHub Gist 终极修复版)

在币安网站右上角显示备注,使用 GitHub Gist 作为永久免费的云同步后端,可以同步页面备注和脚本

// ==UserScript==
// @name         币安网站备注助手 (GitHub Gist 终极修复版)
// @namespace    http://tampermonkey.net/
// @version      6.2
// @description  在币安网站右上角显示备注,使用 GitHub Gist 作为永久免费的云同步后端,可以同步页面备注和脚本
// @author       YourName & Assistant
// @match        https://www.binance.com/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_deleteValue
// @grant        GM_registerMenuCommand
// @grant        GM_xmlhttpRequest
// @connect      api.github.com
// ==/UserScript==

(function() {
    'use strict';

    const STORAGE_KEY_NOTES = 'website_notes_data_v6';
    const STORAGE_KEY_GIST_ID = 'github_gist_id';
    const STORAGE_KEY_GIST_TOKEN = 'github_gist_token';
    const FILENAME = 'binance_notes.json';

    let currentUrl = window.location.href;
    let noteDiv = null;
    let cloudConfig = null;

    // --- 云配置、读取、保存函数 (这些都是正确的,无需改动) ---
    async function getCloudConfig() {
        if (cloudConfig) return cloudConfig;
        let gistId = GM_getValue(STORAGE_KEY_GIST_ID, null);
        let token = GM_getValue(STORAGE_KEY_GIST_TOKEN, null);
        if (!gistId || !token) {
            alert('欢迎使用GitHub Gist版!首次运行,请配置同步信息。');
            gistId = prompt('请输入您在 GitHub Gist 上获取的 "Gist ID":');
            if (!gistId || gistId.trim() === '') {
                alert('操作已取消。云同步将不会启用。');
                return null;
            }
            token = prompt('现在,请输入您的 GitHub Personal Access Token (以 ghp_ 开头):');
            if (!token || token.trim() === '') {
                alert('操作已取消。云同步将不会启用。');
                return null;
            }
            GM_setValue(STORAGE_KEY_GIST_ID, gistId.trim());
            GM_setValue(STORAGE_KEY_GIST_TOKEN, token.trim());
            alert('配置已保存!脚本现在将使用 GitHub Gist 进行同步。');
        }
        cloudConfig = { gistId, token };
        return cloudConfig;
    }

    async function getAllNotes() {
        const config = await getCloudConfig();
        if (!config) {
            const localNotes = GM_getValue(STORAGE_KEY_NOTES, '{}');
            try { return JSON.parse(localNotes); } catch (e) { return {}; }
        }
        return new Promise((resolve) => {
            GM_xmlhttpRequest({
                method: "GET",
                url: `https://api.github.com/gists/${config.gistId}`,
                headers: { "Authorization": `token ${config.token}`, "Accept": "application/vnd.github.v3+json" },
                timeout: 8000,
                onload: function(response) {
                    try {
                        const gistData = JSON.parse(response.responseText);
                        if (gistData.files && gistData.files[FILENAME] && gistData.files[FILENAME].content) {
                            const notes = JSON.parse(gistData.files[FILENAME].content);
                            console.log('GitHub Gist 云端数据获取成功!');
                            GM_setValue(STORAGE_KEY_NOTES, JSON.stringify(notes));
                            resolve(notes);
                        } else { throw new Error(`云端未找到文件 ${FILENAME} 或内容为空`); }
                    } catch (e) {
                        console.error('解析Gist云端数据失败:', e, response.responseText);
                        const localNotes = GM_getValue(STORAGE_KEY_NOTES, '{}');
                        try { resolve(JSON.parse(localNotes)); } catch { resolve({}); }
                    }
                },
                onerror: function(error) {
                    console.log('从Gist获取备注失败 (onerror), 使用本地缓存:', error);
                    const localNotes = GM_getValue(STORAGE_KEY_NOTES, '{}');
                    try { resolve(JSON.parse(localNotes)); } catch { resolve({}); }
                },
                ontimeout: function() {
                    console.log('从Gist获取备注超时, 使用本地缓存');
                    const localNotes = GM_getValue(STORAGE_KEY_NOTES, '{}');
                    try { resolve(JSON.parse(localNotes)); } catch { resolve({}); }
                }
            });
        });
    }

    async function saveAllNotes(notes) {
        GM_setValue(STORAGE_KEY_NOTES, JSON.stringify(notes));
        console.log('备注已保存到本地');
        const config = await getCloudConfig();
        if (!config) return;
        const contentToSave = JSON.stringify(notes, null, 2);
        const dataPayload = { files: { [FILENAME]: { content: contentToSave } } };
        GM_xmlhttpRequest({
            method: "PATCH",
            url: `https://api.github.com/gists/${config.gistId}`,
            headers: {
                "Authorization": `token ${config.token}`,
                "Accept": "application/vnd.github.v3+json",
                "Content-Type": "application/json"
            },
            data: JSON.stringify(dataPayload),
            onload: function(response) { console.log('备注成功同步到 GitHub Gist!'); },
            onerror: function(error) {
                console.error('Gist同步失败:', error);
                alert('备注已保存到本地,但同步到云端失败,请检查网络或配置。');
            }
        });
    }

    async function showEditPrompt() {
        currentUrl = window.location.href;
        const notes = await getAllNotes();
        const existingNote = notes[currentUrl] || '';
        const newNote = prompt('请输入或编辑此页面的备注:\n(输入空内容并确定,即可删除此备注)', existingNote);
        if (newNote !== null) {
            if (newNote.trim() === '') {
                delete notes[currentUrl];
                alert('备注已清除。');
            } else {
                notes[currentUrl] = newNote;
               //  alert('备注已保存!');
            }
            saveAllNotes(notes);
            mainLogic();
        }
    }

    async function mainLogic() {
        currentUrl = window.location.href;
        if (noteDiv && noteDiv.parentNode) {
            noteDiv.parentNode.removeChild(noteDiv);
            noteDiv = null;
        }
        const allNotes = await getAllNotes();
        const noteForThisPage = allNotes[currentUrl];
        if (noteForThisPage) {
            noteDiv = document.createElement('div');
            noteDiv.innerHTML = `
                <div style="font-weight: bold; border-bottom: 1px solid #ccc; padding-bottom: 5px; margin-bottom: 8px;">网站备注 (Gist)</div>
                <div style="white-space: pre-wrap; max-height: 200px; overflow-y: auto;">${noteForThisPage.replace(/</g, "&lt;").replace(/>/g, "&gt;")}</div>
                <span class="close-note-btn" style="position: absolute; top: 5px; right: 8px; cursor: pointer; font-size: 20px; color: #999;">&times;</span>
            `;
            Object.assign(noteDiv.style, {
                position: 'fixed', top: '20px', right: '20px', width: '280px', padding: '15px',
                backgroundColor: '#f0f8ff', border: '1px solid #d3d3d3', borderRadius: '8px',
                boxShadow: '0 4px 8px rgba(0,0,0,0.2)', zIndex: '99999', fontSize: '14px',
                lineHeight: '1.5', color: '#333'
            });
            document.body.appendChild(noteDiv);
            noteDiv.querySelector('.close-note-btn').addEventListener('click', function() {
                noteDiv.style.display = 'none';
            });
        }
    }
    
    // --- 脚本主执行区 ---
    if (window.top === window.self) {
        
        // ---【已修复】创建“笔”图标的代码块 ---
        const editButton = document.createElement('div');
        editButton.innerHTML = '&#9998;';
        Object.assign(editButton.style, {
            position: 'fixed', bottom: '20px', right: '20px', width: '50px', height: '50px',
            backgroundColor: '#007bff', color: 'white', borderRadius: '50%', display: 'flex',
            justifyContent: 'center', alignItems: 'center', fontSize: '24px', cursor: 'pointer',
            boxShadow: '0 4px 8px rgba(0,0,0,0.3)', zIndex: '99998', transition: 'transform 0.2s'
        });
        editButton.title = '添加或编辑此页备注';
        editButton.addEventListener('click', showEditPrompt);
        editButton.addEventListener('mouseenter', () => { editButton.style.transform = 'scale(1.1)'; });
        editButton.addEventListener('mouseleave', () => { editButton.style.transform = 'scale(1)'; });
        document.body.appendChild(editButton);

        // --- 注册(不可用)菜单命令 ---
        GM_registerMenuCommand('添加/编辑此页备注', showEditPrompt);
        GM_registerMenuCommand('强制从Gist同步', mainLogic);
        GM_registerMenuCommand('重置Gist云同步配置', () => {
            if (confirm('确定要清除已保存的Gist ID和GitHub Token吗?')) {
                GM_deleteValue(STORAGE_KEY_GIST_ID);
                GM_deleteValue(STORAGE_KEY_GIST_TOKEN);
                alert('配置已清除。请刷新页面以重新设置。');
                cloudConfig = null;
            }
        });
        
        // --- URL变化监听器 ---
        let lastUrl = window.location.href;
        const observer = new MutationObserver(() => {
            const url = window.location.href;
            if (url !== lastUrl) {
                lastUrl = url;
                setTimeout(mainLogic, 500);
            }
        });
        observer.observe(document.body, { childList: true, subtree: true });

        // --- 初始加载 ---
        setTimeout(mainLogic, 500);
    }
})();

QingJ © 2025

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