Added bookmarklets for auto download of files
This commit is contained in:
97
avista_download_bookmarklet.js
Normal file
97
avista_download_bookmarklet.js
Normal file
@@ -0,0 +1,97 @@
|
||||
javascript:(async function() {
|
||||
function getProperty() {
|
||||
try {
|
||||
const el = document.querySelector('.acct-level-menu-address-single .exclude-translation');
|
||||
const text = el ? el.innerText : document.body.innerText;
|
||||
if (text.includes("23511")) return "23511 E 2nd";
|
||||
if (text.includes("23513")) return "23513 E 2nd";
|
||||
if (text.includes("Myrtlewood")) return "Myrtlewood";
|
||||
if (text.includes("E Upriver")) return "Brookstone";
|
||||
if (text.includes("W 2nd")) return "Ascott";
|
||||
if (text.includes("E 36th") || text.includes("E 37th")) return "Commons";
|
||||
return "Unknown";
|
||||
} catch(e) { return "Unknown"; }
|
||||
}
|
||||
const propertyName = getProperty();
|
||||
const limitInput = prompt("Detected Property: " + propertyName + "\n\nLimit downloads to how many?\n(Leave blank for ALL, click Cancel to stop script)");
|
||||
if (limitInput === null) return;
|
||||
let maxDownloads = Infinity;
|
||||
if (limitInput.trim() !== "") {
|
||||
const parsed = parseInt(limitInput, 10);
|
||||
if (!isNaN(parsed)) maxDownloads = parsed;
|
||||
}
|
||||
const processedUrls = new Set();
|
||||
let totalDownloaded = 0;
|
||||
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
alert("Starting process.\nTarget: " + (maxDownloads === Infinity ? "ALL" : maxDownloads) + " files.\n\nPlease keep this tab open.");
|
||||
async function scanAndDownload() {
|
||||
const rows = document.querySelectorAll('tr[ng-repeat="entry in pageData.Entries"]');
|
||||
const currentTotalRows = rows.length;
|
||||
for (const row of rows) {
|
||||
if (totalDownloaded >= maxDownloads) return { count: currentTotalRows, lastText: "", stop: true };
|
||||
const dateCell = row.querySelector('td[data-heading-label="Date"]');
|
||||
const pdfCell = row.querySelector('td[data-heading-label="View Bill PDF"]');
|
||||
if (!dateCell || !pdfCell || pdfCell.classList.contains('ng-hide')) continue;
|
||||
const linkElement = pdfCell.querySelector('a');
|
||||
if (!linkElement || !linkElement.href) continue;
|
||||
if (processedUrls.has(linkElement.href)) continue;
|
||||
processedUrls.add(linkElement.href);
|
||||
const rawDate = dateCell.innerText.trim();
|
||||
const parts = rawDate.split('/');
|
||||
const cleanDate = parts[2] + "-" + parts[0].padStart(2, '0') + "-" + parts[1].padStart(2, '0');
|
||||
const safeProp = propertyName.replace(/[^a-z0-9\s-_]/gi, '').trim();
|
||||
const filename = "Avista - " + safeProp + " - " + cleanDate + ".pdf";
|
||||
try {
|
||||
const response = await fetch(linkElement.href);
|
||||
const blob = await response.blob();
|
||||
const blobUrl = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = blobUrl;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(blobUrl);
|
||||
document.body.removeChild(a);
|
||||
totalDownloaded++;
|
||||
await wait(800);
|
||||
} catch (err) { console.error("Download failed", err); }
|
||||
}
|
||||
const lastRowText = rows.length > 0 ? rows[rows.length - 1].innerText : "";
|
||||
if (totalDownloaded >= maxDownloads) return { count: currentTotalRows, lastText: lastRowText, stop: true };
|
||||
return { count: currentTotalRows, lastText: lastRowText, stop: false };
|
||||
}
|
||||
async function waitForNewRows(previousState) {
|
||||
let attempts = 0;
|
||||
while (attempts < 30) {
|
||||
const rows = document.querySelectorAll('tr[ng-repeat="entry in pageData.Entries"]');
|
||||
const currentCount = rows.length;
|
||||
const currentLastText = rows.length > 0 ? rows[rows.length - 1].innerText : "";
|
||||
if (currentCount > previousState.count || currentLastText !== previousState.lastText) {
|
||||
return true;
|
||||
}
|
||||
await wait(500);
|
||||
attempts++;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
let keepGoing = true;
|
||||
let currentState = await scanAndDownload();
|
||||
if (currentState.stop) keepGoing = false;
|
||||
while (keepGoing) {
|
||||
const allSpans = Array.from(document.querySelectorAll('span.btn-secondary'));
|
||||
const moreButton = allSpans.find(s => s.innerText && s.innerText.toLowerCase().includes('load more') && s.offsetParent !== null);
|
||||
const navWrapper = document.querySelector('.nav-pagination-mobile');
|
||||
const isHidden = navWrapper && navWrapper.classList.contains('ng-hide');
|
||||
if (moreButton && !isHidden) {
|
||||
moreButton.click();
|
||||
await waitForNewRows(currentState);
|
||||
currentState = await scanAndDownload();
|
||||
if (currentState.stop) keepGoing = false;
|
||||
} else {
|
||||
keepGoing = false;
|
||||
}
|
||||
}
|
||||
alert("Job Done!\n\nTotal bills downloaded: " + totalDownloaded);
|
||||
})();
|
||||
|
||||
63
mrcooper_download_bookmarklet.js
Normal file
63
mrcooper_download_bookmarklet.js
Normal file
@@ -0,0 +1,63 @@
|
||||
javascript:(async function() {
|
||||
console.log(">>> MR COOPER DOWNLOADER STARTED");
|
||||
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
let capturedUrl = null;
|
||||
const originalWindowOpen = window.open;
|
||||
function enableInterceptor() {
|
||||
capturedUrl = null;
|
||||
window.open = function(url) {
|
||||
console.log(">>> Intercepted URL: " + url);
|
||||
capturedUrl = url;
|
||||
return { focus:()=>{}, close:()=>{} };
|
||||
};
|
||||
}
|
||||
function disableInterceptor() { window.open = originalWindowOpen; }
|
||||
const dateDivs = Array.from(document.querySelectorAll(".statement-date-no-type"));
|
||||
if (dateDivs.length === 0) {
|
||||
alert("No statements found! Make sure you are on the Statements page.");
|
||||
return;
|
||||
}
|
||||
let downloadCount = 0;
|
||||
for (const dateDiv of dateDivs) {
|
||||
if (dateDiv.innerText.trim() === "DATE") continue;
|
||||
const row = dateDiv.closest(".row.collapse");
|
||||
const btn = row.querySelector(".view-statement-button");
|
||||
if (!btn) continue;
|
||||
const rawDate = dateDiv.innerText.trim();
|
||||
const d = new Date(rawDate);
|
||||
if (isNaN(d.getTime())) continue;
|
||||
const yyyy = d.getFullYear();
|
||||
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(d.getDate()).padStart(2, '0');
|
||||
const cleanDate = yyyy + "-" + mm + "-" + dd;
|
||||
const filename = "Mr Cooper - E 2nd - " + cleanDate + ".pdf";
|
||||
enableInterceptor();
|
||||
btn.click();
|
||||
let attempts = 0;
|
||||
while (capturedUrl === null && attempts < 20) {
|
||||
await wait(200);
|
||||
attempts++;
|
||||
}
|
||||
disableInterceptor();
|
||||
if (capturedUrl) {
|
||||
try {
|
||||
const response = await fetch(capturedUrl);
|
||||
const blob = await response.blob();
|
||||
const blobUrl = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = blobUrl;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(blobUrl);
|
||||
document.body.removeChild(a);
|
||||
downloadCount++;
|
||||
} catch (e) {
|
||||
console.error("Fetch failed", e);
|
||||
}
|
||||
}
|
||||
await wait(1500);
|
||||
}
|
||||
alert("Done! Downloaded " + downloadCount + " files.");
|
||||
})();
|
||||
104
roundpoint_download_bookmarklet.js
Normal file
104
roundpoint_download_bookmarklet.js
Normal file
@@ -0,0 +1,104 @@
|
||||
javascript:(async function() {
|
||||
console.log(">>> LINK HARVESTER STARTED");
|
||||
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
const capturedLinks = [];
|
||||
const dateCounts = {};
|
||||
|
||||
let capturedUrl = null;
|
||||
const originalWindowOpen = window.open;
|
||||
|
||||
function enableInterceptor() {
|
||||
capturedUrl = null;
|
||||
window.open = function(url) {
|
||||
if (url) {
|
||||
capturedUrl = url;
|
||||
return { focus:()=>{}, close:()=>{} };
|
||||
}
|
||||
const spyWindow = {
|
||||
focus: () => {}, close: () => {},
|
||||
document: { write: () => {}, close: () => {} }
|
||||
};
|
||||
Object.defineProperty(spyWindow, 'location', {
|
||||
set: function(val) { capturedUrl = val; },
|
||||
get: function() { return { set href(val) { capturedUrl = val; }, assign: (val) => { capturedUrl = val; }, replace: (val) => { capturedUrl = val; } }; }
|
||||
});
|
||||
return spyWindow;
|
||||
};
|
||||
}
|
||||
function disableInterceptor() { window.open = originalWindowOpen; }
|
||||
|
||||
const rows = Array.from(document.querySelectorAll("mat-row"));
|
||||
if (rows.length === 0) {
|
||||
alert("No rows found. Please go to the Billing/Documents list.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (const row of rows) {
|
||||
const dateCell = row.querySelector(".mat-column-date");
|
||||
if (!dateCell) continue;
|
||||
|
||||
const rawDate = dateCell.innerText.trim();
|
||||
const d = new Date(rawDate);
|
||||
if (isNaN(d.getTime())) continue;
|
||||
|
||||
const yyyy = d.getFullYear();
|
||||
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
||||
const dd = String(d.getDate()).padStart(2, '0');
|
||||
const baseDate = yyyy + "-" + mm + "-" + dd;
|
||||
|
||||
if (!dateCounts[baseDate]) dateCounts[baseDate] = 0;
|
||||
dateCounts[baseDate]++;
|
||||
const suffix = dateCounts[baseDate] > 1 ? " (" + dateCounts[baseDate] + ")" : "";
|
||||
|
||||
const filename = "Roundpoint - Myrtlewood - " + baseDate + suffix + ".pdf";
|
||||
|
||||
const downloadCell = row.querySelector(".mat-column-download");
|
||||
const clickTarget = downloadCell ? (downloadCell.querySelector("i") || downloadCell) : null;
|
||||
|
||||
if (!clickTarget) continue;
|
||||
|
||||
enableInterceptor();
|
||||
clickTarget.click();
|
||||
|
||||
let attempts = 0;
|
||||
while (capturedUrl === null && attempts < 30) {
|
||||
await wait(100);
|
||||
attempts++;
|
||||
}
|
||||
disableInterceptor();
|
||||
|
||||
if (capturedUrl) {
|
||||
const urlWithHash = capturedUrl + "#" + filename;
|
||||
capturedLinks.push({ filename: filename, url: urlWithHash });
|
||||
}
|
||||
await wait(200);
|
||||
}
|
||||
|
||||
const container = document.querySelector('mat-drawer-content > div') || document.querySelector('.data-table-shell') || document.querySelector('mat-drawer-content');
|
||||
|
||||
if (!container) {
|
||||
alert("Could not find the table container to replace. Check console for links.");
|
||||
console.log(capturedLinks);
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = `
|
||||
<div style="padding: 20px; font-family: sans-serif; background: #fff; color: #000; height: 100%; overflow: auto;">
|
||||
<h2 style="color: #333; margin-top: 0;">Harvest Complete (${capturedLinks.length} files)</h2>
|
||||
<p style="background: #e3f2fd; padding: 10px; border-radius: 4px; border-left: 5px solid #2196F3; font-size: 14px;">
|
||||
<strong>INSTRUCTIONS:</strong><br>
|
||||
Right-click -> <strong>DownThemAll!</strong><br>
|
||||
(Use default settings or <code>*text*</code> mask)
|
||||
</p>
|
||||
<table style="width: 100%; border-collapse: collapse; margin-top: 10px;">
|
||||
${capturedLinks.map(item => `
|
||||
<tr style="border-bottom: 1px solid #ddd;">
|
||||
<td style="padding: 8px;">
|
||||
<a href="${item.url}" download="${item.filename}" style="font-size: 14px; color: #0066cc; text-decoration: none; font-family: monospace;">${item.filename}</a>
|
||||
</td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</table>
|
||||
</div>
|
||||
`;
|
||||
})();
|
||||
134
vera_download_bookmarklet.js
Normal file
134
vera_download_bookmarklet.js
Normal file
@@ -0,0 +1,134 @@
|
||||
javascript:(async function() {
|
||||
console.log(">>> SCRIPT STARTED");
|
||||
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
||||
function parseDate(input) {
|
||||
const d = new Date(input);
|
||||
if (isNaN(d.getTime())) return null;
|
||||
d.setHours(0, 0, 0, 0);
|
||||
return d;
|
||||
}
|
||||
function getUnitName(addressText) {
|
||||
const text = addressText.trim();
|
||||
if (text.toLowerCase().includes("laundry")) {
|
||||
if (text.includes("504")) return "CY1 Laundry";
|
||||
if (text.includes("13129")) return "CY2 Laundry";
|
||||
if (text.includes("13203")) return "CY3 Laundry";
|
||||
if (text.includes("524")) return "CY4 Laundry";
|
||||
return "Laundry";
|
||||
}
|
||||
const parts = text.split(" ");
|
||||
return parts[parts.length - 1];
|
||||
}
|
||||
let capturedUrl = null;
|
||||
const originalWindowOpen = window.open;
|
||||
function enableInterceptor() {
|
||||
capturedUrl = null;
|
||||
window.open = function(url) { capturedUrl = url; return { focus:()=>{}, close:()=>{} }; };
|
||||
}
|
||||
function disableInterceptor() { window.open = originalWindowOpen; }
|
||||
async function setTableTo100() {
|
||||
const wrapper = document.querySelector("#statement-history-table-title")?.parentElement;
|
||||
if (!wrapper) return;
|
||||
const select = wrapper.querySelector("select[name*='length']");
|
||||
if (select && select.value !== "100") {
|
||||
select.value = "100";
|
||||
select.dispatchEvent(new Event('change', { bubbles: true }));
|
||||
await wait(2000);
|
||||
}
|
||||
}
|
||||
async function ensureMenuOpen() {
|
||||
const table = document.querySelector("#my-accounts-table");
|
||||
if (table && table.offsetParent !== null) return;
|
||||
const buttons = Array.from(document.querySelectorAll("button, a, span[role='button']"));
|
||||
const trigger = buttons.find(b => {
|
||||
const t = b.innerText.toLowerCase();
|
||||
return t.includes("switch account") || t.includes("change account") || t.includes("choose account");
|
||||
});
|
||||
if (trigger) { trigger.click(); await wait(1000); }
|
||||
else {
|
||||
const header = document.querySelector("address");
|
||||
if (header) { header.click(); await wait(1000); }
|
||||
}
|
||||
}
|
||||
async function processCurrentAccount(cutoffDate) {
|
||||
await setTableTo100();
|
||||
const addrSpan = document.querySelector("address span[data-bind*='fullAddress']");
|
||||
const fullAddress = addrSpan ? addrSpan.innerText : "Unknown";
|
||||
const extraName = getUnitName(fullAddress);
|
||||
const rows = document.querySelectorAll("#statements-table tbody tr");
|
||||
let downloadCount = 0;
|
||||
for (const row of rows) {
|
||||
const dateCell = row.cells[2];
|
||||
const btn = row.querySelector(".view-statement-details-button");
|
||||
if (!dateCell || !btn) continue;
|
||||
const rawDate = dateCell.innerText.trim();
|
||||
const stmtDate = new Date(rawDate);
|
||||
stmtDate.setHours(0,0,0,0);
|
||||
if (stmtDate < cutoffDate) continue;
|
||||
const yyyy = stmtDate.getFullYear();
|
||||
const mm = String(stmtDate.getMonth()+1).padStart(2,'0');
|
||||
const dd = String(stmtDate.getDate()).padStart(2,'0');
|
||||
const cleanDate = yyyy + "-" + mm + "-" + dd;
|
||||
const filename = "Vera - Brookstone " + extraName + " - " + cleanDate + ".pdf";
|
||||
enableInterceptor();
|
||||
btn.click();
|
||||
let attempts = 0;
|
||||
while (capturedUrl === null && attempts < 15) { await wait(200); attempts++; }
|
||||
disableInterceptor();
|
||||
if (capturedUrl) {
|
||||
try {
|
||||
const response = await fetch(capturedUrl);
|
||||
const blob = await response.blob();
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.style.display = 'none';
|
||||
a.href = url;
|
||||
a.download = filename;
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(url);
|
||||
document.body.removeChild(a);
|
||||
downloadCount++;
|
||||
} catch (e) { console.error("Fetch failed", e); }
|
||||
}
|
||||
await wait(1000);
|
||||
}
|
||||
}
|
||||
const inputStr = prompt("Enter Cutoff Date (MM/DD/YYYY).\nLeave BLANK for ALL history.\nClick Cancel to stop.", "9/18/2025");
|
||||
if (inputStr === null) return;
|
||||
let cutoffDate;
|
||||
if (inputStr.trim() === "") {
|
||||
cutoffDate = new Date("1900-01-01");
|
||||
} else {
|
||||
cutoffDate = parseDate(inputStr);
|
||||
if (!cutoffDate) {
|
||||
alert("Invalid Date format. Please use MM/DD/YYYY.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
let accountRows = document.querySelectorAll("#my-accounts-table tbody tr");
|
||||
if (accountRows.length === 0) {
|
||||
alert("Please open the 'Choose Account' popup first!");
|
||||
return;
|
||||
}
|
||||
const totalAccounts = accountRows.length;
|
||||
for (let i = 0; i < totalAccounts; i++) {
|
||||
await ensureMenuOpen();
|
||||
accountRows = document.querySelectorAll("#my-accounts-table tbody tr");
|
||||
if (!accountRows[i]) break;
|
||||
const row = accountRows[i];
|
||||
const targetCell = row.cells[2] || row.cells[1];
|
||||
const targetAcctNum = row.cells[1].innerText.trim();
|
||||
targetCell.click();
|
||||
let loaded = false;
|
||||
for (let j=0; j<20; j++) {
|
||||
await wait(500);
|
||||
const headerAcct = document.querySelector("address span[data-bind*='accountNumber']");
|
||||
if (headerAcct && headerAcct.innerText.trim() === targetAcctNum) { loaded = true; break; }
|
||||
}
|
||||
if (!loaded) continue;
|
||||
await wait(2000);
|
||||
await processCurrentAccount(cutoffDate);
|
||||
}
|
||||
alert("Batch completed. All accounts processed.");
|
||||
})();
|
||||
Reference in New Issue
Block a user