Leak Finder Overlay

Adds a convenient overlay to check for "leaks" on various sites.

Dovrai installare un'estensione come Tampermonkey, Greasemonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Violentmonkey per installare questo script.

Dovrai installare un'estensione come Tampermonkey o Userscripts per installare questo script.

Dovrai installare un'estensione come ad esempio Tampermonkey per installare questo script.

Dovrai installare un gestore di script utente per installare questo script.

(Ho già un gestore di script utente, lasciamelo installare!)

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione come ad esempio Stylus per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

Dovrai installare un'estensione per la gestione degli stili utente per installare questo stile.

(Ho già un gestore di stile utente, lasciamelo installare!)

// ==UserScript==
// @name         Leak Finder Overlay
// @namespace    http://tampermonkey.net/
// @version      0.9
// @description  Adds a convenient overlay to check for "leaks" on various sites.
// @author       You (updated by AI)
// @match        https://onlyfans.com/*
// @match        https://fansly.com/*
// @match        https://fantrie.com/*
// @match        *://*.coomer.st/*
// @match        *://*.fapello.com/*
// @match        *://*.topfapgirls1.com/*
// @grant        GM.xmlHttpRequest
// @grant        GM_addStyle
// @license      Unlicense
// ==/UserScript==

(function () {
    'use strict';

    // --- Configuration ---
    const COOMER_DOMAIN = 'coomer.st';
    const MAX_RETRIES = 3;

    // --- UI Creation ---
    function createOverlay(username) {
        // Inject styles
        GM_addStyle(`
            #leak-finder-overlay {
                position: fixed;
                bottom: 20px;
                right: 20px;
                width: 220px;
                background-color: #1a1a1a;
                border: 1px solid #444;
                border-radius: 8px;
                box-shadow: 0 4px 12px rgba(0,0,0,0.5);
                color: #f0f0f0;
                font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
                font-size: 14px;
                z-index: 9999;
                overflow: hidden;
            }
            #leak-finder-overlay-header {
                padding: 10px;
                background-color: #2a2a2a;
                cursor: pointer;
                user-select: none;
                display: flex;
                justify-content: space-between;
                align-items: center;
            }
            #leak-finder-overlay-header h3 {
                margin: 0;
                font-size: 1em;
                font-weight: bold;
                white-space: nowrap;
                overflow: hidden;
                text-overflow: ellipsis;
                padding-right: 5px;
            }
            #leak-finder-overlay-list {
                list-style: none;
                padding: 5px 10px;
                margin: 0;
                max-height: 300px;
                overflow-y: auto;
                transition: all 0.3s ease;
            }
            #leak-finder-overlay.collapsed #leak-finder-overlay-list {
                padding-top: 0;
                padding-bottom: 0;
                max-height: 0;
            }
            #leak-finder-overlay-list li {
                margin: 5px 0;
            }
            #leak-finder-overlay-list a {
                text-decoration: none;
                display: block;
                padding: 4px;
                border-radius: 4px;
                transition: background-color 0.2s, color 0.2s;
            }
            #leak-finder-overlay-list a:hover {
                background-color: #333;
            }
            #leak-finder-overlay-list a.found {
                color: #4CAF50; /* Green for found */
                font-weight: bold;
            }
            #leak-finder-overlay-list a.not-found {
                color: #ff4d4d; /* Red for not found */
            }
            #leak-finder-overlay-list a.pending {
                color: #ffc107; /* Yellow for pending */
            }
        `);

        const overlay = document.createElement('div');
        overlay.id = 'leak-finder-overlay';

        const header = document.createElement('div');
        header.id = 'leak-finder-overlay-header';
        header.innerHTML = `<h3>Checking: ${username}</h3><span id="leak-finder-toggle">▼</span>`;

        const list = document.createElement('ul');
        list.id = 'leak-finder-overlay-list';

        overlay.appendChild(header);
        overlay.appendChild(list);
        document.body.appendChild(overlay);

        header.addEventListener('click', () => {
            overlay.classList.toggle('collapsed');
            document.getElementById('leak-finder-toggle').textContent = overlay.classList.contains('collapsed') ? '▶' : '▼';
        });

        return list;
    }

    function addLink(listElement, siteName, url, note = '') {
        const li = document.createElement('li');
        const a = document.createElement('a');
        const dataSite = siteName.toLowerCase() + (note ? '-' + note.toLowerCase() : '');

        a.href = url;
        a.textContent = siteName + (note ? ` (${note})` : '');
        a.target = '_blank';
        a.rel = 'noopener noreferrer';
        a.classList.add('pending');
        a.dataset.site = dataSite;

        li.appendChild(a);
        listElement.appendChild(li);
    }

    function updateLinkStatus(siteName, note, found) {
        const dataSite = siteName.toLowerCase() + (note ? '-' + note.toLowerCase() : '');
        const link = document.querySelector(`[data-site="${dataSite}"]`);
        if (link) {
            link.classList.remove('pending');
            link.classList.add(found ? 'found' : 'not-found');
        }
    }


    // --- Core Logic ---
    function getUsernameFromUrl() {
        const hostname = window.location.hostname;
        const pathname = window.location.pathname;
        const ignoredPaths = ['my', 'home', 'settings', 'notifications', 'lists', 'chats', 'bookmarks', 'explore'];

        if (hostname.includes('onlyfans.com') || hostname.includes('fantrie.com')) {
            const parts = pathname.split('/');
            if (parts.length > 1 && parts[1] && !ignoredPaths.includes(parts[1])) {
                return parts[1];
            }
        }

        if (hostname.includes('fansly.com')) {
            const matches = pathname.match(/^\/(?:profile\/)?([^/]+)/);
            if (matches && matches[1] && !ignoredPaths.includes(matches[1])) {
                return matches[1];
            }
        }
        return null;
    }

    function createRequest(url, headers, onSuccess, onError, retryCount = 0) {
        GM.xmlHttpRequest({
            method: 'GET',
            url: url,
            headers: headers,
            onload(response) {
                onSuccess(response);
            },
            onerror(response) {
                if (retryCount < MAX_RETRIES) {
                    console.log(`Retrying... Attempt ${retryCount + 1} for ${url}`);
                    setTimeout(() => createRequest(url, headers, onSuccess, onError, retryCount + 1), 1000);
                } else {
                    console.error(`Max retries reached for ${url}`);
                    onError(response);
                }
            },
        });
    }

    // --- Site Checkers ---
    function checkCoomer(service, username, displayName = 'Coomer', note = '') {
        const profileUrl = `https://${COOMER_DOMAIN}/${service}/user/${username}`;
        // *** FIX: Added /profile to the API endpoint ***
        const apiUrl = `https://${COOMER_DOMAIN}/api/v1/${service}/user/${username}/profile`;
        // *** FIX: Added 'Accept' header for coomer requests ***
        const coomerHeaders = { 'Accept': 'text/css' };

        addLink(document.getElementById('leak-finder-overlay-list'), displayName, profileUrl, note);
        createRequest(apiUrl, coomerHeaders,
            (response) => updateLinkStatus(displayName, note, response.status === 200),
            () => updateLinkStatus(displayName, note, false)
        );
    }

    function checkFapello(username, note = '') {
        const profileUrl = `https://fapello.com/${username}/`;
        addLink(document.getElementById('leak-finder-overlay-list'), 'Fapello', profileUrl, note);
        createRequest(profileUrl, {},
            (response) => {
                const found = response.status === 200 && response.finalUrl.startsWith(profileUrl);
                updateLinkStatus('Fapello', note, found);
            },
            () => updateLinkStatus('Fapello', note, false)
        );
    }

    function checkLeakNudes(username, note = '') {
        const profileUrl = `https://leaknudes.com/model/${username}`;
        addLink(document.getElementById('leak-finder-overlay-list'), 'LeakNudes', profileUrl, note);
        createRequest(profileUrl, {},
            (response) => {
                const found = response.status === 200 && response.finalUrl === profileUrl;
                updateLinkStatus('LeakNudes', note, found);
            },
            () => updateLinkStatus('LeakNudes', note, false)
        );
    }

    function checkTopFapGirls(username, note = '') {
        const searchUrl = `https://www.topfapgirls1.com/search/?q=${username}`;
        const dataSite = 'topfapgirls' + (note ? '-' + note.toLowerCase() : '');
        addLink(document.getElementById('leak-finder-overlay-list'), 'TopFapGirls', searchUrl, note);

        createRequest(searchUrl, {},
            (response) => {
                const found = response.status === 200 && !response.finalUrl.includes("?q=");
                updateLinkStatus('TopFapGirls', note, found);
                if (found) {
                    const link = document.querySelector(`[data-site="${dataSite}"]`);
                    if (link) link.href = response.finalUrl;
                }
            },
            () => updateLinkStatus('TopFapGirls', note, false)
        );
    }

    // --- Platform-specific Initializers ---
    function runChecks(username) {
        const oldOverlay = document.getElementById('leak-finder-overlay');
        if (oldOverlay) oldOverlay.remove();

        const listElement = createOverlay(username);
        const hostname = window.location.hostname;

        if (hostname.includes('onlyfans.com')) {
            checkCoomer('onlyfans', username);
            checkFapello(username);
            checkLeakNudes(username);
            checkTopFapGirls(username);
            if (username.includes('_')) {
                checkFapello(username.replace(/_/g, '-'), 'alt');
            }
        } else if (hostname.includes('fansly.com')) {
            const coomerNote = 'Fansly ID';
            const coomerDataSite = 'coomer-' + coomerNote.toLowerCase().replace(' ', '');
            addLink(listElement, 'Coomer', `https://${COOMER_DOMAIN}/`, coomerNote);

            const fanslyApiUrl = `https://apiv3.fansly.com/api/v1/account?usernames=${username}`;
            createRequest(fanslyApiUrl, {},
                (response) => {
                    try {
                        const data = JSON.parse(response.responseText);
                        if (data.success && data.response.length > 0) {
                            const id = data.response[0].id;
                            // *** FIX: Added /profile to the API endpoint ***
                            const coomerApiUrl = `https://${COOMER_DOMAIN}/api/v1/fansly/user/${id}/profile`;
                            const coomerProfileUrl = `https://${COOMER_DOMAIN}/fansly/user/${id}`;
                            const link = document.querySelector(`[data-site="${coomerDataSite}"]`);
                            if (link) link.href = coomerProfileUrl;

                            // *** FIX: Added 'Accept' header for coomer requests ***
                            const coomerHeaders = { 'Accept': 'text/css' };

                            createRequest(coomerApiUrl, coomerHeaders,
                                (coomerRes) => updateLinkStatus('Coomer', coomerNote, coomerRes.status === 200),
                                () => updateLinkStatus('Coomer', coomerNote, false)
                            );
                        } else {
                            updateLinkStatus('Coomer', coomerNote, false);
                        }
                    } catch (e) {
                        updateLinkStatus('Coomer', coomerNote, false);
                    }
                },
                () => updateLinkStatus('Coomer', coomerNote, false)
            );

            checkFapello(username);
            checkLeakNudes(username);
            checkTopFapGirls(username);
            if (username.includes('_')) {
                checkFapello(username.replace(/_/g, '-'), 'alt');
            }

            setTimeout(() => {
                const instaElement = document.querySelector('a[href*="instagram.com"]');
                if (instaElement) {
                    const instaMatch = instaElement.href.match(/instagram\.com\/([a-zA-Z0-9_.]+)/);
                    if (instaMatch && instaMatch[1]) {
                        const instaHandle = instaMatch[1];
                        checkFapello(instaHandle, 'Insta');
                        checkTopFapGirls(instaHandle, 'Insta');
                    }
                }
            }, 2000);

        } else if (hostname.includes('fantrie.com')) {
            checkFapello(username);
            checkLeakNudes(username);
            checkTopFapGirls(username);
        }
    }

    // --- Main Execution ---
    let lastUrl = location.href;
    let lastUsername = null;

    function main() {
        const username = getUsernameFromUrl();
        const overlay = document.getElementById('leak-finder-overlay');

        if (username && username !== lastUsername) {
            lastUsername = username;
            setTimeout(() => runChecks(username), 500);
        } else if (username && overlay && overlay.style.display === 'none') {
             overlay.style.display = 'block';
        } else if (!username && overlay) {
            overlay.style.display = 'none';
            lastUsername = null;
        }
    }

    // Initial run
    setTimeout(main, 1000);

    // Rerun on URL changes for SPAs
    new MutationObserver(() => {
        if (location.href !== lastUrl) {
            lastUrl = location.href;
            main();
        }
    }).observe(document.body, { subtree: true, childList: true });

})();