Greasy Fork

来自缓存

Greasy Fork is available in English.

夸克懒得点 (WebDAV同步+防重复版)

夸克懒得点.. 修复误判,精准屏蔽,自动记录日志,支持 WebDAV 同步,支持检测重复链接

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         夸克懒得点 (WebDAV同步+防重复版)
// @namespace    http://greasyfork.icu/users/158417
// @version      0.36
// @description  夸克懒得点.. 修复误判,精准屏蔽,自动记录日志,支持 WebDAV 同步,支持检测重复链接
// @author       JIEMO
// @match        *://pan.quark.cn/*
// @icon         https://pan.quark.cn/favicon.ico
// @license      GPL-3.0 License
// @run-at       document-end
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_unregisterMenuCommand
// @grant        GM_setClipboard
// @grant        GM_xmlhttpRequest
// @connect      *
// ==/UserScript==

(function() {
    'use strict';

    // ================= 配置区域 =================
    const STORAGE_KEY = "blocked_users_v2"; // 屏蔽列表键名
    const LOG_KEY = "auto_save_logs";       // 日志存储键名
    const WEBDAV_CONF_KEY = "webdav_config"; // WebDAV配置
    const MAX_LOGS = 300;                   // 最大保留日志条数
    const CLOUD_FILE_NAME = "quark_script_data.json"; // 云端文件名
    const DEFAULT_BLOCKED = [];
    // ===========================================

    // ============================================================
    // 1. 基础工具 & WebDAV 模块
    // ============================================================

    function getBlockedList() { return GM_getValue(STORAGE_KEY, DEFAULT_BLOCKED); }
    function setBlockedList(list) { GM_setValue(STORAGE_KEY, list); }
    function getLogs() { return GM_getValue(LOG_KEY, []); }
    function setLogs(list) { GM_setValue(LOG_KEY, list); }

    function formatTime(date) {
        const y = date.getFullYear();
        const m = String(date.getMonth() + 1).padStart(2, '0');
        const d = String(date.getDate()).padStart(2, '0');
        const h = String(date.getHours()).padStart(2, '0');
        const min = String(date.getMinutes()).padStart(2, '0');
        const s = String(date.getSeconds()).padStart(2, '0');
        return `${y}-${m}-${d} ${h}:${min}:${s}`;
    }

    function computeStringHash(str) {
        if (!str) return "null";
        let hash = 0;
        for (let i = 0; i < str.length; i++) {
            const char = str.charCodeAt(i);
            hash = (hash << 5) - hash + char;
            hash |= 0;
        }
        return "u" + Math.abs(hash);
    }

    // --- WebDAV 核心逻辑 ---
    const WebDAV = {
        getConfig: () => GM_getValue(WEBDAV_CONF_KEY, { url: "", user: "", pass: "" }),
        setConfig: (conf) => GM_setValue(WEBDAV_CONF_KEY, conf),

        pull: function(callback) {
            const conf = this.getConfig();
            if (!conf.url) { if(callback) callback(); return; }

            console.log("[夸克懒得点] 正在从云端拉取数据...");
            const fileUrl = conf.url.endsWith('/') ? conf.url + CLOUD_FILE_NAME : conf.url + '/' + CLOUD_FILE_NAME;

            GM_xmlhttpRequest({
                method: "GET",
                url: fileUrl,
                user: conf.user,
                password: conf.pass,
                headers: { "Cache-Control": "no-cache" }, // 防止缓存
                onload: function(response) {
                    if (response.status >= 200 && response.status < 300) {
                        try {
                            const cloudData = JSON.parse(response.responseText);
                            WebDAV.mergeData(cloudData);
                            console.log("[夸克懒得点] ✅ 云端同步成功");
                        } catch (e) {
                            console.error("[夸克懒得点] 解析云端数据失败", e);
                        }
                    } else if (response.status === 404) {
                        console.log("[夸克懒得点] 云端文件不存在,将在下次保存时创建");
                    } else {
                        console.error(`[夸克懒得点] 拉取失败: ${response.status} ${response.statusText}`);
                    }
                    if(callback) callback();
                },
                onerror: function(err) {
                    console.error("[夸克懒得点] 网络请求错误 (Pull)", err);
                    if(callback) callback();
                }
            });
        },

        push: function() {
            const conf = this.getConfig();
            if (!conf.url) return;

            const data = {
                blocked: getBlockedList(),
                logs: getLogs(),
                updated: new Date().getTime()
            };
            const fileUrl = conf.url.endsWith('/') ? conf.url + CLOUD_FILE_NAME : conf.url + '/' + CLOUD_FILE_NAME;

            console.log("[夸克懒得点] 正在上传数据到云端...", fileUrl);

            GM_xmlhttpRequest({
                method: "PUT",
                url: fileUrl,
                user: conf.user,
                password: conf.pass,
                data: JSON.stringify(data),
                headers: {
                    "Content-Type": "application/json;charset=UTF-8" // 明确告诉服务器这是JSON
                },
                onload: function(response) {
                    if (response.status >= 200 && response.status < 300) {
                        console.log("[夸克懒得点] ✅ 上传成功");
                    } else {
                        console.error(`[夸克懒得点] ❌ 上传失败: ${response.status} ${response.statusText}`);
                        // 提示用户检查 F12
                        if(response.status === 401) alert("WebDAV同步失败:账号或密码错误 (401)");
                        if(response.status === 403) alert("WebDAV同步失败:权限不足 (403)");
                        if(response.status === 405) alert("WebDAV同步失败:服务器不支持 PUT 方法 (405)");
                    }
                },
                onerror: function(err) {
                    console.error("[夸克懒得点] 网络请求错误 (Push)", err);
                    alert("WebDAV 连接失败,请检查 F12 控制台错误信息");
                }
            });
        },

        mergeData: function(cloudData) {
            if (!cloudData) return;

            // 1. 合并屏蔽列表
            let localBlocked = getBlockedList();
            const localHashes = new Set(localBlocked.map(u => u.hash));
            let hasChange = false;

            if (cloudData.blocked && Array.isArray(cloudData.blocked)) {
                cloudData.blocked.forEach(u => {
                    if (!localHashes.has(u.hash)) {
                        localBlocked.push(u);
                        hasChange = true;
                    }
                });
            }
            if (hasChange) setBlockedList(localBlocked);

            // 2. 合并日志
            let localLogs = getLogs();
            if (cloudData.logs && Array.isArray(cloudData.logs)) {
                const uniqueSet = new Set(localLogs.map(l => l.url));
                cloudData.logs.forEach(l => {
                    if (!uniqueSet.has(l.url)) {
                        localLogs.push(l);
                        uniqueSet.add(l.url);
                    }
                });
                localLogs.sort((a, b) => new Date(b.time) - new Date(a.time));
                if (localLogs.length > MAX_LOGS) localLogs = localLogs.slice(0, MAX_LOGS);
                setLogs(localLogs);
            }
        }
    };

    // ============================================================
    // 2. 核心逻辑:提取信息
    // ============================================================

    function getTargetSharerInfo() {
        const shareContainer = document.querySelector('.share-info-wrap');
        if (!shareContainer) return null;

        const imgElement = shareContainer.querySelector('img');
        if (!imgElement || !imgElement.src) return null;
        const hashID = computeStringHash(imgElement.src);

        const nameElement = shareContainer.querySelector('.author-name');
        let nickName = "Unknown";
        if (nameElement) {
            nickName = nameElement.innerText.trim();
        } else {
            const possibleNames = shareContainer.querySelectorAll('div');
            if(possibleNames.length > 1) {
                nickName = possibleNames[1].innerText.trim();
            }
        }
        return { name: nickName, hash: hashID };
    }

    function getFileTitle() {
        const titleEl = document.querySelector('.filename-text');
        if (titleEl) {
            return titleEl.getAttribute('title') || titleEl.innerText.trim();
        }
        return document.title.replace(' - 夸克网盘', '') || "未知标题";
    }

    // ============================================================
    // 3. 日志记录逻辑
    // ============================================================

    function recordLog(user) {
        const fileName = getFileTitle();
        const currentTime = formatTime(new Date());
        const currentUrl = window.location.href;

        let logs = getLogs().filter(l => l.url !== currentUrl);

        const newLog = {
            time: currentTime,
            name: user.name,
            hash: user.hash,
            title: fileName,
            url: currentUrl
        };

        logs.unshift(newLog);
        if (logs.length > MAX_LOGS) logs = logs.slice(0, MAX_LOGS);
        setLogs(logs);

        console.log(`[夸克懒得点] 日志已记录: ${fileName}`);
        WebDAV.push();
    }

    // ============================================================
    // 4. UI 交互
    // ============================================================

    function showBlockedOverlay(user) {
        var overlay = document.createElement('div');
        Object.assign(overlay.style, {
            position: 'fixed', top: '0', left: '0', width: '100%', height: '100%',
            backgroundColor: 'rgba(0, 0, 0, 0.95)', zIndex: '999999',
            display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column'
        });

        var text = document.createElement('h1');
        text.innerText = "⚠️ 已屏蔽该分享者";
        text.style.cssText = "color: red; font-size: 60px; font-weight: bold; text-shadow: 2px 2px 10px black; margin: 0;";

        var subText = document.createElement('div');
        subText.innerHTML = `<p style='font-size:24px; color:white'>昵称:<span style='color:#ff6a00'>${user.name}</span></p>`;

        var unlockBtn = document.createElement('button');
        unlockBtn.innerText = "本次临时允许";
        unlockBtn.style.cssText = "margin-top: 30px; padding: 10px 20px; cursor: pointer; background: #333; color: #fff; border: 1px solid #666;";
        unlockBtn.onclick = function() { overlay.remove(); };

        overlay.appendChild(text);
        overlay.appendChild(subText);
        overlay.appendChild(unlockBtn);
        document.body.appendChild(overlay);
    }

    function showDuplicateOverlay(log, callback) {
        var overlay = document.createElement('div');
        Object.assign(overlay.style, {
            position: 'fixed', top: '0', left: '0', width: '100%', height: '100%',
            backgroundColor: 'rgba(0, 0, 0, 0.90)', zIndex: '999999',
            display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column'
        });

        var text = document.createElement('h1');
        text.innerText = "🔁 此链接已保存过";
        text.style.cssText = "color: #FFD700; font-size: 50px; font-weight: bold; text-shadow: 2px 2px 5px black; margin: 0;";

        var infoDiv = document.createElement('div');
        infoDiv.style.cssText = "margin-top:20px; color: #ddd; text-align:center; font-size: 16px; line-height: 1.6;";
        infoDiv.innerHTML = `
            <p>上次保存时间: <span style="color:white; font-weight:bold">${log.time}</span></p>
            <p>文件标题: ${log.title}</p>
        `;

        var btnContainer = document.createElement('div');
        btnContainer.style.marginTop = "40px";

        var cancelBtn = document.createElement('button');
        cancelBtn.innerText = "我知道了 (关闭页面)";
        cancelBtn.style.cssText = "padding: 10px 20px; cursor: pointer; background: #444; color: #fff; border: none; border-radius: 4px; margin-right: 20px;";
        cancelBtn.onclick = function() { window.close(); overlay.remove(); };

        var forceBtn = document.createElement('button');
        forceBtn.innerText = "强制再次保存";
        forceBtn.style.cssText = "padding: 10px 20px; cursor: pointer; background: #007bff; color: #fff; border: none; border-radius: 4px;";
        forceBtn.onclick = function() {
            overlay.remove();
            if (callback) callback();
        };

        btnContainer.appendChild(cancelBtn);
        btnContainer.appendChild(forceBtn);
        overlay.appendChild(text);
        overlay.appendChild(infoDiv);
        overlay.appendChild(btnContainer);
        document.body.appendChild(overlay);
    }

    function showLogViewer() {
        const logs = getLogs();
        let logText = "时间\t\t\t昵称\t\t文件标题\t\t\t网址\n";
        logText += "--------------------------------------------------------------------------------------\n";
        logs.forEach(log => {
            logText += `[${log.time}]  ${log.name}  >>>  ${log.title}  >>>  ${log.url}\n`;
        });

        var overlay = document.createElement('div');
        Object.assign(overlay.style, {
            position: 'fixed', top: '0', left: '0', width: '100%', height: '100%',
            backgroundColor: 'rgba(0,0,0,0.8)', zIndex: '999999',
            display: 'flex', justifyContent: 'center', alignItems: 'center'
        });

        var box = document.createElement('div');
        Object.assign(box.style, {
            width: '85%', height: '85%', backgroundColor: '#fff', borderRadius: '8px',
            padding: '20px', display: 'flex', flexDirection: 'column', color: '#333'
        });

        var title = document.createElement('h2');
        title.innerText = `📜 保存记录 (共 ${logs.length} 条)`;
        title.style.margin = '0 0 10px 0';

        var textarea = document.createElement('textarea');
        textarea.value = logText;
        Object.assign(textarea.style, {
            flex: '1', width: '100%', fontSize: '12px', fontFamily: 'monospace',
            whiteSpace: 'pre', overflow: 'auto', border: '1px solid #ccc', padding: '10px'
        });

        var btnContainer = document.createElement('div');
        btnContainer.style.marginTop = '10px';
        btnContainer.style.textAlign = 'right';

        var copyBtn = document.createElement('button');
        copyBtn.innerText = "复制";
        copyBtn.style.marginRight = "10px";
        copyBtn.onclick = function() { GM_setClipboard(logText); alert("✅ 已复制!"); };

        var forceSyncBtn = document.createElement('button');
        forceSyncBtn.innerText = "☁️ 立即同步";
        forceSyncBtn.style.marginRight = "10px";
        forceSyncBtn.style.color = "blue";
        forceSyncBtn.onclick = function() {
            WebDAV.pull(() => {
                WebDAV.push();
                alert("同步指令已发送,请查看控制台或稍后重试");
                overlay.remove();
                showLogViewer();
            });
        };

        var clearBtn = document.createElement('button');
        clearBtn.innerText = "清空";
        clearBtn.style.color = "red";
        clearBtn.style.marginRight = "10px";
        clearBtn.onclick = function() {
            if(confirm("确定要清空?")) { setLogs([]); WebDAV.push(); textarea.value = ""; }
        };

        var closeBtn = document.createElement('button');
        closeBtn.innerText = "关闭";
        closeBtn.onclick = function() { overlay.remove(); };

        btnContainer.appendChild(forceSyncBtn);
        btnContainer.appendChild(clearBtn);
        btnContainer.appendChild(copyBtn);
        btnContainer.appendChild(closeBtn);

        box.appendChild(title);
        box.appendChild(textarea);
        box.appendChild(btnContainer);
        overlay.appendChild(box);
        document.body.appendChild(overlay);
    }

    function showWebDAVConfig() {
        const conf = WebDAV.getConfig();
        const url = prompt("WebDAV 地址:", conf.url);
        if (url === null) return;
        const user = prompt("WebDAV 账号:", conf.user);
        if (user === null) return;
        const pass = prompt("WebDAV 密码:", conf.pass);
        if (pass === null) return;

        WebDAV.setConfig({ url, user, pass });
        alert("✅ 配置保存,正在尝试连接...");
        WebDAV.pull();
    }

    function registerMenus() {
        GM_registerMenuCommand("🚫 屏蔽当前分享者", function() {
            const currentUser = getTargetSharerInfo();
            if (!currentUser) { alert("页面未加载完成"); return; }
            const list = getBlockedList();
            if (!list.some(u => u.name === currentUser.name && u.hash === currentUser.hash)) {
                list.push(currentUser);
                setBlockedList(list);
                WebDAV.push();
                if(confirm(`✅ 已屏蔽: ${currentUser.name}\n刷新?`)) location.reload();
            }
        });
        GM_registerMenuCommand("⚙️ 管理屏蔽列表", function() {
            const list = getBlockedList();
            let msg = "屏蔽列表:\n";
            list.forEach((u, i) => msg += `【${i+1}】${u.name}\n`);
            const input = prompt(msg + "\n输入序号删除:");
            if (input) {
                list.splice(parseInt(input)-1, 1);
                setBlockedList(list);
                WebDAV.push();
                alert("✅ 已删除");
                location.reload();
            }
        });
        GM_registerMenuCommand("📜 查看日志", showLogViewer);
        GM_registerMenuCommand("☁️ WebDAV 设置", showWebDAVConfig);
    }

    registerMenus();

    // ============================================================
    // 5. 主程序执行
    // ============================================================

    function executeSaveAction(currentUser) {
        console.log("[夸克懒得点] 执行转存...");

        var checkboxElement = document.querySelector('.ant-checkbox-input');
        try { if (checkboxElement && !checkboxElement.checked) checkboxElement.click(); } catch (e) {}

        var saveButtonElement = document.querySelector('.share-save');
        if (saveButtonElement) {
            if (currentUser) recordLog(currentUser);
            saveButtonElement.click();
        } else {
            var saveButtonElement2 = document.querySelector('.file-info_r');
            if (saveButtonElement2) {
                if (currentUser) recordLog(currentUser);
                saveButtonElement2.click();
            }
        }

        setTimeout(function() {
            var confirmButtonElement = document.querySelector('.confirm-btn');
            if (confirmButtonElement) confirmButtonElement.click();

            var intervalId = setInterval(function() {
                var viewButtonElement = document.querySelector('.path');
                if (viewButtonElement) {
                    viewButtonElement.click();
                    clearInterval(intervalId);
                }
            }, 1000);
        }, 1000);
    }

    if (window.location.href.startsWith("https://pan.quark.cn/s/")) {
        window.onload = function() {
            WebDAV.pull(() => {
                setTimeout(function() {
                    const currentUser = getTargetSharerInfo();

                    if (currentUser) {
                        const blockedList = getBlockedList();
                        if (blockedList.some(u => u.name === currentUser.name && u.hash === currentUser.hash)) {
                            console.warn(`[夸克懒得点] 已屏蔽: ${currentUser.name}`);
                            showBlockedOverlay(currentUser);
                            return;
                        }
                    }

                    const currentUrl = window.location.href;
                    const logs = getLogs();
                    const existingLog = logs.find(l => l.url === currentUrl);

                    if (existingLog) {
                        console.warn("[夸克懒得点] 检测到重复链接,暂停保存");
                        showDuplicateOverlay(existingLog, function() {
                            executeSaveAction(currentUser);
                        });
                        return;
                    }

                    executeSaveAction(currentUser);

                }, 1000);
            });
        };
    }

    if (window.location.href.startsWith("https://pan.quark.cn/list")) {
        window.onload = function() {
            setTimeout(function() {
                var checkboxElement = document.querySelector('.ant-checkbox-wrapper');
                try { if(checkboxElement) checkboxElement.click(); } catch (error) {}
            }, 1000);
        };
    }

})();