Files
scripts/banking/anz-download-statements.js

102 lines
4.3 KiB
JavaScript

// =============================================================================
// ANZ Statement Download Helper
// =============================================================================
// PASTE + ENTER, then click Download buttons normally.
// The blob tab still opens (just close it), BUT you also get a properly
// named PDF file downloaded automatically.
//
// TO STOP: just refresh the page (F5)
// =============================================================================
(function () {
'use strict';
let downloadCount = 0;
let lastClickedFilename = null;
// Track which button was clicked
document.addEventListener('click', function (e) {
const btn = e.target.closest('button[data-clicktrack="Download"]');
if (btn) {
const label = btn.getAttribute('aria-label') || '';
const match = label.match(
/Download the statement issued on (\d{2}) (\w{3}) (\d{4}) (\w+) \d{4}/
);
if (match) {
const [, , monthShort, year, monthFull] = match;
const monthNum = new Date(`${monthShort} 1, 2000`).getMonth() + 1;
lastClickedFilename = `ANZ-Statement-${year}-${String(monthNum).padStart(2, '0')}-${monthFull}.pdf`;
} else {
lastClickedFilename = `ANZ-Statement-${Date.now()}.pdf`;
}
console.log(`${lastClickedFilename}...`);
}
}, true);
// Intercept XHR — READ ONLY, don't modify anything
const origOpen = XMLHttpRequest.prototype.open;
const origSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function (method, url, ...rest) {
this._url = url;
return origOpen.call(this, method, url, ...rest);
};
XMLHttpRequest.prototype.send = function (body) {
if (this._url && this._url.includes('download-statement')) {
// Just listen — don't modify anything. ANZ's code runs normally.
this.addEventListener('load', function () {
try {
const text = typeof this.response === 'string' ? this.response : this.responseText;
const json = JSON.parse(text);
const pdf = findPdfData(json);
if (pdf) {
const fn = lastClickedFilename || `ANZ-Statement-${Date.now()}.pdf`;
savePdf(pdf, fn);
downloadCount++;
console.log(`✅ #${downloadCount} ${fn} (${(pdf.length * 3 / 4 / 1024).toFixed(0)} KB)`);
console.log(` Close the blob tab, then click next Download button.`);
}
} catch (e) {
// Silently ignore — ANZ's handler still runs fine
}
});
}
return origSend.call(this, body);
};
function findPdfData(obj, depth) {
if ((depth || 0) > 5) return null;
if (typeof obj === 'string' && obj.length > 1000) {
if (obj.startsWith('JVBERi')) return obj;
if (obj.match(/^[A-Za-z0-9+/=]+$/) && obj.length > 10000) return obj;
}
if (Array.isArray(obj)) {
for (const item of obj) { const r = findPdfData(item, (depth || 0) + 1); if (r) return r; }
}
if (obj && typeof obj === 'object') {
for (const key of Object.keys(obj)) { const r = findPdfData(obj[key], (depth || 0) + 1); if (r) return r; }
}
return null;
}
function savePdf(base64, filename) {
const bin = atob(base64);
const bytes = new Uint8Array(bin.length);
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i);
const blob = new Blob([bytes], { type: 'application/pdf' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url; a.download = filename;
document.body.appendChild(a); a.click(); document.body.removeChild(a);
setTimeout(() => URL.revokeObjectURL(url), 2000);
}
console.log('');
console.log('✅ Download helper active!');
console.log('👆 Click Download buttons normally.');
console.log(' Each click: PDF saves as file + blob tab opens (close it).');
console.log(' To stop: refresh (F5)');
console.log('');
})();