Greasy Fork is available in English.

Cubox助手

在 Cubox 左上角添加一个随机漫游的按钮(也可使用 Ctrl+R 触发),支持在指定的收藏夹内随机漫游

// ==UserScript==
// @name         Cubox助手
// @author       岚浅浅
// @description  在 Cubox 左上角添加一个随机漫游的按钮(也可使用 Ctrl+R 触发),支持在指定的收藏夹内随机漫游
// @namespace    http://tampermonkey.net/
// @homepageURL  https://github.com/LanQianqian/greasyForkScripts
// @version      1.0.4
// @include      *cubox.pro/*
// @grant        GM_addStyle
// @license      GPL-3.0 License
// @require      http://libs.baidu.com/jquery/1.8.3/jquery.js
// @require      http://libs.baidu.com/underscore/1.3.3/underscore-min.js
// @require      https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.29.1/moment.min.js
// ==/UserScript==
// jshint esversion: 6

$(function () {

    let TOOLBAR_HTML = `
            <div id='toolbar' class="clickable" style="display:flex;flex-direction:column">
                <div style="margin:2px auto">
                    <a id="start-btn" style="margin:auto">随机漫游</a>
                </div>
            </div>
        `;

    $('body').append(TOOLBAR_HTML);

    $(document).on('click', '#start-btn', function () {
        start();
    });

    // 禁用Crtrl+R刷新页面,而是执行随机漫游
    document.addEventListener('keydown', function (event) {
        if (event.ctrlKey && event.key === 'r') {
            event.preventDefault();
            start();
        }
        if (event.ctrlKey && ['1', '2', '3', '4'].includes(event.key)) {
            event.preventDefault();
            markCurrentCard(parseInt(event.key));
            start();
        }
    });

    let TOKEN = localStorage.getItem('token');

    let currentUrl;
    let isCardPage;

    let cache = new Map();

    refreshStyle();

    if (currentUrl.endsWith('random')) {
        start();
    }

    function refreshStyle() {
        // 获取地址栏的URL
        currentUrl = window.location.href;
        isCardPage = extractCardId(currentUrl) !== null;

        let toolbarTop = !isCardPage ? 25 : 15;
        let toolbarLeft = !isCardPage ? 180 : 120;
        let toolbarCss = `
            #toolbar {
                z-index: 999999;
                position: fixed;
                top: ${toolbarTop}px;
                left: ${toolbarLeft}px;
                width: 120px;
                opacity: 0.6;
                border: 1px solid #a38a54;
                border-radius: 3px;
                background-color: white
            }
            .clickable a {
                cursor: pointer;
            }
        `;
        GM_addStyle(toolbarCss);
    }

    function start() {
        refreshStyle();

        let randomPage;
        let currentPage = 0;
        let searchBase = function (response) {
            let pageCount = response.pageCount;
            randomPage = Math.floor(Math.random() * pageCount) + 1;

            currentPage++;
            if (currentPage === randomPage) {
                viewRandomCard(response);
            } else {
                let cacheKey = `${searchBaseUrl}[${randomPage}]`;
                let lastBookmarkId;
                if (cache.get(cacheKey)) {
                    lastBookmarkId = cache.get(cacheKey);
                    currentPage = randomPage;
                } else {
                    lastBookmarkId = response.data[response.data.length - 1].userSearchEngineID;
                    currentPage++;
                }
                getRequest(searchBaseUrl.replace('page=1', `page=${currentPage}`) + `&lastBookmarkId=${lastBookmarkId}`, searchRandomPage);
            }
        };
        let searchRandomPage = function (response) {
            if (currentPage === randomPage) {
                viewRandomCard(response);
            } else {
                let cacheKey = `${searchBaseUrl}[${currentPage}]`;
                let lastBookmarkId = response.data[response.data.length - 1].userSearchEngineID;
                currentPage++;
                cache.set(cacheKey, lastBookmarkId);
                getRequest(searchBaseUrl.replace('page=1', `page=${currentPage}`) + `&lastBookmarkId=${lastBookmarkId}`, searchRandomPage);
            }
        };

        let searchBaseUrl = localStorage.getItem('searchBaseUrl');
        if (currentUrl.startsWith('https://www.cubox.pro/my/inbox')) {
            searchBaseUrl = 'https://cubox.pro/c/api/v2/search_engine/inbox?asc=false&page=1&filters=&archiving=false';
        } else if (currentUrl.startsWith('https://www.cubox.pro/my/all')) {
            searchBaseUrl = 'https://cubox.pro/c/api/v2/search_engine/my?asc=false&page=1&filters=&archiving=false';
        } else if (currentUrl.startsWith('https://www.cubox.pro/my/folder?id=')) {
            let folderId = currentUrl.split("id=")[1].replace('&random', '');
            searchBaseUrl = `https://cubox.pro/c/api/v2/search_engine/my?asc=false&page=1&filters=&groupId=${folderId}&archiving=false`;
        } else {
            popup('获取不到URL,如果当前位于快照页面,请将鼠标焦点移至页面右上角,而不是图片区域');
        }
        localStorage.setItem('searchBaseUrl', searchBaseUrl);
        getRequest(searchBaseUrl, searchBase);
    }

    function getRequest(url, callback) {
        $.ajax({
            type: 'GET',
            url: url,
            headers: {
                'Authorization': TOKEN
            },
            success: callback
        });
    }

    function viewRandomCard(response) {
        let cardIds = _.map(response.data, function (data) {
            return data.userSearchEngineID
        });
        // 使用洗牌算法打乱cardIds的顺序
        for (let i = cardIds.length - 1; i >= 0; i--) {
            let randomIndex = Math.floor(Math.random() * (i + 1));
            let itemAtIndex = cardIds[randomIndex];
            cardIds[randomIndex] = cardIds[i];
            cardIds[i] = itemAtIndex;
        }

        let latestCardIds = [];
        let latestCardIdStr = localStorage.getItem('latestCardIds');
        if (latestCardIdStr) {
            latestCardIds = latestCardIdStr.split(',');
        }

        // 遍历cardIds,找到第一个未被标记的卡片
        for (let i = 0; i < cardIds.length; i++) {
            let cardId = cardIds[i];
            const key = `card-${cardId}`;
            let valueJson = localStorage.getItem(key);
            if (!valueJson) {
                if (openCard(cardId, latestCardIds)) {
                    return;
                }
            } else {
                let value = JSON.parse(valueJson);
                let due = window.moment(value.due, ['YYYY-MM-DD']);
                if (due.isBefore(window.moment())) {
                    if (openCard(cardId, latestCardIds)) {
                        return;
                    }
                }
            }
        }
    }

    function openCard(cardId, latestCardIds) {
        if (latestCardIds.includes(cardId)) {
            return false;
        }
        latestCardIds.push(cardId);
        localStorage.setItem('latestCardIds', latestCardIds);
        window.open(`https://www.cubox.pro/my/card?id=${cardId}`, '_self');
        return true;
    }

    // 标记当前卡片
    function markCurrentCard(response) {
        let cardId = extractCardId(currentUrl);
        if (!cardId) {
            // 弹窗提示获取不到cardId
            popup('获取不到卡片ID');
            return;
        }
        const key = `card-${cardId}`;
        let ease, interval, delayBeforeReview;
        const now = Date.now();
        let valueJson = localStorage.getItem(key);
        if (!valueJson) {
            ease = 250;
            interval = 1;
            delayBeforeReview = 0;
        } else {
            let value = JSON.parse(valueJson);
            ease = value.ease;
            interval = value.interval;
            delayBeforeReview = now - window.moment(value.due, ['YYYY-MM-DD']).valueOf();
        }
        const schedObj = schedule(
            response,
            interval,
            ease,
            delayBeforeReview
        );
        interval = schedObj.interval;
        ease = schedObj.ease;
        const due = window.moment(now + interval * 24 * 3600 * 1e3);
        const dueString = due.format("YYYY-MM-DD");
        let value = {
            'due': dueString,
            'interval': interval,
            'ease': ease
        };
        localStorage.setItem(key, JSON.stringify(value));
    }

    function schedule(response, interval, ease, delayBeforeReview) {
        delayBeforeReview = Math.max(0, Math.floor(delayBeforeReview / (24 * 3600 * 1e3)));
        if (response === 4 /* Very Easy */) {
            ease += 20;
            interval = (interval + delayBeforeReview) * ease / 100;
            interval *= 4;
        } if (response === 3 /* Easy */) {
            ease += 20;
            interval = (interval + delayBeforeReview) * ease / 100;
            interval *= 1.3;
        } else if (response === 2 /* Good */) {
            interval = (interval + delayBeforeReview / 2) * ease / 100;
        } else if (response === 1 /* Hard */) {
            ease = Math.max(130, ease - 20);
            interval = Math.max(
                1,
                (interval + delayBeforeReview / 4) * 50
            );
        }
        interval = Math.min(interval, 365);
        return { interval: Math.round(interval * 10) / 10, ease };
    }

    function extractCardId(url) {
        // 正则提取 https://www.cubox.pro/my/card?id={cardId},如果提取不到,返回null
        let regex = /.*cubox.pro\/my\/card\?id=(\d+)/;
        let result = regex.exec(url);
        if (result) {
            return result[1];
        } else {
            return null;
        }
    }

    // 浮动层提示用户
    function popup(message) {
        // 创建浮动层
        var popup = document.createElement('div');
        popup.textContent = message;
        popup.style.position = 'fixed';
        popup.style.top = '10%';
        popup.style.left = '50%';
        popup.style.transform = 'translate(-50%, -50%)';
        popup.style.padding = '20px';
        popup.style.backgroundColor = 'rgba(255, 255, 255, 0.8)';
        popup.style.color = '#000';
        popup.style.fontSize = '24px';

        // 插入浮动层到页面中
        document.body.appendChild(popup);

        // 一定时间后隐藏浮动层
        setTimeout(function () {
            popup.style.display = 'none';
        }, 3000);
    }
});