Skip to content

Instantly share code, notes, and snippets.

@malaquiasdev
Created September 12, 2024 17:54
Show Gist options
  • Save malaquiasdev/1f03a0963f2bf2fdda426852f5a3718c to your computer and use it in GitHub Desktop.
Save malaquiasdev/1f03a0963f2bf2fdda426852f5a3718c to your computer and use it in GitHub Desktop.
test
// ==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;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment