ceepies / Airbnb Price Replacer & Receipt Redirect

// ==UserScript==
// @name         Airbnb Price Replacer & Receipt Redirect
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  Replaces prices on Airbnb and redirects receipt-on-demand
// @author       ZXZXZXZX
// @license      MIT
// @match        https://www.airbnb.com/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=airbnb.com
// @grant        none
// @run-at       document-start
// ==/UserScript==
(function() {
    'use strict';

    // ===== CONFIGURATION =====
    const PRICE_REPLACEMENTS = [
        { old: "€849.13", new: "€1543.52" },
        { old: "€391.25", new: "€711.29" },
        { old: "€457.88", new: "€832.23" }
    ];

    const REDIRECT_FROM = '/receipt-on-demand';
    const REDIRECT_TO = '/account-settings/payments/your-payments?product_type=RESERVATION&product_id=1701724700477580669';

    // ===== REDIRECT LOGIC =====
    // Check if we need to redirect
    if (window.location.pathname === REDIRECT_FROM) {
        window.location.replace(REDIRECT_TO);
        return; // Stop execution, we're redirecting
    }

    // ===== PRICE REPLACEMENT LOGIC =====
    let isReplacing = false;
    let hasReplaced = false;

    // Hide the page initially to prevent flash of original prices
    const style = document.createElement('style');
    style.textContent = `
        body {
            opacity: 0 !important;
            visibility: hidden !important;
            transition: opacity 0.01s;
        }
    `;
    document.documentElement.appendChild(style);

    function showPage() {
        if (document.body) {
            document.body.style.opacity = '1';
            document.body.style.visibility = 'visible';
        }
    }

    function replacePrices() {
        if (isReplacing) return;
        if (!document.body) return;

        isReplacing = true;

        try {
            // Get all text nodes
            const walker = document.createTreeWalker(
                document.body,
                NodeFilter.SHOW_TEXT,
                {
                    acceptNode: function(node) {
                        // Skip script, style, and other non-visible tags
                        if (node.parentElement) {
                            const tag = node.parentElement.tagName;
                            if (tag === 'SCRIPT' || tag === 'STYLE' || tag === 'NOSCRIPT' ||
                                tag === 'TEXTAREA' || tag === 'INPUT' || tag === 'META' ||
                                tag === 'LINK' || tag === 'TITLE' || tag === 'HEAD') {
                                return NodeFilter.FILTER_REJECT;
                            }
                        }
                        return NodeFilter.FILTER_ACCEPT;
                    }
                }
            );

            const nodes = [];
            let currentNode;
            while (currentNode = walker.nextNode()) {
                nodes.push(currentNode);
            }

            let modified = false;
            for (const node of nodes) {
                let text = node.nodeValue;
                if (!text || text.trim() === '') continue;

                let changed = false;
                for (const price of PRICE_REPLACEMENTS) {
                    if (text.includes(price.old)) {
                        text = text.replaceAll(price.old, price.new);
                        changed = true;
                    }
                }

                if (changed && text !== node.nodeValue) {
                    node.nodeValue = text;
                    modified = true;
                }
            }

            hasReplaced = true;
            showPage();

            return modified;
        } catch (error) {
            console.error('Error replacing prices:', error);
            showPage();
        } finally {
            isReplacing = false;
        }
    }

    // Wait for body and content to be ready
    function waitForBodyAndReplace() {
        if (!document.body) {
            setTimeout(waitForBodyAndReplace, 50);
            return;
        }

        // If body is empty, wait for content
        if (document.body.textContent.length < 10) {
            setTimeout(waitForBodyAndReplace, 100);
            return;
        }

        replacePrices();
    }

    // Start the process
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() {
            waitForBodyAndReplace();
        });
    } else {
        waitForBodyAndReplace();
    }

    // Safety net: force show page after 3 seconds
    setTimeout(function() {
        if (!hasReplaced) {
            showPage();
        }
    }, 3000);

    // Observer for dynamically loaded content
    const observer = new MutationObserver(function(mutations) {
        if (hasReplaced) {
            let shouldCheck = false;
            for (const mutation of mutations) {
                if (mutation.type === 'childList' && mutation.addedNodes.length > 0) {
                    shouldCheck = true;
                    break;
                }
                if (mutation.type === 'characterData') {
                    shouldCheck = true;
                    break;
                }
            }

            if (shouldCheck) {
                clearTimeout(window.replaceTimeout);
                window.replaceTimeout = setTimeout(replacePrices, 500);
            }
        }
    });

    // Start observer when body is available
    function startObserver() {
        if (document.body) {
            observer.observe(document.body, {
                childList: true,
                subtree: true,
                characterData: true
            });
        } else {
            setTimeout(startObserver, 100);
        }
    }

    startObserver();

    // Re-run when page becomes visible again
    document.addEventListener('visibilitychange', function() {
        if (!document.hidden && hasReplaced) {
            replacePrices();
        }
    });

    console.log('🏠 Airbnb Price Replacer loaded!');
    console.log('📝 Price replacements:', PRICE_REPLACEMENTS);
    console.log('🔄 Redirect: receipt-on-demand → payments page');

})();