codexus / HitBTC extract payments

// ==UserScript==
// @name         HitBTC extract payments
// @namespace    https://openuserjs.org//users/codexus
// @version      0.1
// @description  Extract payments into CSV
// @license      GPL-3.0-or-later
// @author       JO
// @match        https://hitbtc.com/reports/history
// @grant        GM_notification
// @grant        window.focus
// @grant        document
// @require       https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.js
// ==/UserScript==

let csrf = document.getElementsByTagName("body")[0].getAttribute("data-csrftoken");

function sleep (ms) {return new Promise((resolve) => setTimeout(resolve, ms))};

function end_of_month (date, month_offset=0) { return new Date(date.getFullYear(), date.getMonth() + 1 + month_offset, 0); };

function flatten (data) {
    var result = {};
    function recurse (cur, prop) {
        if (Object(cur) !== cur) {
            result[prop] = cur;
        } else if (Array.isArray(cur)) {
            for(var i=0, l=cur.length; i<l; i++)
                recurse(cur[i], prop + "[" + i + "]");
            if (l == 0)
                result[prop] = [];
        } else {
            var isEmpty = true;
            for (var p in cur) {
                isEmpty = false;
                recurse(cur[p], prop ? prop+"."+p : p);
            }
            if (isEmpty && prop)
                result[prop] = {};
        }
    }
    recurse(data, "");
    return result;
}

function to_csv({
    list,
    order = null,
    sep = '\t',
    replace = {}
}) {
    order = order || Object.keys(list[0]);
    let date_replacer = x => (x) ? x.toISOString().replace('T', ' ').replace(/\.[0-9]{3}Z/g, '') : '';
    let csv = [order.join(sep)];
    for (let o of list) {
        let row = [];
        for (let k of order) {
            for (let rem in replace){
                if (typeof o[k] == 'string') o[k] = o[k].replace(rem, replace[rem]);
            };
            if (k.toLowerCase().includes("date")) row.push(date_replacer(o[k]))
            else row.push(o[k])
        };
        csv.push(row.join(sep))
    }
    return csv.join('\n')
}

function write_file({
    name,
    content
}) {
    let a = document.createElement("a");
    a.href = `data:text,${content}`; //content
    a.download = name; //file name
    a.click();
}
function notify({text, title, timeout, onclick, image}){
    GM_notification( {
        text: text,
        title: 'HitBTC extract' || title,
        timeout:3000 || timeout,
        image: 'https://i.stack.imgur.com/geLPT.png' || image,
        onclick:    function () {
            console.log ("Notice clicked.");
            window.focus ();
        } || onclick
    })
}

function extract_page(data){
    if (!data.transactions || !data.transactions.data) return []
    let result = data.transactions.data.map(function(o){
        ["created", "finished"].forEach(k=>{
            o[`date${k.charAt(0).toUpperCase() + k.slice(1)}`]=new Date(o[k]*1000);
            delete o[k];
        });
        for (let k of ['amountStr', 'hashStr', 'detailStr', 'idStr']) o[k] && delete o[k];
        return o;
    });
    return result;
}

async function fetch_page ({start_date, end_date, page=1}){
    let t = new Date();
    notify( {text: `Downloading items #${start_date} (page ${page})`})
    try{
        console.log(start_date, end_date, page);
        let url = `https://hitbtc.com/reports/get-payment-history`;
        if (!end_date) end_date = end_of_month(new Date(start_date)).toLocaleDateString('fr-CA');
        let form = new FormData(),
            params = {
                page: page,
                date_from: start_date,
                date_till: end_date,
                currency_code: "",
                period: "on",
                items: 100,
                __csrf__: csrf
            };
        for (let p in params) form.set(p, params[p]);
        let data = await axios.post(url, form).then(r=>r.data);
        notify( {text: `${new Date()-t}ms for #${start_date} (page ${page})`})
        if (data && data.status=='ok') { return data.data ;}
        else throw new Error(`Error in downloading page ${start_date}}`);
    }
    catch(err) {console.log(err); return {}};
}

async function fetch_all({start_date="2017-10-01", end_date="2019-02-01", wait=500, max=1}={}) {
    if (!end_date) end_date = new Date().toLocaleDateString('fr-CA');
    let p = [],
        p1 = await fetch_page({start_date, end_date, page:1});
    p.push(extract_page(p1));
    if (p1.transactions && !p1.transactions.isLast) {
        for (let i=2 ; i<=max ; i++) {
            await sleep(wait);
            let page = await fetch_page({start_date, end_date, page:i});
            p.push(extract_page(page));
            if (page.transactions.isLast) break
        }
    }
    let data = await Promise.all(p).then(arr=>Array.concat(...arr));
    console.log(data)

    notify({text: `Fetched ${data.length} items`});
    let csv = to_csv({
        list: data,
        sep: ",",
        replace: {'&mdash':'-', '&#43':'+', ";":' ', "&#45":'-', "\n": ' ', ',': ""}
    });
    write_file({
        name: "payments.csv",
        content: csv
    });
};

fetch_all()