From 1e6e114ab854efab9b6172e6d92a80ddde619331 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Fri, 17 Feb 2023 10:24:44 -0600 Subject: [PATCH 1/5] feat(colo17): claimable commissions (WIP), LoadColoInvoices - search GMail for invoices since the validator was created - TODO: automate claimable commissions --- packages/colo17/UI.js | 23 ++++++++++++ packages/colo17/appsscript.json | 14 ++++++++ packages/colo17/coloInvoices.js | 63 +++++++++++++++++++++++++++++++++ packages/colo17/sheetTools.js | 4 +++ 4 files changed, 104 insertions(+) create mode 100644 packages/colo17/UI.js create mode 100644 packages/colo17/appsscript.json create mode 100644 packages/colo17/coloInvoices.js create mode 100644 packages/colo17/sheetTools.js diff --git a/packages/colo17/UI.js b/packages/colo17/UI.js new file mode 100644 index 0000000..fcb9a10 --- /dev/null +++ b/packages/colo17/UI.js @@ -0,0 +1,23 @@ +// cribbed from : https://developers.google.com/apps-script/guides/menus +// also: https://developers.google.com/apps-script/guides/sheets/functions + +function onOpen() { + console.warn('AMBIENT: SpreadsheetApp'); + const ui = SpreadsheetApp.getUi(); + + ui.createMenu('MadMode') + .addItem('Load Colo Invoices', 'LoadColoInvoices') + .addToUi(); + // .addSubMenu( + // ui + // .createMenu('Lunch Money') + // .addItem('Load Txs', 'loadLunchMoneyTransactions') + // .addItem('Save Txs', 'saveLunchMoneyTransactions'), + // ) +} + +function LoadColoInvoices() { + console.warn('AMBIENT: SpreadsheetApp'); + SpreadsheetApp.getUi() + .alert('TODO'); +} \ No newline at end of file diff --git a/packages/colo17/appsscript.json b/packages/colo17/appsscript.json new file mode 100644 index 0000000..37e66e1 --- /dev/null +++ b/packages/colo17/appsscript.json @@ -0,0 +1,14 @@ +{ + "timeZone": "America/Chicago", + "dependencies": { + "enabledAdvancedServices": [ + { + "userSymbol": "Sheets", + "version": "v4", + "serviceId": "sheets" + } + ] + }, + "exceptionLogging": "STACKDRIVER", + "runtimeVersion": "V8" +} \ No newline at end of file diff --git a/packages/colo17/coloInvoices.js b/packages/colo17/coloInvoices.js new file mode 100644 index 0000000..53541c6 --- /dev/null +++ b/packages/colo17/coloInvoices.js @@ -0,0 +1,63 @@ +const Colo = { + sheet: 'Expenses', + queryRange: 'coloInvoiceQuery', + hd: [ + 'Date', + 'Message Id', + 'Subject', + // 'Payee', + // 'Amount', + // 'Note', + // 'Account', + // 'Payment ID', +], +}; + +function messageDetail_(m) { + const subject = m.getSubject(); + // const body = m.getBody(); + + // const tx = maybeMatch( + // subject, + // /You (?:paid|completed) (?[^'\$]+)(?:'s )?\$(?[\d\.,]+)/, + // ); + // if (!tx.amount) { + // console.warn('no amount??', subject); + // } + // const acct = maybeMatch(body, /(from|via) your (?[^\.]+)/m).account; + // const pmtId = maybeMatch(body, /Payment ID: (?\d+)/m).id; + + return [ + m.getDate(), + m.getId(), + subject, + // tx.payee, + // maybeNumber(tx.amount), + // getNote(body), + // acct, + // pmtId, + ]; +} + +function LoadColoInvoices() { + console.warn('AMBIENT: SpreadsheetApp'); + const doc = SpreadsheetApp.getActive(); + const sheet = doc.getSheetByName(Colo.sheet); + + const query = doc.getRangeByName(Colo.queryRange).getValue(); + console.warn('AMBIENT: GmailApp'); + const threads = GmailApp.search(query); + const rows = []; + threads.forEach(thread => + thread.getMessages().forEach((m, ix) => { + if (ix > 0) { + console.warn('more than 1 thread in message', m); + return; + }; + const values = messageDetail_(m); + rows.push(values); + }), + ); + sheet.clear(); + setRange(sheet, Colo.hd, rows); +} diff --git a/packages/colo17/sheetTools.js b/packages/colo17/sheetTools.js new file mode 100644 index 0000000..ea03823 --- /dev/null +++ b/packages/colo17/sheetTools.js @@ -0,0 +1,4 @@ +function setRange(sheet, hd, rows, hdRow = 1, detailRow = hdRow + 1) { + sheet.getRange(hdRow, 1, 1, hd.length).setValues([hd]); + sheet.getRange(detailRow, 1, rows.length, hd.length).setValues(rows); +} From 095a81f6c9aa479f97529e409d4a0e1a50f99472 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Fri, 17 Feb 2023 10:26:46 -0600 Subject: [PATCH 2/5] build(colo17): clasp pull --- packages/colo17/package.json | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 packages/colo17/package.json diff --git a/packages/colo17/package.json b/packages/colo17/package.json new file mode 100644 index 0000000..7fab460 --- /dev/null +++ b/packages/colo17/package.json @@ -0,0 +1,5 @@ +{ + "scripts": { + "pull": "clasp -P . pull $SCRIPT_ID" + } +} From d985f39ceae5a8016409cd98b7a448e7e3605b8a Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Fri, 17 Feb 2023 11:43:53 -0600 Subject: [PATCH 3/5] feat(colo17): fetch prices automatically --- packages/colo17/priceFetch.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 packages/colo17/priceFetch.js diff --git a/packages/colo17/priceFetch.js b/packages/colo17/priceFetch.js new file mode 100644 index 0000000..777d429 --- /dev/null +++ b/packages/colo17/priceFetch.js @@ -0,0 +1,24 @@ +const CoinGecko = { + keyRange: 'priceKey', + priceApi: 'https://pro-api.coingecko.com/api/v3/simple/price' +}; + +/** + * ack: https://www.coingecko.com/en/api/documentation + */ +function FetchPrice(token='agoric', vs='usd') { + console.warn('AMBIENT: UrlFetchApp'); + const { fetch } = UrlFetchApp; + console.warn('AMBIENT: SpreadsheetApp'); + const doc = SpreadsheetApp.getActive(); + const apiKey = doc.getRangeByName(CoinGecko.keyRange).getValue(); + + const url = `${CoinGecko.priceApi}?ids=${token}&vs_currencies=${vs}&x_cg_pro_api_key=${apiKey}`; + + const resp = fetch(url, + { headers: {accept: 'application/json'}}); + const data = JSON.parse(resp.getContentText()); + // console.log(data); + const price = data[token].usd; + return price; +} From 1b743dae2373cef6c9d75772ba9c58d9199d0735 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sun, 19 Feb 2023 00:24:26 -0600 Subject: [PATCH 4/5] build(colo17): add Cheerio library --- packages/colo17/appsscript.json | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/packages/colo17/appsscript.json b/packages/colo17/appsscript.json index 37e66e1..aa87b75 100644 --- a/packages/colo17/appsscript.json +++ b/packages/colo17/appsscript.json @@ -7,6 +7,13 @@ "version": "v4", "serviceId": "sheets" } + ], + "libraries": [ + { + "userSymbol": "Cheerio", + "version": "14", + "libraryId": "1ReeQ6WO8kKNxoaA_O0XEQ589cIrRvEBA9qcWpNqdOP17i47u6N9M5Xh0" + } ] }, "exceptionLogging": "STACKDRIVER", From f91cabb00cbf6037d34073e45cf7cb4646fdea35 Mon Sep 17 00:00:00 2001 From: Dan Connolly Date: Sun, 19 Feb 2023 00:24:42 -0600 Subject: [PATCH 5/5] feat(colo17): parse invoice details --- packages/colo17/coloInvoices.js | 54 ++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/packages/colo17/coloInvoices.js b/packages/colo17/coloInvoices.js index 53541c6..2756043 100644 --- a/packages/colo17/coloInvoices.js +++ b/packages/colo17/coloInvoices.js @@ -2,40 +2,40 @@ const Colo = { sheet: 'Expenses', queryRange: 'coloInvoiceQuery', hd: [ - 'Date', - 'Message Id', - 'Subject', - // 'Payee', - // 'Amount', - // 'Note', - // 'Account', - // 'Payment ID', -], + 'Date', + 'Message Id', + 'From', + 'Subject', + 'Item #', + 'Description', + 'Amount', + ], }; -function messageDetail_(m) { - const subject = m.getSubject(); - // const body = m.getBody(); +function tableRecord_($) { + const { fromEntries } = Object; + + const [hd, detail] = $('tr', 'table'); + const td = [...$('td', detail)]; + const entries = [...$('th', hd)].map((th, col) => { + return [$(th).text(), $(td[col]).text()]; + }) - // const tx = maybeMatch( - // subject, - // /You (?:paid|completed) (?[^'\$]+)(?:'s )?\$(?[\d\.,]+)/, - // ); - // if (!tx.amount) { - // console.warn('no amount??', subject); - // } - // const acct = maybeMatch(body, /(from|via) your (?[^\.]+)/m).account; - // const pmtId = maybeMatch(body, /Payment ID: (?\d+)/m).id; + return fromEntries(entries); +} + +function messageDetail_(m) { + const $ = Cheerio.load(m.getBody()); + const detail = tableRecord_($); return [ m.getDate(), m.getId(), - subject, - // tx.payee, - // maybeNumber(tx.amount), - // getNote(body), - // acct, - // pmtId, + m.getFrom(), + m.getSubject(), + detail['Item #'], + detail['Description'], + detail['Amount'], ]; }