Created
          September 12, 2024 17:54 
        
      - 
      
 - 
        
Save malaquiasdev/1f03a0963f2bf2fdda426852f5a3718c to your computer and use it in GitHub Desktop.  
Revisions
- 
        
malaquiasdev created this gist
Sep 12, 2024 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,345 @@ // ==UserScript== // @name Ebenezer // @namespace http://tampermonkey.net/ // @version 0.1 // @description Embed Ebenezer in HubSpot // @author StudentRoomStay // @match https://app.hubspot.com/contacts/2113514/contact/* // @match https://app.hubspot.com/contacts/2113514/deal/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant none // ==/UserScript== const version = "1.0.5"; console.log(`### [ebenezer-run] Version is: ${version}`); const recordContactIndicator = "0-1"; const recordDealIndicator = "0-3"; const recordTicketIndicator = "0-5"; const environments = { local: "http://localhost:3000", beta: "https://test-ledger.studentroomstay.com/web", prod: "https://ledger.studentroomstay.com/web", }; let state, currentHref, ebenezer; window.addEventListener("message", ebenezerReady); waitForPageLoad(); // Code to observe changes to parts of the DOM const observer = new MutationObserver(pageChanged); observer.observe(document, { childList: true, subtree: true }); function waitForPageLoad() { // prevent duplicate loads ebenezer = document.getElementById("ebenezer"); if (!ebenezer) { state = "waiting"; console.log("EBENEZER", state); ebenezer = null; currentHref = document.location.href; } else { state = "initialized"; currentHref = document.location.href; ebenezer.style.width = "60px"; ebenezer.style.minWidth = "60px"; } } function pageChanged(changes, observer) { if (state === "waiting") { setupEbenezer(); } else if (state === "initialized") { loadData(); } else if (currentHref !== document.location.href) { waitForPageLoad(); } } function setupEbenezer() { const insertionPoint = getInsertionPoint(); // prevent duplicate loads ebenezer = document.getElementById("ebenezer"); if (!ebenezer && insertionPoint) { ebenezer = insertEbenezer(insertionPoint); fixFormatting(); } } function loadData() { try { const data = scrapeData(); console.log("LOADED DATA", data); if (data) { sendData(ebenezer, data); state = "loading"; console.log("EBENEZER", state); } } catch (err) { console.log("LOADING ERROR", err); // ignore; the data isn't ready yet } } function ebenezerReady(event) { if (event.data === "ebenezer:init") { state = "initialized"; console.log("EBENEZER", state); loadData(); } else if (event.data === "ebenezer:login") { console.log("EBENEZER LOGIN REQUIRED"); ebenezer.style.width = "auto"; ebenezer.style.minWidth = "240px"; } else if (event.data === "ebenezer:ready") { state = "loaded"; console.log("EBENEZER", state); ebenezer.style.width = "auto"; ebenezer.style.minWidth = "400px"; } else if (event.data === "ebenezer:close") { ebenezer.style.width = "0px"; ebenezer.style.minWidth = "0px"; } } function getInsertionPoint() { return document.querySelector("div[data-unit-test='filterAndMiddle']"); } function fixFormatting() { const timeline = document.querySelector( "div[data-selenium-test='timeline-wrapper']", ); timeline.style.minWidth = "500px"; } function insertEbenezer(insertionPoint) { const ebenezer = document.createElement("iframe"); const env = localStorage.getItem("ebenezer_env"); const page_type = window.location.href.split("/")[5]; ebenezer.src = environments[env] + "/#/hubspot/" + page_type; ebenezer.id = "ebenezer"; ebenezer.style.width = "60px"; ebenezer.style.minWidth = "60px"; ebenezer.style.border = "0"; ebenezer.style.borderLeft = "medium solid green"; insertionPoint.appendChild(ebenezer); return ebenezer; } function sendData(ebenezer, data) { console.log("Ebenezer Data", data); ebenezer.contentWindow.postMessage(data, "*"); } function scrapeData() { const href = window.location.href; const page_type = href.split("/")[5]; if (page_type === "contact") { return scrapeContactPage(); } else if (page_type === "ticket") { return scrapeTicketPage(); } else if (page_type === "record") { const recordIndicator = href.split("/")[6]; if (recordIndicator === recordContactIndicator) { return scrapeContactPage(true); } else if (recordIndicator === recordTicketIndicator) { return scrapeTicketPage(true); } else { return { page_type: "unknown" }; } } return { page_type: "unknown" }; } let moreClicked = false; function scrapeDealPage(isRecordPage = false) { const dealDetails = document.querySelector( "div[data-selenium-test='deal-highlight-details']", ); const associatedNodes = [ ...document.querySelectorAll( "div[data-sidebar-card-type='AssociatedObjectsCard']", ), ]; const contactsList = associatedNodes.find((h5) => h5.innerText.includes("Contacts"), ); const moreContacts = contactsList.querySelector( "button[data-selenium-test='associated-objects-card-view-more']", ); if (moreContacts) { if (!moreClicked) { // only click this once; hubspot gets funny if you click it more than once moreContacts.click(); moreClicked = true; } // wait until the button is gone return null; } if (!dealDetails || !contactsList) { return null; } return { page_type: "deal", deal: scrapeDeal(dealDetails, isRecordPage), contacts: scrapeAssociatedContacts(contactsList, isRecordPage), }; } function scrapeTicketPage(isRecordPage = false) { const ticketDetails = document.querySelector( "div[data-selenium-test='ticket-highlight-details']", ); const contactsList = document.querySelector('[data-sidebar-key="Contacts"]'); const dealsList = document.querySelector('[data-sidebar-key="Deals"]'); const moreContacts = contactsList.querySelector( "button[data-selenium-test='associated-objects-card-view-more']", ); if (moreContacts) { if (!moreClicked) { // only click this once; hubspot gets funny if you click it more than once moreContacts.click(); moreClicked = true; } // wait until the button is gone return null; } if (!ticketDetails || !contactsList || !dealsList) { return null; } return { page_type: "ticket", ticket: scrapeTicket(ticketDetails, isRecordPage), contacts: scrapeAssociatedContacts(contactsList, isRecordPage), deals: scrapeAssociatedDeals(dealsList, isRecordPage), }; } function scrapeContactPage(isRecordPage = false) { const contactDetailsHTML = document.querySelector( "div[data-selenium-test='contact-highlight-details']", ); const associatedNodes = [ ...document.querySelectorAll( "div[data-sidebar-card-type='AssociatedObjectsCard']", ), ]; const dealsListHTML = associatedNodes.find( (div) => typeof div.getAttribute("data-sidebar-key") === "string" && div.getAttribute("data-sidebar-key").includes("Deals"), ); if (!contactDetailsHTML || !dealsListHTML) { return null; } return { page_type: "contact", contact: scrapeContact(contactDetailsHTML, isRecordPage), deals: scrapeAssociatedDeals(dealsListHTML, isRecordPage), }; } function scrapeDeal(dealHTML, isRecordPage) { const hubspot_url = window.location.href; return { hubspot_url: hubspot_url, hubspot_id: hubspot_url.split("/")[isRecordPage ? 7 : 6], name: dealHTML.querySelector("span[data-selenium-test='highlightTitle']") .innerText, }; } function scrapeTicket(ticketHTML, isRecordPage) { const hubspot_url = window.location.href; const pipeline = ticketHTML.querySelector( "button[data-selenium-test='pipeline-select'] span", )?.innerText; if (!pipeline || /^[0-9]*$/.test(pipeline)) { throw new Error("pipeline not yet loaded"); } return { hubspot_url: hubspot_url, hubspot_id: hubspot_url.split("/")[isRecordPage ? 7 : 6], name: ticketHTML.querySelector("span[data-selenium-test='highlightTitle']") .innerText, pipeline, }; } function scrapeContact(contactHTML, isRecordPage) { const hubspot_url = window.location.href; return { hubspot_url: hubspot_url, hubspot_id: hubspot_url.split("/")[isRecordPage ? 7 : 6], name: contactHTML.querySelector("span[data-selenium-test='highlightTitle']") .firstChild.innerText, }; } function scrapeAssociatedDeals(dealList, isRecordPage) { const dealNodes = [ ...dealList.querySelectorAll("div[data-selenium-test='chicklet']"), ]; deals = []; dealNodes.forEach((d) => { const a = d.querySelector( "div[data-selenium-test='deal-chicklet-title'] > a", )?.href; const dealName = d?.querySelector( "span[data-selenium-test='property-input-dealname']", ); deals.push({ name: dealName?.innerText, hubspot_url: a, hubspot_id: a?.split("/")[isRecordPage ? 7 : 6], }); }); console.log("### [ebenezer-run] Deals: ", deals); return deals; } function scrapeAssociatedContacts(contactsList, isRecordPage) { const contactNodes = [ ...contactsList.querySelectorAll("div[data-selenium-test='chicklet']"), ]; contacts = []; contactNodes.forEach((c) => { const hubspot_url = c.querySelector( "span[data-selenium-test='contact-chicklet-title-link']", ).parentNode.href; contacts.push({ name: c.querySelector( "span[data-selenium-test='contact-chicklet-title-link']", ).innerText, customer_type: c.querySelector( "span[data-selenium-test='formatted-property-customer_type']", ).innerText, hubspot_url: hubspot_url, hubspot_id: hubspot_url.split("/")[isRecordPage ? 7 : 6], }); }); return contacts; }