Threads V1.21

自動瀏覽 Threads 文章,模擬真實使用行為。工具欄新增【開始 🚀】與【暫停 ⛔】按鈕,並在狀態欄顯示當前狀態、倒計時與完整循環次數(每完成一次目標頁與首頁瀏覽算一輪)。請用於刷文章觀看。

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey, το Greasemonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Violentmonkey για να εγκαταστήσετε αυτόν τον κώδικα.

θα χρειαστεί να εγκαταστήσετε μια επέκταση όπως το Tampermonkey ή το Userscripts για να εγκαταστήσετε αυτόν τον κώδικα.

You will need to install an extension such as Tampermonkey to install this script.

Θα χρειαστεί να εγκαταστήσετε μια επέκταση διαχείρισης κώδικα χρήστη για να εγκαταστήσετε αυτόν τον κώδικα.

(Έχω ήδη έναν διαχειριστή κώδικα χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(Έχω ήδη έναν διαχειριστή στυλ χρήστη, επιτρέψτε μου να τον εγκαταστήσω!)

// ==UserScript==
// @name         Threads V1.21
// @namespace    http://tampermonkey.net/
// @version      1.3
// @description  自動瀏覽 Threads 文章,模擬真實使用行為。工具欄新增【開始 🚀】與【暫停 ⛔】按鈕,並在狀態欄顯示當前狀態、倒計時與完整循環次數(每完成一次目標頁與首頁瀏覽算一輪)。請用於刷文章觀看。
// @author       ChatGPT
// @match        https://www.threads.net/*
// @grant        none
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // ========= 初始設定 =========
    // 讀取或設定目標文章與首頁 URL(若 localStorage 中無則採用預設值)
    let targetUrl = localStorage.getItem("THREADS_TARGET_URL") || "https://www.threads.net/posts/xxxxxx"; // 請替換預設目標文章 URL(請注意目標文章 URL 應包含 "/post/" 或 "/posts/")
    let homeUrl = localStorage.getItem("HOME_URL") || "https://www.threads.net"; // 預設首頁 URL
    localStorage.setItem("THREADS_TARGET_URL", targetUrl);
    localStorage.setItem("HOME_URL", homeUrl);

    // 使用 localStorage 儲存自動運行旗標與循環次數(每次進入目標文章頁視為完成一輪循環)
    const AUTO_FLAG = "THREADS_AUTOMATION_RUNNING";
    const LOOP_COUNT_KEY = "LOOP_COUNT";

    // ========= 時間參數(毫秒) =========
    const STAY_TIME = [30000, 60000];       // 目標文章頁瀏覽:30~60秒
    const BROWSE_TIME = [120000, 240000];     // 首頁瀏覽:2~4分鐘,原3~5分鐘
    const SCROLL_INTERVAL = [2000, 5000];     // 滾動間隔:2~5秒

    // ========= 工具函式 =========
    function randomDelay(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    async function wait(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    // 倒計時等待,每秒更新,並顯示當前階段描述
    async function countdown(ms, description) {
        let seconds = Math.ceil(ms / 1000);
        while (seconds > 0 && localStorage.getItem(AUTO_FLAG) === "true") {
            countdownDisplay.textContent = `${description} 倒數:${seconds}秒`;
            await wait(1000);
            seconds--;
        }
    }

    // 更新狀態顯示
    function updateStatus(text) {
        statusDisplay.textContent = "狀態:" + text;
    }
    // 寫入日誌
    function logMessage(msg) {
        const div = document.createElement('div');
        div.textContent = msg;
        logArea.appendChild(div);
        logArea.scrollTop = logArea.scrollHeight;
    }

    // 隨機滾動一次,增加隨機上下滾動效果(模擬真實使用)
    async function scrollPage() {
        logMessage("👉 開始滾動頁面...");
        const scrollStep = window.innerHeight * (Math.random() * 0.5 + 0.5);
        // 70% 機率向下滾,30% 機率向上滾
        const direction = Math.random() < 0.7 ? 1 : -1;
        window.scrollBy({ top: scrollStep * direction, behavior: 'smooth' });
        await wait(randomDelay(...SCROLL_INTERVAL));
        logMessage("✅ 完成滾動");
    }

    // ========= 模擬流程 =========

    // 模擬目標文章頁瀏覽(包含隨機滾動與倒計時等待,完成後等待一段時間再跳轉至首頁)
    async function simulateTargetPage() {
        // 判斷條件修改為:只要 URL 包含 "/post/" 或 "/posts/" 就認為是目標文章頁
        if (!(window.location.href.includes("/post/") || window.location.href.includes("/posts/"))) return;
        // 累計循環次數,每進入目標文章頁即累加
        let count = parseInt(localStorage.getItem(LOOP_COUNT_KEY) || "0") + 1;
        localStorage.setItem(LOOP_COUNT_KEY, count.toString());
        cycleCountDisplay.textContent = "已循環:" + count + " 次";
        updateStatus("瀏覽目標文章中 📄");
        logMessage("🔔 開始在目標文章頁模擬瀏覽...");
        let duration = randomDelay(...STAY_TIME);
        let startTime = Date.now();
        while (Date.now() - startTime < duration && localStorage.getItem(AUTO_FLAG) === "true") {
            await scrollPage();
            let remaining = Math.ceil((duration - (Date.now() - startTime)) / 1000);
            countdownDisplay.textContent = "目標頁倒數:" + remaining + "秒";
        }
        if (localStorage.getItem(AUTO_FLAG) === "true") {
            updateStatus("結束目標文章瀏覽,準備返回首頁 🏠");
            let waitTime = randomDelay(10000, 20000);
            await countdown(waitTime, "返回首頁等待");
            window.location.href = homeUrl;
        }
    }

    // 模擬首頁瀏覽(包含隨機滾動、倒計時等待,以及隨機點擊文章內部模擬瀏覽,再返回首頁)
    async function simulateHomePage() {
        if (window.location.href.includes("/post/") || window.location.href.includes("/posts/")) return;
        updateStatus("瀏覽首頁中 🌐");
        logMessage("🔔 開始在首頁模擬瀏覽...");
        const startTime = Date.now();
        const browseDuration = randomDelay(...BROWSE_TIME);
        let clickCount = 0; // 控制隨機點擊文章次數(最多 2 次)
        while (Date.now() - startTime < browseDuration && localStorage.getItem(AUTO_FLAG) === "true") {
            await scrollPage();
            let remaining = Math.ceil((browseDuration - (Date.now() - startTime)) / 1000);
            countdownDisplay.textContent = "首頁倒數:" + remaining + "秒";
            // 隨機觸發文章點擊(模擬使用者點進文章閱讀),機率 30%
            if (Math.random() < 0.3 && clickCount < 5) {
                logMessage("隨機點擊一篇文章...");
                let posts = document.querySelectorAll('.x1xdureb.xkbb5z.x13vxnyz');
                if (posts.length > 0) {
                    const randomPost = posts[Math.floor(Math.random() * posts.length)];
                    randomPost.click();
                    logMessage("點擊了文章,等待模擬瀏覽...");
                    // 模擬在文章內瀏覽,停留 5~15秒
                    let articleDuration = randomDelay(5000, 15000);
                    let articleStart = Date.now();
                    while (Date.now() - articleStart < articleDuration && localStorage.getItem(AUTO_FLAG) === "true") {
                        await scrollPage();
                    }
                    // 返回首頁(使用瀏覽器的 history.back() 模擬返回)
                    window.history.back();
                    logMessage("返回首頁...");
                    await wait(randomDelay(2000, 5000));  // 停留 2~5秒
                    clickCount++;
                }
            }
        }
        if (localStorage.getItem(AUTO_FLAG) === "true") {
            updateStatus("結束首頁瀏覽,準備返回目標文章 📄");
            let waitTime = randomDelay(5000, 10000);
            await countdown(waitTime, "返回目標等待");
            window.location.href = targetUrl;
        }
    }

    // 自動恢復流程:根據當前 URL 執行對應的模擬過程
    function autoContinue() {
        if (localStorage.getItem(AUTO_FLAG) === "true") {
            if (window.location.href.includes("/post/") || window.location.href.includes("/posts/")) {
                simulateTargetPage();
            } else {
                simulateHomePage();
            }
        } else {
            updateStatus("待命");
        }
    }

    // ========= 建立工具欄 =========
    // 加入 Emoji 使介面更活潑
    const controlPanelDiv = document.createElement('div');
    controlPanelDiv.style.position = 'fixed';
    controlPanelDiv.style.top = '10px';
    controlPanelDiv.style.right = '10px';
    controlPanelDiv.style.backgroundColor = '#f1f1f1';
    controlPanelDiv.style.padding = '10px';
    controlPanelDiv.style.border = '1px solid #ccc';
    controlPanelDiv.style.zIndex = '9999';
    controlPanelDiv.style.fontSize = '14px';
    controlPanelDiv.style.maxWidth = '300px';

    // 【更新目標文章 ✏️】按鈕
    const updateTargetBtn = document.createElement('button');
    updateTargetBtn.textContent = "更新目標文章 ✏️";
    updateTargetBtn.style.display = 'block';
    updateTargetBtn.style.marginBottom = '5px';
    updateTargetBtn.onclick = function() {
        const newPostUrl = prompt("請輸入新的目標文章 URL:");
        if (newPostUrl) {
            targetUrl = newPostUrl;
            localStorage.setItem("THREADS_TARGET_URL", targetUrl);
            logMessage("🔄 目標文章連結已更新:" + targetUrl);
            updateStatus("目標文章更新完畢");
        }
    };

    // 【更新首頁 URL 🌐】按鈕
    const updateHomeBtn = document.createElement('button');
    updateHomeBtn.textContent = "更新首頁 URL 🌐";
    updateHomeBtn.style.display = 'block';
    updateHomeBtn.style.marginBottom = '5px';
    updateHomeBtn.onclick = function() {
        const newHomeUrl = prompt("請輸入新的首頁 URL:");
        if (newHomeUrl) {
            homeUrl = newHomeUrl;
            localStorage.setItem("HOME_URL", homeUrl);
            logMessage("🔄 首頁連結已更新:" + homeUrl);
            updateStatus("首頁更新完畢");
        }
    };

    // 開始與暫停按鈕
    const startBtn = document.createElement('button');
    startBtn.textContent = "開始 🚀";
    startBtn.style.marginRight = '10px';
    const pauseBtn = document.createElement('button');
    pauseBtn.textContent = "暫停 ⛔";

    // 將開始與暫停按鈕放在同一行
    const btnContainer = document.createElement('div');
    btnContainer.appendChild(startBtn);
    btnContainer.appendChild(pauseBtn);

    // 狀態顯示區
    const statusDisplay = document.createElement('div');
    statusDisplay.style.backgroundColor = '#fff';
    statusDisplay.style.border = '1px solid #ccc';
    statusDisplay.style.padding = '4px';
    statusDisplay.style.margin = '4px 0';
    statusDisplay.textContent = "狀態:待命";

    // 倒計時顯示區
    const countdownDisplay = document.createElement('div');
    countdownDisplay.style.backgroundColor = '#fff';
    countdownDisplay.style.border = '1px solid #ccc';
    countdownDisplay.style.padding = '4px';
    countdownDisplay.style.margin = '4px 0';
    countdownDisplay.textContent = "倒計時:";

    // 循環次數顯示區
    const cycleCountDisplay = document.createElement('div');
    cycleCountDisplay.style.backgroundColor = '#fff';
    cycleCountDisplay.style.border = '1px solid #ccc';
    cycleCountDisplay.style.padding = '4px';
    cycleCountDisplay.style.margin = '4px 0';
    cycleCountDisplay.textContent = "已循環:0 次";

    // 日誌顯示區
    const logArea = document.createElement('div');
    logArea.style.height = '200px';
    logArea.style.overflowY = 'auto';
    logArea.style.backgroundColor = '#fff';
    logArea.style.border = '1px solid #ccc';
    logArea.style.padding = '5px';

    // 組裝控制面板
    controlPanelDiv.appendChild(updateTargetBtn);
    controlPanelDiv.appendChild(updateHomeBtn);
    controlPanelDiv.appendChild(btnContainer);
    controlPanelDiv.appendChild(statusDisplay);
    controlPanelDiv.appendChild(countdownDisplay);
    controlPanelDiv.appendChild(cycleCountDisplay);
    controlPanelDiv.appendChild(logArea);
    document.body.appendChild(controlPanelDiv);

    // --------------------
    // 開始按鈕事件:設置 AUTO_FLAG 為 "true",重置循環計數,然後開始流程
    // --------------------
    startBtn.addEventListener('click', function() {
        localStorage.setItem(AUTO_FLAG, "true");
        localStorage.setItem(LOOP_COUNT_KEY, "0");
        cycleCountDisplay.textContent = "已循環:0 次";
        logMessage("🚀 開始模擬...");
        updateStatus("開始模擬");
        // 若當前頁面不在目標文章頁(判斷條件:同時支援 "/post/" 或 "/posts/"),則跳轉
        if (!(window.location.href.includes("/post/") || window.location.href.includes("/posts/"))) {
            window.location.href = targetUrl;
        } else {
            simulateTargetPage();
        }
    });

    // 暫停按鈕事件:將 AUTO_FLAG 設為 "false",重置循環計數
    pauseBtn.addEventListener('click', function() {
        localStorage.setItem(AUTO_FLAG, "false");
        logMessage("⛔ 模擬已暫停");
        updateStatus("已暫停");
        cycleCountDisplay.textContent = "已循環:0 次";
    });

    // --------------------
    // 當頁面載入時,自動檢查是否需要恢復模擬
    // --------------------
    window.addEventListener('load', function() {
        if (localStorage.getItem(AUTO_FLAG) === "true") {
            logMessage("🔄 自動啟動檢測:恢復模擬");
            let cnt = localStorage.getItem(LOOP_COUNT_KEY) || "0";
            cycleCountDisplay.textContent = "已循環:" + cnt + " 次";
            if (window.location.href.includes("/post/") || window.location.href.includes("/posts/")) {
                simulateTargetPage();
            } else {
                simulateHomePage();
            }
        } else {
            updateStatus("待命");
            logMessage("頁面載入完成,請更新目標文章與首頁 URL,再點【開始 🚀】按鈕");
        }
    });

    // --------------------
    // 自動恢復:根據當前 URL 執行對應模擬流程
    // --------------------
    function autoContinue() {
        if (localStorage.getItem(AUTO_FLAG) === "true") {
            if (window.location.href.includes("/post/") || window.location.href.includes("/posts/")) {
                simulateTargetPage();
            } else {
                simulateHomePage();
            }
        } else {
            updateStatus("待命");
        }
    }
    // 若頁面是因跳轉而重載,延遲 3 秒執行 autoContinue
    setTimeout(autoContinue, 3000);

})();