Multi Forum Read Marker (Soutong + TT1069)

标记已访问的帖子标题,支持搜同网和 TT1069 论坛,在任何排序方式下生效,标记放文字标题左边。

À partir de 2025-06-08. Voir la dernière version.

// ==UserScript==
// @name         Multi Forum Read Marker (Soutong + TT1069)
// @namespace    https://felixchristian.dev/userscripts/multi-forum-read-marker
// @version      1.2.1
// @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 HOST = location.hostname;
    const isSoutong = HOST.includes('soutong.men');
    const isTT1069 = HOST.includes('tt1069.com');
    const STORAGE_KEY = 'visitedTids';
    let visitedTids = GM_getValue(STORAGE_KEY, {});

    // 获取当前帖子 TID
    function getCurrentTid() {
        if (location.href.includes('tid=')) {
            const url = new URL(location.href);
            return url.searchParams.get('tid');
        }
        const match = location.href.match(/thread-(\d+)-/);
        return match ? match[1] : null;
    }

    function recordTidVisit(tid) {
        if (tid) {
            visitedTids[tid] = Date.now();
            GM_setValue(STORAGE_KEY, visitedTids);
        }
    }

    // 标记已访问帖子
    function markReadThreads() {
        const threadLinks = document.querySelectorAll('a.s.xst');

        threadLinks.forEach(link => {
            let tid = null;

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

            if (tid && visitedTids[tid] && !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';
            }
        });
    }

    const isViewThread = location.href.includes('mod=viewthread') || /thread-\d+-/.test(location.pathname);
    const isForumDisplay = location.href.includes('mod=forumdisplay') || /forum-\d+-\d+\.html/.test(location.pathname);

    if (isViewThread) {
        const tid = getCurrentTid();
        recordTidVisit(tid);
    }

    if (isForumDisplay) {
        window.addEventListener('load', markReadThreads);
        const observer = new MutationObserver(markReadThreads);
        observer.observe(document.body, { childList: true, subtree: true });
    }
})();