Multi Forum Read Marker (Soutong + TT1069)

点击帖子链接时立刻标记为已读,支持搜同和TT1069,解决快速跳转丢记录问题。

Устаревшая версия за 08.06.2025. Перейдите к последней версии.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey, Greasemonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Violentmonkey.

Чтобы установить этот скрипт, вы сначала должны установить расширение браузера, например Tampermonkey или Userscripts.

Чтобы установить этот скрипт, сначала вы должны установить расширение браузера, например Tampermonkey.

Чтобы установить этот скрипт, вы должны установить расширение — менеджер скриптов.

(у меня уже есть менеджер скриптов, дайте мне установить скрипт!)

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение браузера, например Stylus.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

Чтобы установить этот стиль, сначала вы должны установить расширение — менеджер стилей.

(у меня уже есть менеджер стилей, дайте мне установить скрипт!)

// ==UserScript==
// @name         Multi Forum Read Marker (Soutong + TT1069)
// @namespace    https://felixchristian.dev/userscripts/multi-forum-read-marker
// @version      1.4.0
// @description  点击帖子链接时立刻标记为已读,支持搜同和TT1069,解决快速跳转丢记录问题。
// @author       FelixChristian
// @license      MIT
// @match        https://soutong.men/forum.php?mod=forumdisplay&fid=*
// @match        https://soutong.men/forum.php?mod=viewthread&tid=*
// @match        https://www.tt1069.com/bbs/thread-*-*-*.html
// @match        https://www.tt1069.com/bbs/forum-*-*.html
// @match        https://www.tt1069.com/bbs/forum.php?mod=forumdisplay&fid=*
// @match        https://www.tt1069.com/bbs/forum.php?mod=forumdisplay&fid=*&*
// @match        https://www.tt1069.com/bbs/forum.php?mod=viewthread&tid=*
// @grant        GM_getValue
// @grant        GM_setValue
// ==/UserScript==

(function () {
    'use strict';

    const STORAGE_KEY = 'visitedTids';
    let visitedTids = {};

    // 初始化访问记录,合并localStorage和GM存储
    function loadVisited() {
        try {
            visitedTids = GM_getValue(STORAGE_KEY, {});
            const localData = localStorage.getItem(STORAGE_KEY + '_temp');
            if (localData) {
                const localObj = JSON.parse(localData);
                visitedTids = {...visitedTids, ...localObj};
            }
        } catch (e) {
            visitedTids = {};
        }
    }
    loadVisited();

    function saveVisited() {
        try {
            GM_setValue(STORAGE_KEY, visitedTids);
            localStorage.setItem(STORAGE_KEY + '_temp', JSON.stringify(visitedTids));
        } catch (e) {}
    }

    function getTidFromUrl(url) {
        try {
            const u = new URL(url, location.origin);
            let tid = u.searchParams.get('tid');
            if (!tid) {
                const match = url.match(/thread-(\d+)-/);
                tid = match ? match[1] : null;
            }
            return tid;
        } catch (e) {
            return null;
        }
    }

    // 给帖子链接加已读标记
    function markReadThreads() {
        const threadLinks = document.querySelectorAll('a.s.xst');
        threadLinks.forEach(link => {
            if (link.dataset.markedVisited) return; // 已标记跳过
            const tid = getTidFromUrl(link.href);
            if (tid && visitedTids[tid]) {
                const tag = document.createElement('span');
                tag.textContent = '[已读] ';
                tag.style.color = 'red';
                tag.style.fontWeight = 'bold';
                tag.style.marginRight = '4px';
                link.insertBefore(tag, link.firstChild);
                link.dataset.markedVisited = 'true';
            }
        });
    }

    // 点击帖子链接时立即标记并保存
    function attachClickListeners() {
        const threadLinks = document.querySelectorAll('a.s.xst');
        threadLinks.forEach(link => {
            if (link.dataset.clickListenerAdded) return;
            link.addEventListener('click', () => {
                const tid = getTidFromUrl(link.href);
                if (!tid) return;
                if (!visitedTids[tid]) {
                    visitedTids[tid] = Date.now();
                    // 立即存储
                    saveVisited();
                    // 立刻显示标记
                    if (!link.dataset.markedVisited) {
                        const tag = document.createElement('span');
                        tag.textContent = '[已读] ';
                        tag.style.color = 'red';
                        tag.style.fontWeight = 'bold';
                        tag.style.marginRight = '4px';
                        link.insertBefore(tag, link.firstChild);
                        link.dataset.markedVisited = 'true';
                    }
                }
            });
            link.dataset.clickListenerAdded = 'true';
        });
    }

    // 页面列表页加载时标记和绑定点击事件
    if (location.href.includes('mod=forumdisplay') || /forum-\d+-\d+\.html/.test(location.pathname)) {
        window.addEventListener('load', () => {
            markReadThreads();
            attachClickListeners();
        });
        const observer = new MutationObserver(() => {
            markReadThreads();
            attachClickListeners();
        });
        observer.observe(document.body, {childList: true, subtree: true});
    }

    // 帖子详情页访问时也保存记录,防止跳转直接访问帖子时丢记录
    if (location.href.includes('mod=viewthread') || /thread-\d+-/.test(location.pathname)) {
        const tid = getTidFromUrl(location.href);
        if (tid && !visitedTids[tid]) {
            visitedTids[tid] = Date.now();
            saveVisited();
        }
    }
})();