HabrCacheEnhanced

Перенаправляет скрытые из-за региональных ограничений, удалённые или скрытые в черновики записи с habr.com на различные зеркала. Redirects posts hidden due to regional restrictions, deleted, or hidden in drafts from habr.com to various mirror sites.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name            HabrCacheEnhanced
// @description     Перенаправляет скрытые из-за региональных ограничений, удалённые или скрытые в черновики записи с habr.com на различные зеркала. Redirects posts hidden due to regional restrictions, deleted, or hidden in drafts from habr.com to various mirror sites.
// @namespace       habrcache
// @version         20260319.3
// @author          dartraiden
// @match           https://habr.com/*
// @supportURL      https://github.com/dartraiden/HabrCacheEnhanced/issues
// ==/UserScript==

(function() {
    'use strict'; // Включаем строгий режим для избегания типичных ошибок

    /**
     * Генерируем HTML-строку со списком всех доступных зеркал.
     * @param {string} currentUrl - Полный URL текущей страницы
     * @param {string} postId - ID статьи, извлечённый из URL
     * @returns {string} - Готовый HTML-блок со ссылками
     */
    const getLinksHtml = (currentUrl, postId) => {
        const services = [
            { name: 'ITnan', url: `https://itnan.ru/post.php?c=1&p=${postId}` },
            { name: 'СоХабр', url: `https://sohabr.net/habr/post/${postId}/` },
            { name: 'SavePearlHarbor', url: `https://savepearlharbor.com/?p=${postId}` },
            { name: 'Archive.today', url: `https://archive.today/${currentUrl}`, target: '_blank' }
        ];

        // Трансформируем массив объектов в массив HTML-строк и объединяем в одну строку
        return services
            .map(s => `<p style="margin: 4px 0;">&bull; <a href="${s.url}" target="_blank" rel="noopener">${s.name}</a></p>`)
            .join('');
    };

    /**
     * Основная логика поиска места на странице и вставки ссылок.
     */
    const inject = () => {
        const currentUrl = window.location.href;

        // Извлекаем ID статьи: ищем цифры между слэшами.
        // Используем опциональный чейнинг ?.[1], чтобы не упасть с ошибкой, если ID не найден.
        const postId = currentUrl.match(/\/(\d+)\/?/)?.[1];
        if (!postId) return;

        // Проверяем, не вставили ли мы ссылки ранее.
        // Это КРИТИЧЕСКИ важно для MutationObserver, иначе будет бесконечный цикл.
        if (document.querySelector('.tm-mirror-links-added')) return;

        // Определяем возможные элементы-маркеры на странице
        const errorBody = document.querySelector(".tm-error-message__body");
        const errorCode = document.querySelector(".tm-error-message__code");
        const regionalTitle = document.querySelector(".tm-regional-restriction__title");

        let targetEl = null; // Куда вставляем

        // Определяем тип ошибки
        if (errorCode?.innerText === "403" && errorBody) {
            targetEl = errorBody;
        } else if (regionalTitle) {
            targetEl = regionalTitle;
        }

        // Если подходящее место найдено — создаём и вставляем элемент
        if (targetEl) {
            const wrapper = document.createElement('div');
            wrapper.className = 'tm-mirror-links-added'; // Помечаем класс-маркер
            wrapper.style.marginTop = '16px';
            wrapper.innerHTML = getLinksHtml(currentUrl, postId);
            targetEl.appendChild(wrapper);
        }
    };

    /**
     * НАБЛЮДАТЕЛЬ (MutationObserver)
     * Нужен, потому что Хабр — это динамическое приложение (SPA).
     * Скрипты сайта могут перерисовать страницу в любой момент, удалив наши изменения.
     */
    const observer = new MutationObserver((mutations) => {
        inject();
    });

    // Начинаем следить за всем деревом элементов внутри body.
    observer.observe(document.body, {
        childList: true,
        subtree: true
    });

    // Запускаем в первый раз вручную, на случай если страница уже загружена.
    inject();
})();