// ==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 // @author Copiis // @version 2024.12.23 // @license MIT // @match https://x.com/home // @grant GM_setValue // @grant GM_getValue // @downloadURL none // ==/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 (isAtTopOfPage() && !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 (window.scrollY === 0 && !isAutomationActive) { activateAutomation(); } else if (window.scrollY > 0 && isAutomationActive) { deactivateAutomation(); } }); } 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. Scrollen..."); scrollToPost(matchedPost, "center"); } else if (!isAtBottomOfPage()) { console.log("Scrollen nach unten, um mehr Beiträge zu laden..."); scrollToBottomWithDelay(() => { console.log("Warten auf neue Beiträge nach Scrollen..."); }); } else { console.log("Gespeicherter Beitrag konnte nicht gefunden werden. Weitere Beiträge werden geladen..."); } }, 1000); } function scrollToBottomWithDelay(callback) { window.scrollTo({ top: document.body.scrollHeight, behavior: "smooth" }); setTimeout(() => { if (isPageFullyLoaded()) { callback(); } else { console.log("Seite ist noch nicht vollständig geladen. Weitere Wartezeit..."); setTimeout(callback, 2000); // Zusätzliche Wartezeit, wenn noch nicht geladen } }, 3000); // Wartezeit nach Scrollen, angepasst für langsame Ladezeiten } function activateAutomation() { isAutomationActive = true; console.log("Automatik aktiviert."); saveTopPostData(); } function deactivateAutomation() { isAutomationActive = false; console.log("Automatik deaktiviert."); } function saveTopPostData() { const topPost = getTopVisiblePost(); if (topPost) { savedTopPostData = { timestamp: getPostTimestamp(topPost), authorHandler: getPostAuthorHandler(topPost), }; saveData(savedTopPostData); } } function loadSavedData() { const savedData = GM_getValue("topPostData", null); if (savedData) { savedTopPostData = JSON.parse(savedData); } } function saveData(data) { GM_setValue("topPostData", JSON.stringify(data)); console.log(`Daten dauerhaft gespeichert: Handler: ${data.authorHandler}, Timestamp: ${data.timestamp}`); } function waitForNewPostsToLoad(callback) { const checkInterval = setInterval(() => { if (isPageFullyLoaded()) { clearInterval(checkInterval); callback(); } }, 500); } 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 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.getHours() === savedDate.getHours() && postDate.getMinutes() === savedDate.getMinutes() ); } 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 getTopVisiblePost() { const posts = Array.from(document.querySelectorAll("article")); return posts.length > 0 ? posts[0] : null; } 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 isPageFullyLoaded() { return document.readyState === "complete"; } function isAtTopOfPage() { return window.scrollY === 0; } function isAtBottomOfPage() { return window.innerHeight + window.scrollY >= document.body.scrollHeight - 1; } })();