sniperex168.blogspot.com LocalStorage 選項記錄 匯出/匯入工具

點擊時才偵測 localStorage.KEY,並提供匯出/匯入 JSON 功能(改善 script 延遲載入問題)

// ==UserScript==
// @name         sniperex168.blogspot.com LocalStorage 選項記錄 匯出/匯入工具
// @namespace    http://tampermonkey.net/
// @version      2.1
// @description  點擊時才偵測 localStorage.KEY,並提供匯出/匯入 JSON 功能(改善 script 延遲載入問題)
// @author       HrJasn
// @match        *://sniperex168.blogspot.com/*
// @grant        none
// @license      GPL3
// @license      Copyright HrJasn
// ==/UserScript==

console.log("載入 sniperex168.blogspot.com LocalStorage 選項記錄 匯出匯入工具");

(function () {
    console.log("執行 sniperex168.blogspot.com LocalStorage 選項記錄 匯出匯入工具");
    // ✅ 只有點擊時才找 script 內的 localStorage key
    function findLocalStorageKey() {

        // 在 UserScript 中「共用」的變數
        let foundKey = null;

        // 取得原本的 localStorage 物件
        const originalLocalStorage = window.localStorage;

        // 用 Proxy 包裝整個 localStorage,攔截所有屬性賦值(包括 .ED9Kaidata = ... 這種)
        const proxyLocalStorage = new Proxy(originalLocalStorage, {
            set(target, prop, value) {
                console.log(`✅ localStorage 被修改: key=${String(prop)}, value=${value}`);
                if (!foundKey) {
                    foundKey = String(prop);
                    console.log(`✅ 找到的 localStorage Key: ${foundKey}`);
                    cleanup();
                }
                target[prop] = value; // 實際執行賦值
                return true;
            }
        });

        // 替換 window.localStorage 為 Proxy 物件
        Object.defineProperty(window, 'localStorage', {
            configurable: true,
            enumerable: true,
            get() {
                return proxyLocalStorage;
            }
        });

        const confirmElements = document.querySelectorAll('.confirm');

        function cleanup() {
            // 恢復原本的 localStorage
            Object.defineProperty(window, 'localStorage', {
                configurable: true,
                enumerable: true,
                value: originalLocalStorage
            });
            console.log('🛑 已恢復原本的 localStorage 物件');
        }

        (async function autoTriggerEachConfirm() {
            for (let el of confirmElements) {
                if (foundKey) break;

                const originalChecked = el.checked;

                // 嘗試改變勾選
                el.checked = !el.checked;
                console.log('🛑 嘗試異動勾選狀態', el, originalChecked, el.checked);
                el.dispatchEvent(new Event('change', { bubbles: true }));

                await new Promise(r => setTimeout(r, 100));

                // 恢復勾選
                console.log('🛑 恢復勾選狀態', el, el.checked, originalChecked);
                el.checked = originalChecked;
                el.dispatchEvent(new Event('change', { bubbles: true }));

                await new Promise(r => setTimeout(r, 100));

                if (foundKey) break;
            }

            if (!foundKey) {
                alert('❌ 沒有找到任何 localStorage Key');
                cleanup();
            }
        })();

        return foundKey;
    }


    // 加入樣式
    const style = document.createElement('style');
    style.textContent = `
      #json-buttons {
        position: fixed !important;
        bottom: 100px !important;
        right: 20px !important;
        display: flex !important;
        flex-direction: column !important;
        gap: 10px !important;
        z-index: 2147483647 !important;
      }

      .json-btn {
        padding: 8px 12px;
        font-size: 14px;
        background-color: #777;
        color: white;
        border: none;
        border-radius: 5px;
        cursor: pointer;
      }

      .json-btn:hover {
        background-color: #000;
      }
    `;
    document.head.appendChild(style);

    // 建立按鈕區塊
    const container = document.createElement('div');
    container.id = 'json-buttons';

    const exportBtn = document.createElement('button');
    exportBtn.className = 'json-btn';
    exportBtn.textContent = '匯出選項記錄JSON';

    const importBtn = document.createElement('button');
    importBtn.className = 'json-btn';
    importBtn.textContent = '匯入選項記錄JSON';

    const fileInput = document.createElement('input');
    fileInput.type = 'file';
    fileInput.accept = '.json';
    fileInput.style.display = 'none';

    container.appendChild(exportBtn);
    container.appendChild(importBtn);
    container.appendChild(fileInput);
    document.body.appendChild(container);

    // 匯出功能
    exportBtn.addEventListener('click', () => {
        const detectedKey = findLocalStorageKey();
        if (!detectedKey) {
            alert(`找不到 localStorage.Key`);
            return;
        }

        let data = localStorage.getItem(detectedKey);
        if (!data) {
            alert(`找不到 localStorage.${detectedKey} 的資料`);
            return;
        }
        let jsondata = JSON.parse(data);
        jsondata = [...new Set(jsondata)];
        jsondata.sort();
        data = JSON.stringify(jsondata);

        // 產生時間戳記 yyyyMMdd-HHmmss
        const now = new Date();
        const pad = (n) => n.toString().padStart(2, '0');
        const timestamp = `${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;

        // 網址轉為合法檔名
        const urlPart = window.location.href.replace(/[^a-zA-Z0-9]+/g, '_');

        // 完整檔名
        const filename = `${urlPart}_${detectedKey}-${timestamp}.json`;

        const blob = new Blob([data], { type: 'application/json' });
        const url = URL.createObjectURL(blob);
        const a = document.createElement('a');
        a.href = url;
        a.download = filename;
        document.body.appendChild(a);
        a.click();
        document.body.removeChild(a);
        URL.revokeObjectURL(url);
    });


    // 匯入功能
    importBtn.addEventListener('click', () => {
        fileInput.click();
    });

    fileInput.addEventListener('change', (event) => {
        const detectedKey = findLocalStorageKey();
        if (!detectedKey) return;

        const file = event.target.files[0];
        if (!file) return;

        const reader = new FileReader();
        reader.onload = (e) => {
            try {
                const json = JSON.parse(e.target.result);
                localStorage.setItem(detectedKey, JSON.stringify(json));

                const confirms = document.querySelectorAll('.confirm');
                let storagetemp = JSON.parse(localStorage.getItem(detectedKey));
                if (storagetemp !== undefined) {
                    confirms.forEach((elem) => {
                        let chkstts = false;
                        storagetemp.forEach((item) => {
                            if (elem.name == item) {
                                chkstts = true;
                                elem.scrollIntoView({
                                    behavior: "smooth",
                                    block: "center" // 可選值: "start", "center", "end", "nearest"
                                });
                            }
                        });
                        elem.checked = chkstts;
                    });
                }
                storagetemp = [...new Set(storagetemp)];
                storagetemp.sort();
                console.log(`匯入成功:已寫入 localStorage.${detectedKey}`);
            } catch (err) {
                alert('匯入失敗:',err);
                console.log('匯入失敗:',err);
            }
        };
        reader.readAsText(file);
    });
})();
长期地址
遇到问题?请前往 GitHub 提 Issues,或加Q群1031348184

赞助商

Fishcpy

广告

Rainyun

一年攒够 12 元

云驰互联

云驰互联