Better Real Estate

Enhance real estate websites with additional features

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

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

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

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

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

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name         Better Real Estate
// @namespace    https://github.com/ChenglongMa/tampermonkey-scripts
// @version      1.0.4
// @description  Enhance real estate websites with additional features
// @author       Chenglong Ma
// @match        *://*.realestate.com.au/*
// @match        *://*.domain.com.au/*
// @icon         https://www.google.com/s2/favicons?sz=64&domain=realestate.com.au
// @grant        none
// @license MIT
// ==/UserScript==

(async function () {
    'use strict';

    function getPropertyAddress() {
        const addressElement = document.querySelector('h1.property-info-address, div[data-testid="listing-details__button-copy-wrapper"] > h1');
        if (!addressElement) return undefined;
        return addressElement.textContent;
    }

    async function getPropertyLink(address) {
        // Step 1: Construct the GET request URL
        const query = encodeURIComponent(address);
        const requestUrl = `https://suggest.realestate.com.au/consumer-suggest/suggestions?max=1&type=address%2Csuburb%2Cpostcode%2Cstate%2Cregion&src=reax-multi-intent-search-modal&query=${query}`;

        try {
            // Step 2: Fetch the JSON response
            const response = await fetch(requestUrl);
            const data = await response.json();

            // Step 3: Extract the property URL from the JSON response
            const propertyUrl = data._embedded.suggestions[0].source.url;

            // Step 4: Fetch the HTML content of the property URL
            const propertyResponse = await fetch(propertyUrl);
            const propertyHtml = await propertyResponse.text();

            // Step 5: Parse the HTML and extract the href value of the <a> tag with class containing "PropertyLinkWrapper"
            const parser = new DOMParser();
            const doc = parser.parseFromString(propertyHtml, 'text/html');
            return doc.querySelector('a[class*="PropertyLinkWrapper"]').href;
        } catch (error) {
            console.error('Error fetching property link:', error);
        }
    }

    async function enhancePropertyProfile(propertyLink) {
        const propertyResponse = await fetch(propertyLink, {
            "headers": {
                "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
                "accept-language": "en-AU,en-US;q=0.9,en;q=0.8,zh;q=0.7,zh-TW;q=0.6,zh-CN;q=0.5",
                "priority": "u=0, i",
                "sec-ch-ua": "\"Chromium\";v=\"134\", \"Not:A-Brand\";v=\"24\", \"Microsoft Edge\";v=\"134\"",
                "sec-ch-ua-mobile": "?0",
                "sec-ch-ua-platform": "\"Windows\"",
                "sec-fetch-dest": "document",
                "sec-fetch-mode": "navigate",
                "sec-fetch-site": "same-origin",
                "sec-fetch-user": "?1",
                "upgrade-insecure-requests": "1"
            },
            "referrer": "https://www.property.com.au/dashboard/",
            "referrerPolicy": "strict-origin-when-cross-origin",
            "body": null,
            "method": "GET",
            "mode": "no-cors",
            "credentials": "include"
        });

        const propertyHtml = await propertyResponse.text();
        const parser = new DOMParser();
        const propertyDoc = parser.parseFromString(propertyHtml, 'text/html');

        // 1. Extract overlay profile
        const overlayProfile = {};
        const overlayTiles = propertyDoc.querySelectorAll('div[class*="OverlayTiles__TileHeader"]');
        overlayTiles.forEach((overlayTile) => {
            const [title, value] = overlayTile.innerText.split("\n\n");
            overlayProfile[title] = value;
        })
        console.log("overlayProfile", overlayProfile);

        // 2. Extract property value profile - property.com.au
        const propertyValueProfile = {};
        const estimatedValueElement = propertyDoc.querySelector('p[data-testid="valuation-sub-brick-price-text"]');
        if (estimatedValueElement) {
            propertyValueProfile['Estimated Value'] = estimatedValueElement.textContent;
        }
        const estimatedValueRangeElement = propertyDoc.querySelector('div[data-testid="valuation-sub-brick-estimate-range"]');
        if (estimatedValueRangeElement) {
            const [low, high] = estimatedValueRangeElement.innerText.split("\n\n");
            propertyValueProfile['Low'] = low;
            propertyValueProfile['High'] = high;
        }

        // 3. Extract Floor area size
        const floorAreaElement = propertyDoc.querySelector('div[title="Floor area"]');
        const propertyInfoElement = document.querySelector('ul[class*="property-info__primary-features"]');
        if (propertyInfoElement) {
            const infoContainer = propertyInfoElement.firstElementChild;
            console.log("infoContainer", infoContainer);
            console.log("floorAreaElement", floorAreaElement);
            infoContainer.appendChild(floorAreaElement);
        }


        // 4. Extract property value profile - findbesthouse.com.au
        const findBestHouseValueProfile = {};

    }

    function getFindBestHouseLink(address) {
        // Encode the address to be URL-friendly
        const encodedAddress = address
            .replace(/[^a-zA-Z0-9]+/g, '-') // Replace non-alphanumeric characters with '-'
            .replace(/^-+|-+$/g, '') // Remove leading and trailing '-'
            .toLowerCase();
        // Construct the URL
        return `https://www.findbesthouse.com/en/property/${encodedAddress}`;
    }

    function getGoogleMapLink(address) {
        const encodedAddress = encodeURIComponent(address);
        return `https://www.google.com/maps/place/${encodedAddress}`;
    }

    function addLinkButtons(propertyLink, findBestHouseLink, mapLink) {
        const rightPanel = document.querySelector('div[class="contact-agent-panel"], div[data-testid="listing-details__agent-details"]');
        if (!rightPanel) return;

        let stackDiv = rightPanel.querySelector('div[class^="Stack__StackContainer"], div[class="css-jmaqhc"]');
        if (!stackDiv) {
            stackDiv = rightPanel.lastElementChild;
        }
        if (!stackDiv) return;

        // Create a new button element
        const propertyComAuButton = document.createElement('button');
        const findBestHouseButton = document.createElement('button');
        const mapButton = document.createElement('button');

        const lastButton = stackDiv.querySelector('button[class*="SaveButton__StyledButton"], button[data-testid="listing-details__phone-cta-button"], button');

        if (lastButton) {
            propertyComAuButton.className = lastButton.className;
            findBestHouseButton.className = lastButton.className;
            mapButton.className = lastButton.className;
        }
        propertyComAuButton.title = 'View in property.com.au';
        propertyComAuButton.textContent = 'View in property.com.au';
        const address = getPropertyAddress();

        propertyComAuButton.addEventListener('click', function () {
            if (propertyLink) {
                window.open(propertyLink, '_blank');
            }
        });

        findBestHouseButton.title = 'View in FindBestHouse.com.au';
        findBestHouseButton.textContent = 'View in FindBestHouse.com.au';

        findBestHouseButton.addEventListener('click', function () {
            if (findBestHouseLink) {
                window.open(findBestHouseLink, '_blank');
            }
        });

        mapButton.title = 'View in Google Map';
        mapButton.textContent = 'View in Google Map';
        mapButton.addEventListener('click', function () {
            if (mapLink) {
                window.open(mapLink, '_blank');
            }
        })

        // Append the new button to the stackDiv
        stackDiv.appendChild(propertyComAuButton);
        stackDiv.appendChild(findBestHouseButton);
        stackDiv.appendChild(mapButton);
    }

    // Entry point
    const propertyAddress = getPropertyAddress();
    if (!propertyAddress) return;
    const propertyLink = await getPropertyLink(propertyAddress);
    const findBestHouseLink = getFindBestHouseLink(propertyAddress);
    const mapLink = getGoogleMapLink(propertyAddress);
    addLinkButtons(propertyLink, findBestHouseLink, mapLink);
    // await enhancePropertyProfile(propertyLink); // TODO: property.com.au cannot be fetched.
})();