HabrCacheEnhanced

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

スクリプトをインストールするには、Tampermonkey, GreasemonkeyViolentmonkey のような拡張機能のインストールが必要です。

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

スクリプトをインストールするには、TampermonkeyViolentmonkey のような拡張機能のインストールが必要です。

スクリプトをインストールするには、TampermonkeyUserscripts のような拡張機能のインストールが必要です。

このスクリプトをインストールするには、Tampermonkeyなどの拡張機能をインストールする必要があります。

このスクリプトをインストールするには、ユーザースクリプト管理ツールの拡張機能をインストールする必要があります。

(ユーザースクリプト管理ツールは設定済みなのでインストール!)

このスタイルをインストールするには、Stylusなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus などの拡張機能をインストールする必要があります。

このスタイルをインストールするには、Stylus tなどの拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

このスタイルをインストールするには、ユーザースタイル管理用の拡張機能をインストールする必要があります。

(ユーザースタイル管理ツールは設定済みなのでインストール!)

作者のサイトでサポートを受ける。または、このスクリプトの質問や評価の投稿はこちら通報はこちらへお寄せください
// ==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();
})();