X.com/Twitter Automation

Automatically loads new posts on X.com/Twitter and scrolls back to the reading position.

目前為 2024-11-24 提交的版本,檢視 最新版本

// ==UserScript==
// @name               X.com/Twitter Automation
// @name:de            X.com/Twitter Automatik
// @name:es            Automatización de X.com/Twitter
// @name:fr            Automatisation de X.com/Twitter
// @name:it            Automazione di X.com/Twitter
// @name:pt            Automação do X.com/Twitter
// @name:ru            Автоматизация X.com/Twitter
// @name:zh            X.com/Twitter 自动化
// @name:ja            X.com/Twitter 自動化
// @name:ko            X.com/Twitter 자동화
// @name:hi            X.com/Twitter स्वचालन
// @name:ar            أتمتة X.com/Twitter
// @namespace          http://tampermonkey.net/
// @description        Automatically loads new posts on X.com/Twitter and scrolls back to the reading position.
// @description:de     Lädt automatisch neue Beiträge auf X.com/Twitter und scrollt zur Leseposition zurück.
// @description:es     Carga automáticamente nuevos tweets en X.com/Twitter y vuelve a la posición de lectura.
// @description:fr     Charge automatiquement de nouveaux posts sur X.com/Twitter et revient à la position de lecture.
// @description:it     Carica automaticamente nuovi post su X.com/Twitter e torna alla posizione di lettura.
// @description:pt     Carrega automaticamente novos posts no X.com/Twitter e retorna à posição de leitura.
// @description:ru     Автоматически загружает новые посты на X.com/Twitter и возвращает к позиции чтения.
// @description:zh     自动加载 X.com/Twitter 上的新帖子,并返回到阅读位置。
// @description:ja     X.com/Twitterで新しい投稿を自動的に読み込み、読書位置に戻ります.
// @description:ko     X.com/Twitter에서 새 게시물을 자동으로 로드하고 읽던 위치로 돌아갑니다.
// @description:hi     X.com/Twitter पर नए पोस्ट स्वचालित रूप से लोड करता है और पढ़ने की स्थिति पर वापस ले जाता है.
// @description:ar     يقوم بتحميل المنشورات الجديدة تلقائيًا على X.com/Twitter ويعيدك إلى موضع القراءة.
// @icon               https://cdn-icons-png.flaticon.com/128/14417/14417460.png
// @supportURL         https://www.paypal.com/paypalme/Coopiis?country.x=DE&locale.x=de_DE
// @author             Copiis
// @version            2024.12.24-1
// @license            MIT
// @match              https://x.com/home
// @grant              GM_setValue
// @grant              GM_getValue
// ==/UserScript==

(function () {
    let isAutomationActive = false;
    let isAutoScrolling = false;
    let savedTopPostData = null;

    window.onload = () => {
        console.log("Seite vollständig geladen. Initialisiere Script...");
        initializeScript();
    };

    function initializeScript() {
        loadSavedData();
        if (savedTopPostData) {
            console.log(`Gespeicherte Daten gefunden. Versuche zum gespeicherten Beitrag zu scrollen: Handler: ${savedTopPostData.authorHandler}, Timestamp: ${savedTopPostData.timestamp}`);
            scrollToSavedPost();
        } else {
            console.log("Keine gespeicherten Daten gefunden. Automatik startet erst, wenn der Benutzer manuell scrollt.");
        }

        const observer = new MutationObserver(() => {
            if (isNearTopOfPage() && !isAutomationActive) {
                activateAutomation();
            }

            if (isAutomationActive) {
                const newPostsButton = getNewPostsButton();
                if (newPostsButton) {
                    newPostsButton.click();
                    waitForNewPostsToLoad(() => scrollToSavedPost());
                }
            }
        });

        observer.observe(document.body, { childList: true, subtree: true });

        window.addEventListener('scroll', () => {
            if (isAutoScrolling) return;

            if (isNearTopOfPage() && !isAutomationActive) {
                activateAutomation();
            } else if (window.scrollY > 3 && isAutomationActive) {
                deactivateAutomation();
            }
        });
    }

    function saveTopPostData() {
        const posts = Array.from(document.querySelectorAll("article")); // Alle sichtbaren Beiträge
        if (posts.length === 0) {
            console.log("Keine Beiträge sichtbar. Daten können nicht gespeichert werden.");
            return;
        }

        for (const post of posts) {
            const postTimestamp = getPostTimestamp(post);

            if (!postTimestamp) {
                console.log("Beitrag hat keinen gültigen Timestamp. Überspringe...");
                continue;
            }

            const savedTimestamp = savedTopPostData?.timestamp;

            // Wenn kein gespeicherter Timestamp existiert oder der neue aktueller ist, speichere ihn
            if (!savedTimestamp || new Date(postTimestamp) > new Date(savedTimestamp)) {
                savedTopPostData = {
                    timestamp: postTimestamp,
                    authorHandler: getPostAuthorHandler(post),
                };
                saveData(savedTopPostData); // Speichere die neuen Daten
                console.log(`Neuer Beitrag gespeichert: Handler: ${savedTopPostData.authorHandler}, Timestamp: ${savedTopPostData.timestamp}`);
                return; // Beende die Schleife, wenn gespeichert wurde
            } else {
                console.log(`Beitrag mit Timestamp ${postTimestamp} ist älter als gespeicherter Timestamp ${savedTimestamp}. Prüfe nächsten Beitrag...`);
            }
        }

        console.log("Kein neuer Beitrag gefunden. Keine Daten gespeichert.");
    }

    function waitForNewPostsToLoad(callback) {
        const interval = setInterval(() => {
            const posts = document.querySelectorAll("article");
            if (posts.length > 0) {
                console.log("Neue Beiträge geladen.");
                clearInterval(interval);
                callback();
            } else {
                console.log("Warte auf das Laden neuer Beiträge...");
            }
        }, 500);
    }

    function isNearTopOfPage() {
        return window.scrollY <= 3;
    }

    function isPageFullyLoaded() {
        return document.readyState === "complete";
    }

    function loadSavedData() {
        const savedData = GM_getValue("topPostData", null);
        if (savedData) {
            savedTopPostData = JSON.parse(savedData);
            console.log("Daten geladen:", savedTopPostData);
        }
    }

    function scrollToSavedPost() {
        const interval = setInterval(() => {
            if (!isPageFullyLoaded()) {
                console.log("Warte auf vollständiges Laden der Seite...");
                return;
            }

            const matchedPost = findPostByData(savedTopPostData);

            if (matchedPost) {
                clearInterval(interval);
                console.log("Gespeicherter Beitrag gefunden. Warte auf vollständiges Laden...");
                waitForPostToLoad(matchedPost, () => {
                    console.log("Gespeicherter Beitrag vollständig geladen. Scrollen...");
                    scrollToPost(matchedPost, "center");
                });
            } else if (!isAtBottomOfPage()) {
                console.log("Scrollen nach unten, um mehr Beiträge zu laden...");
                scrollWithLazyLoading();
            } else {
                console.log("Gespeicherter Beitrag konnte nicht gefunden werden. Weitere Beiträge werden geladen...");
            }
        }, 1000);
    }

    function waitForPostToLoad(post, callback) {
        const interval = setInterval(() => {
            if (isPostFullyLoaded(post)) {
                clearInterval(interval);
                callback();
            } else {
                console.log("Warte darauf, dass der Beitrag vollständig geladen wird...");
            }
        }, 500);
    }

    function isPostFullyLoaded(post) {
        const timeElement = post.querySelector("time");
        const isTimeLoaded = timeElement && timeElement.getAttribute("datetime");

        const authorElement = post.querySelector(".css-1jxf684.r-bcqeeo.r-1ttztb7.r-qvutc0.r-poiln3");
        const isAuthorLoaded = authorElement && authorElement.textContent.trim();

        const images = Array.from(post.querySelectorAll("img"));
        const areImagesLoaded = images.every(img => img.complete && img.naturalWidth > 0);

        return isTimeLoaded && isAuthorLoaded && areImagesLoaded;
    }

    function scrollWithLazyLoading() {
        const interval = setInterval(() => {
            const posts = Array.from(document.querySelectorAll("article"));
            const lastPost = posts[posts.length - 1];

            if (!lastPost || !isPostFullyLoaded(lastPost)) {
                console.log("Warte darauf, dass der letzte Beitrag vollständig geladen wird...");
                return;
            }

            console.log("Letzter Beitrag vollständig geladen. Scrolle weiter...");
            clearInterval(interval);
            window.scrollBy({ top: 500, behavior: "smooth" });
        }, 1000);
    }

    function getNewPostsButton() {
        return Array.from(document.querySelectorAll("button, span")).find((button) =>
            /neue Posts anzeigen|Post anzeigen/i.test(button.textContent.trim())
        );
    }

    function scrollToPost(post, position = "center") {
        isAutoScrolling = true;
        post.scrollIntoView({ behavior: "smooth", block: position });
        setTimeout(() => {
            isAutoScrolling = false;
        }, 1000);
    }

    function isAtBottomOfPage() {
        return window.innerHeight + window.scrollY >= document.body.scrollHeight - 1;
    }

    function activateAutomation() {
        isAutomationActive = true;
        console.log("Automatik aktiviert.");
        saveTopPostData();
    }

    function deactivateAutomation() {
        isAutomationActive = false;
        console.log("Automatik deaktiviert.");
    }

    function getPostTimestamp(post) {
        const timeElement = post.querySelector("time");
        return timeElement?.getAttribute("datetime") || null;
    }

    function getPostAuthorHandler(post) {
        const authorElement = post.querySelector(".css-1jxf684.r-bcqeeo.r-1ttztb7.r-qvutc0.r-poiln3");
        return authorElement?.textContent.trim() || null;
    }

    function saveData(data) {
        GM_setValue("topPostData", JSON.stringify(data));
        console.log(`Daten dauerhaft gespeichert: Handler: ${data.authorHandler}, Timestamp: ${data.timestamp}`);
    }

    function findPostByData(data) {
        if (!data || !data.timestamp || !data.authorHandler) return null;

        const posts = Array.from(document.querySelectorAll("article"));
        return posts.find((post) => {
            const postTimestamp = getPostTimestamp(post);
            const postAuthorHandler = getPostAuthorHandler(post);
            return (
                postTimestamp &&
                postAuthorHandler &&
                isTimestampMatching(postTimestamp, data.timestamp) &&
                postAuthorHandler === data.authorHandler
            );
        });
    }

    function isTimestampMatching(postTimestamp, savedTimestamp) {
        if (!postTimestamp || !savedTimestamp) return false;

        const postDate = new Date(postTimestamp);
        const savedDate = new Date(savedTimestamp);

        return (
            postDate.getFullYear() === savedDate.getFullYear() &&
            postDate.getMonth() === savedDate.getMonth() &&
            postDate.getDate() === savedDate.getDate() &&
            postDate.getHours() === savedDate.getHours() &&
            postDate.getMinutes() === savedDate.getMinutes()
        );
    }
})();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址