Last active
January 27, 2022 13:28
-
-
Save paulera/3c19278b2d2a4f49abfffdca09aa8ad8 to your computer and use it in GitHub Desktop.
This file contains 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 characters
/** | |
* Mural downloader | |
* | |
* Run this script in Google Chrome's console to display buttons for PDF download and a switch for automatic mode. | |
* | |
* This requires the extension Ignore X-Frame headers | |
* https://chrome.google.com/webstore/detail/ignore-x-frame-headers/gleekbfjekiniecknbkamfmkohkpodhe | |
* | |
**/ | |
// Default state is automatic download OFF | |
function autodownload(enable) { | |
window.autoDownloadEnabled = enable; | |
if (window.autoDownloadEnabled) { | |
document.getElementById("autoon").style.display = "block"; | |
document.getElementById("autooff").style.display = "none"; | |
} else { | |
document.getElementById("autoon").style.display = "none"; | |
document.getElementById("autooff").style.display = "block"; | |
} | |
console.log ("Autodownload is " + (window.autoDownloadEnabled?"on":"off")); | |
} | |
function downloadPDF(button, title, url) { | |
console.log (`[${title}] 🤖 Processing started`); | |
// Remember button previous attributes | |
let oldButtonClickEvent = button.onclick; | |
let oldButtonClassName = button.className; | |
let oldButtonInnerHTML = button.innerHTML; | |
// Use button as status message | |
button.onclick = function(){}; | |
button.className = "pdf-export ui-button-search"; | |
button.innerHTML = "Loading mural"; | |
// Open url in an iframe | |
let iframe; | |
iframe = document.createElement('iframe'); | |
iframe.src = "https://app.mural.co" + url; | |
iframe.style.display = "none"; | |
iframe.style.width = "700px"; | |
iframe.style.height = "300px"; | |
let header = document.querySelector(".dashboard-layout-header"); | |
header.appendChild(iframe); | |
console.log (`[${title}] 🎁 Iframe created: ` + iframe.src); | |
// wait for the iframe document to load. When it finished it doesn't | |
// mean it is ready yet | |
iframe.addEventListener('load', () => { | |
console.log (`[${title}] 👍 Mural loaded`); | |
// Wait for the export button to be rendered, so it can be clicked | |
button.innerHTML = "Waiting menus"; | |
console.log (`[${title}] ⏳ Waiting for export button`); | |
let intervalFindExportButton = setInterval(() => { | |
try { | |
// search for the export button | |
let buttonExport = iframe.contentWindow.document.querySelector("[data-qa='export-mural-menu']"); | |
if (buttonExport) { | |
console.log (`[${title}] 👍 Export button found`); | |
clearInterval(intervalFindExportButton); // stop searching | |
buttonExport.click(); | |
console.log (`[${title}] 👊 Export button clicked`); | |
// Wait for the PDF button to be rendered, so it can be clicked | |
console.log (`[${title}] ⏳ Waiting for PDF button`); | |
let intervalFindPDFButton = setInterval(() => { | |
// search for the export button | |
let buttondPDF = iframe.contentWindow.document.querySelector("[data-qa='menu-item-pdf']"); | |
if (buttondPDF) { | |
console.log (`[${title}] 👍 PDF button found`); | |
clearInterval(intervalFindPDFButton); // stop searching | |
buttondPDF.click(); | |
console.log (`[${title}] 👊 PDF button clicked`); | |
button.innerHTML = "PDF requested..."; | |
// Notifies the end of one download, so another one can | |
// get ready to start. | |
window.dispatchEvent( | |
new CustomEvent( | |
"PdfExportDone", | |
{ | |
detail: { | |
"pdfexportindex": button.getAttribute("pdfexportindex"), | |
"title": title | |
} | |
} | |
) | |
); | |
// Destroy the iframe to release memory and bandwidth, but | |
// wait a bit so the download can start. | |
setTimeout(() => { | |
iframe.remove(); | |
console.log (`[${title}] 💣 Iframe destroyed`); | |
console.log (`[${title}] 🤖 Processing finished`); | |
button.onclick = oldButtonClickEvent; | |
button.className = oldButtonClassName; | |
button.innerHTML = oldButtonInnerHTML; | |
}, 10000); // interval to destroy the iframe after start the download | |
} | |
},200); // interval to search for the PDF button | |
} | |
} catch (error) { | |
console.log (`[${title}] 🚫 Search for export button aborted`); | |
clearInterval(intervalFindExportButton); | |
button.onclick = oldButtonClickEvent; | |
button.className = oldButtonClassName; | |
button.innerHTML = oldButtonInnerHTML; | |
if (error instanceof DOMException) { | |
console.log("This script requires the extension \"Ignore X-Frame headers\": https://chrome.google.com/webstore/detail/ignore-x-frame-headers/gleekbfjekiniecknbkamfmkohkpodhe"); | |
alert ("This script requires the extension \"Ignore X-Frame headers\" - see console for link"); | |
throw(error); | |
} else { | |
throw(error); | |
} | |
} | |
}, 200); // interval to search for the export button | |
}, true); //addEventListener | |
} | |
if (!window.pdfButtonsAdded) { | |
window.pdfButtonsAdded = true; | |
document.querySelector("[data-qa='folder-murals-title']").append( | |
new DOMParser().parseFromString( | |
"<button id='autoon' onclick='autodownload(false)' style='width:20rem' class='ui-button medium-size'>Automatic download: ON</button>", | |
'text/html' | |
).documentElement.querySelector('body').firstChild | |
); | |
document.querySelector("[data-qa='folder-murals-title']").append( | |
new DOMParser().parseFromString( | |
"<button id='autooff' onclick='autodownload(true)' style='width:20rem' class='ui-button-search'>Automatic download: OFF</button>", | |
'text/html' | |
).documentElement.querySelector('body').firstChild | |
); | |
document.querySelector("[data-qa='folder-murals-title']").append( | |
new DOMParser().parseFromString( | |
"<span>When automatic download is on, start one download manually and the rest will follow automatically.", | |
'text/html' | |
).documentElement.querySelector('body').firstChild | |
); | |
document.querySelector("[data-qa='folder-murals-title']").append( | |
new DOMParser().parseFromString( | |
"<span id='nap' style='display:none'>😴💤 Simulating a nap...</span>", | |
'text/html' | |
).documentElement.querySelector('body').firstChild | |
); | |
setTimeout(() => { | |
autodownload(false); | |
}, 500); | |
var i = 0; | |
document.querySelector( | |
"ul[data-qa='dashboard-room']" | |
).children.forEach(function(item) { | |
i++; | |
const linkNode = item.querySelector("a[data-qa='dashboard-mural-row-item-link']") | |
const href = linkNode.getAttribute("href"); | |
const title = linkNode.getAttribute("aria-label"); | |
infoNode = item.querySelector(".dashboard-grid-mural-info"); | |
infoNode.style.height="11rem"; | |
button = new DOMParser().parseFromString("<button pdfexportindex=\"" + i + "\" onclick='downloadPDF(this, \"" + title + "\", \"" + href + "\")' type='button' style='margin-top:4px;margin-left:auto; margin-right:auto' class='pdf-export ui-button medium-size'>PDF</button>", 'text/html').documentElement.querySelector('body').firstChild; | |
infoNode.appendChild(button); | |
}); | |
} | |
// Parses events notifying that a download was finished. If the automatic mode | |
// is on, try to find the next mural and continues with the download. | |
window.addEventListener("PdfExportDone", function(event) { | |
window.event = event; | |
console.log ("🗣 Event dispached: " + event.detail.title + " is done!"); | |
if (window.autoDownloadEnabled) { | |
// defines a random wait between export requests to not raise suspicion | |
// on robot detection mechanisms and to not overdo throttling. | |
minWait = 3000; | |
maxWait = 10000; | |
pdfExportIndex = parseInt(event.detail.pdfexportindex); | |
nextButton = document.querySelector("button[pdfexportindex='" + (pdfExportIndex + 1) + "']"); | |
if (nextButton) { | |
document.getElementById("nap").style.display = "block"; | |
setTimeout(function() { | |
nextButton.click(); | |
document.getElementById("nap").style.display = "none"; | |
}, minWait + Math.round(Math.random() * (maxWait - minWait))); | |
} | |
} | |
}, false); |
This file contains 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 characters
/** | |
* Some useful code snippets to use in mural. | |
* If you are here for the PDF export, ignore this part | |
**/ | |
// Map all board in the current room/folder | |
let boards = [...document.querySelectorAll('li[data-qa="dashboard-mural-row-item"]')].map( (item) => { | |
let elementLink = item.querySelector('a[data-qa="dashboard-mural-row-item-link"]'); | |
let elementImage = elementLink.getElementsByClassName('thumb-image')[0]; | |
let urlLocation = new URL(window.location.href); | |
return { | |
"title": elementLink.getAttribute('aria-label'), | |
"id": elementLink.href.split('/')[7], | |
"href": elementLink.href, | |
"workspace": urlLocation.pathname.split('/')[2], | |
"room": urlLocation.pathname.split('/')[4], | |
"folder": urlLocation.searchParams.get('folderUuid'), | |
} | |
}); | |
// Runs a function with all boards mapped | |
boards.forEach( (board) => { | |
console.log (board); | |
}); | |
// List all folders in the current room/folder | |
console.table([...document.querySelectorAll(".dashboard-folders-link")].map( i => i.innerHTML )); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment