FMHY SafeLink Guard 2.0

Warns about unsafe/scammy links based on FMHY filterlist

Устаревшая версия за 03.03.2025. Перейдите к последней версии.

// ==UserScript==
// @name         FMHY SafeLink Guard 2.0
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Warns about unsafe/scammy links based on FMHY filterlist
// @author       maxikozie
// @match        *://*/*
// @grant        GM_xmlhttpRequest
// @connect      raw.githubusercontent.com
// @run-at       document-end
// @license MIT
// ==/UserScript==

(function() {
    'use strict';

    /*
      1) filterlistUrl points to the raw text version
         of the FMHY GitHub filterlist. It relies on
         this updated list each time it run.
    */
    const filterlistUrl = 'https://raw.githubusercontent.com/fmhy/FMHYFilterlist/refs/heads/main/filterlist.txt';

    // It stores all detected unsafe domains here
    let unsafeDomains = [];

    /*
      2) This CSS style determines how the warning
         badge looks in the browser. I kept it simple.
    */
    const warningStyle = `
        background-color: #ff0000;
        color: #fff;
        padding: 2px 6px;
        font-weight: bold;
        border-radius: 4px;
        font-size: 12px;
        margin-left: 6px;
        z-index: 9999;
    `;

    /*
      3) fetchFilterlist() is the entry point.
         It uses GM_xmlhttpRequest (Tamper/Violentmonkey’s
         built-in cross-domain request) to retrieve the filterlist.
    */
    function fetchFilterlist() {
        GM_xmlhttpRequest({
            method: "GET",
            url: filterlistUrl,
            onload: function(response) {
                // Once it gets the raw text, parse it and start the scanning
                parseFilterlist(response.responseText);
                markUnsafeLinks();
                observePageChanges();
            },
            onerror: function() {
                console.error('FMHY SafeLink Guard failed to fetch filterlist');
            }
        });
    }

    /*
      4) parseFilterlist() looks through each line in the text file.
         It looks for lines starting with "||" and ending with "^",
         because these indicate domain-based blocking in uBlock syntax.
    */
    function parseFilterlist(text) {
        const lines = text.split('\n');
        for (const line of lines) {
            const trimmed = line.trim();
            // Basic check: valid domain lines typically start with "||" and end with "^"
            if (trimmed.startsWith('||') && trimmed.endsWith('^')) {
                /*
                  For example, a line like:
                    ||bit.ly^
                  becomes "bit.ly" after it remove the prefix "||" and suffix "^".
                */
                const domain = trimmed
                    .replace('||', '')
                    .replace('^', '');

                // Store the domain in our array
                unsafeDomains.push(domain);
            }
        }
        // The script logs how many domains we captured — purely informational
        console.log(`FMHY SafeLink Guard loaded ${unsafeDomains.length} unsafe domains`);
    }

    /*
      5) markUnsafeLinks() scans every <a> tag on the current page.
         The SafeGuard creates a new warning <span> if the link's hostname ends with
         any domain found in the filterlist.
    */
    function markUnsafeLinks() {
        document.querySelectorAll('a[href]').forEach(link => {
            const url = new URL(link.href);
            if (isUnsafe(url.hostname)) {
                const warning = document.createElement('span');
                warning.textContent = '⚠️ FMHY Unsafe Site';
                warning.style = warningStyle;
                link.after(warning);
            }
        });
    }

    /*
      6) isUnsafe() checks if the link's hostname is in our
         unsafeDomains list. It does a suffix match because
         'example.com' might appear as 'www.example.com'.
    */
    function isUnsafe(hostname) {
        // Remove "www." if present
        hostname = hostname.replace(/^www\./, '');
        // We'll check if the hostname ends with any domain in unsafeDomains
        return unsafeDomains.some(domain => hostname.endsWith(domain));
    }

    /*
      7) observePageChanges() sets up a MutationObserver,
         so if new links appear dynamically (e.g., infinite scrolling
         on Reddit), we run markUnsafeLinks() again.
    */
    function observePageChanges() {
        const observer = new MutationObserver(markUnsafeLinks);
        observer.observe(document.body, { childList: true, subtree: true });
    }

    // Finally, we initiate the script by fetching the list
    fetchFilterlist();
})();