// ==UserScript== // @name Better Real Estate // @namespace https://github.com/ChenglongMa/tampermonkey-scripts // @version 1.0.1 // @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 // @downloadURL none // ==/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 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); 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 = encodeURIComponent(address.replace(/,/g, '')).replace(/%20/g, '-').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(); 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. })();