From 8c816c7de741945fc1ded84d59203fe0b9dca896 Mon Sep 17 00:00:00 2001 From: HugoPoi Date: Wed, 22 Dec 2021 17:36:33 +0100 Subject: [PATCH] feat: NodeRED implementation --- README.md | 36 +- last-invoice-to-leeto.sh | 38 -- node_red_data/flows.json | 630 ++++++++++++++++++-------------- node_red_data/package-lock.json | 18 +- node_red_data/package.json | 4 +- 5 files changed, 388 insertions(+), 338 deletions(-) delete mode 100755 last-invoice-to-leeto.sh diff --git a/README.md b/README.md index 2741675..0133a69 100644 --- a/README.md +++ b/README.md @@ -4,29 +4,35 @@ Automation tool for POSTING your [Netflix](https://www.netflix.com) invoice to [ ## Requirements -`apt install bash curl grep jq wkhtmltopdf` +* Docker +* An instance of [Url to PDF](https://github.com/alvarcarto/url-to-pdf-api) to convert html invoice to PDF ## Usage -1. `cp .env.netflix_cookies.example .env.netflix_cookies` -1. `cp config.sh.example .env.netflix_cookies` -1. Fill the `.env.netflix_cookies` with `SecureNetflixId` and `NetflixId`. - In firefox these are in the developper tools in `Storage/Cookies` -1. Fill the `config.sh` with your email and password for Leeto -1. Change the amount of your invoice usally either `7.99`, `11.99` or - `15.99` -1. If you're not part of [Monibrand](https://www.monibrand.com) you need - to change the `LEETO_ORGANISATION_ID` and `LEETO_QUOTUM_ID` with yours -1. `$ ./last-invoice-to-leeto.sh` -1. then clean the cached files `rm *.html *.pdf` +1. `cd node_red_data` +1. `npm install` +1. `docker run -it -p 1880:1880 -v $PWD/node_red_data:/data nodered/node-red` +1. You need to provide credentials for + * `url-to-pdf-api Credentials` + * `url` example `https://url-to-pdf-api.herokuapp.com/api/render` + * `apiToken` for Url to PDF if needed + * `Netflix Credentials` Netflix + * `netflixId` get the value from cookies in a web browser ( + through the inspector ) + * `secureNetflixId` get the value from cookies in a web browser ( + through the inspector ) + * `Leeto Credentials` [Leeto](https://app.leeto.co/) + * `login` is an email + * `password` ## TODO -* Put this in an automation cron like Zappier or IFTTT or NodeRED +* Add an automation cron +* Deploy to cloud something +* Multi-user (webui maybe ?) * Better handling error * Netflix login (can be a pain in the ass) * Trigger on the good invoice date of Netflix once a month -* Better error handling -* Handle `La somme demandée ne peut être supérieure à ses droits (somme restante: 4.01)` * Implement other invoice types like Spotify + diff --git a/last-invoice-to-leeto.sh b/last-invoice-to-leeto.sh deleted file mode 100755 index 69930b0..0000000 --- a/last-invoice-to-leeto.sh +++ /dev/null @@ -1,38 +0,0 @@ -#!/bin/bash -set -e -source ./config.sh -# Get the Netflix Billing Page -[[ -f "BillingActivity.html" ]] || curl -v 'https://www.netflix.com/BillingActivity'\ - -H 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8'\ - --compressed \ - --config .env.browser_fingerprint\ - --config .env.netflix_cookies > BillingActivity.html - -# Download the last invoice -LAST_INVOICE_URL=$(cat BillingActivity.html | grep -o '/invoice/print/[^"]*' | head -1) -[[ -f "LastInvoice.html" ]] || curl -v --compressed --config .env.netflix_cookies "$NETFLIX_BASE_URL$LAST_INVOICE_URL" > LastInvoice.html - -# Convert to pdf -[[ -f "LastInvoice.pdf" ]] || wkhtmltopdf LastInvoice.html LastInvoice.pdf - -# Leeto login -[[ -f "leeto_auth_payload.json" ]] || curl -v 'https://api.leeto.co/api/v2/users/sign_in?locale=fr'\ - -H 'Accept: application/json, text/plain, */*'\ - --config .env.browser_fingerprint\ - --compressed -H 'Content-Type: application/json'\ - --data-raw '{"user":{"email":"'"$LEETO_EMAIL"'","password":"'"$LEETO_PASSWORD"'"}}' > leeto_auth_payload.json - -LEETO_BEARER_TOKEN=$(cat leeto_auth_payload.json | jq --raw-output '.token') -LEETO_USER_ID=$(cat leeto_auth_payload.json | jq --raw-output '.id') - -curl -v "https://api.leeto.co/api/v2/organisations/$LEETO_ORGANISATION_ID/reimbursement_requests?locale=fr"\ - -H 'Accept: application/json, text/plain, */*'\ - --compressed \ - --header "Authorization: Bearer $LEETO_BEARER_TOKEN"\ - --config .env.browser_fingerprint\ - --form "reimbursement_request[user_id]=$LEETO_USER_ID"\ - --form "reimbursement_request[grantable_id]=$LEETO_USER_ID"\ - --form "reimbursement_request[grantable_type]=User"\ - --form "reimbursement_request[amount]=$LEETO_AMOUNT"\ - --form "reimbursement_request[quotum_id]=$LEETO_QUOTUM_ID"\ - --form "reimbursement_request[receipts_attributes][][image]=@LastInvoice.pdf" diff --git a/node_red_data/flows.json b/node_red_data/flows.json index 2897f79..fbcb4ea 100644 --- a/node_red_data/flows.json +++ b/node_red_data/flows.json @@ -45,6 +45,45 @@ "env": [], "color": "#DDAA99" }, + { + "id": "6d4fb2bd.73cc2c", + "type": "function", + "z": "fdd2168a.835ab8", + "name": "HTTP Headers for Netflix", + "func": "msg.headers = {};\nmsg.headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0';\nmsg.headers['Accept-Language'] = 'fr-FR,fr;q=0.5';\nmsg.headers['Cookie'] = [\n 'SecureNetflixId=' + msg.secureNetflixId,\n 'NetflixId=' + msg.netflixId,\n].join(';');\nreturn msg;", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "x": 410, + "y": 80, + "wires": [ + [] + ] + }, + { + "id": "bf25f228.3c6a5", + "type": "credentials", + "z": "fdd2168a.835ab8", + "name": "Netflix Credentials", + "props": [ + { + "value": "netflixId", + "type": "msg" + }, + { + "value": "secureNetflixId", + "type": "msg" + } + ], + "x": 190, + "y": 80, + "wires": [ + [ + "6d4fb2bd.73cc2c" + ] + ] + }, { "id": "f120e3b1.f67b38", "type": "inject", @@ -96,45 +135,6 @@ ], "info": "Get invoice page from Netflix" }, - { - "id": "6d4fb2bd.73cc2c", - "type": "function", - "z": "fdd2168a.835ab8", - "name": "HTTP Headers for Netflix", - "func": "msg.headers = {};\nmsg.headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0';\nmsg.headers['Accept-Language'] = 'fr-FR,fr;q=0.5';\nmsg.headers['Cookie'] = [\n 'SecureNetflixId=' + msg.secureNetflixId,\n 'NetflixId=' + msg.netflixId,\n].join(';');\nreturn msg;", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "x": 410, - "y": 80, - "wires": [ - [] - ] - }, - { - "id": "bf25f228.3c6a5", - "type": "credentials", - "z": "fdd2168a.835ab8", - "name": "Netflix Credentials", - "props": [ - { - "value": "netflixId", - "type": "msg" - }, - { - "value": "secureNetflixId", - "type": "msg" - } - ], - "x": 190, - "y": 80, - "wires": [ - [ - "6d4fb2bd.73cc2c" - ] - ] - }, { "id": "7ddcb19f.58034", "type": "http request", @@ -258,11 +258,253 @@ "type": "link out", "z": "e79fd0e1.60f55", "name": "Last Netflix Invoice as PDF", - "links": [], - "x": 455, - "y": 580, + "mode": "link", + "links": [ + "848ba398.23bef8" + ], + "x": 635, + "y": 540, "wires": [] }, + { + "id": "174093e7.8794e4", + "type": "html", + "z": "e79fd0e1.60f55", + "name": "", + "property": "payload", + "outproperty": "payload", + "tag": ".invoiceFooter dd", + "ret": "text", + "as": "single", + "x": 170, + "y": 400, + "wires": [ + [ + "5f5a3dc9.eab71c" + ] + ] + }, + { + "id": "d2bf2b71.93482", + "type": "change", + "z": "e79fd0e1.60f55", + "name": "Move as payload.file", + "rules": [ + { + "t": "move", + "p": "payload", + "pt": "msg", + "to": "payload.file", + "tot": "msg" + }, + { + "t": "set", + "p": "payload.filename", + "pt": "msg", + "to": "netflix-invoice.pdf", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 660, + "y": 400, + "wires": [ + [ + "97cde9aaf371b9ac" + ] + ] + }, + { + "id": "73c55749.b6bf7", + "type": "debug", + "z": "e79fd0e1.60f55", + "name": "Tap payload", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "true", + "targetType": "full", + "statusVal": "", + "statusType": "auto", + "x": 690, + "y": 480, + "wires": [] + }, + { + "id": "8e61a051.3430c8", + "type": "debug", + "z": "e79fd0e1.60f55", + "d": true, + "name": "Last invoice url", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "url", + "targetType": "msg", + "statusVal": "", + "statusType": "auto", + "x": 800, + "y": 100, + "wires": [] + }, + { + "id": "5f5a3dc9.eab71c", + "type": "change", + "z": "e79fd0e1.60f55", + "name": "parse amount", + "rules": [ + { + "t": "move", + "p": "payload[0]", + "pt": "msg", + "to": "textAmount", + "tot": "msg" + }, + { + "t": "set", + "p": "payload", + "pt": "msg", + "to": "{}", + "tot": "json" + }, + { + "t": "set", + "p": "payload.amount", + "pt": "msg", + "to": "$number($replace($match(textAmount, /[0-9\\,]+/, 1).match, ',', '.'))", + "tot": "jsonata" + }, + { + "t": "set", + "p": "payload.name", + "pt": "msg", + "to": "Netflix", + "tot": "str" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 360, + "y": 400, + "wires": [ + [ + "97cde9aaf371b9ac" + ] + ] + }, + { + "id": "643c6d17.cec784", + "type": "function", + "z": "e79fd0e1.60f55", + "name": "Cleaning payload", + "func": "return {payload: msg.payload};", + "outputs": 1, + "noerr": 0, + "initialize": "", + "finalize": "", + "x": 470, + "y": 500, + "wires": [ + [ + "73c55749.b6bf7", + "4c6af2dc.2504bc" + ] + ] + }, + { + "id": "2d46da6b.8dc806", + "type": "subflow:fdd2168a.835ab8", + "z": "e79fd0e1.60f55", + "name": "Netflix HTTP Auth", + "env": [], + "x": 170, + "y": 100, + "wires": [ + [ + "a9e0512d.6cdd7" + ] + ] + }, + { + "id": "797b306d.21639", + "type": "subflow:fdd2168a.835ab8", + "z": "e79fd0e1.60f55", + "name": "Netflix HTTP Auth", + "env": [], + "x": 170, + "y": 160, + "wires": [ + [ + "7ddcb19f.58034" + ] + ] + }, + { + "id": "41d7704e.4f4a48", + "type": "change", + "z": "e79fd0e1.60f55", + "name": "Extract last invoice url", + "rules": [ + { + "t": "set", + "p": "url", + "pt": "msg", + "to": "$join([\t 'https://www.netflix.com',\t $lookup($match(payload, /\\/invoice\\/print\\/[^\"]*/), 'match')[0]\t])", + "tot": "jsonata" + } + ], + "action": "", + "property": "", + "from": "", + "to": "", + "reg": false, + "x": 540, + "y": 100, + "wires": [ + [ + "8e61a051.3430c8", + "797b306d.21639" + ] + ] + }, + { + "id": "97cde9aaf371b9ac", + "type": "join", + "z": "e79fd0e1.60f55", + "name": "", + "mode": "custom", + "build": "merged", + "property": "payload", + "propertyType": "msg", + "key": "topic", + "joiner": "\\n", + "joinerType": "str", + "accumulate": false, + "timeout": "", + "count": "3", + "reduceRight": false, + "reduceExp": "", + "reduceInit": "", + "reduceInitType": "", + "reduceFixup": "", + "x": 260, + "y": 520, + "wires": [ + [ + "643c6d17.cec784" + ] + ], + "info": "NOTE : How the fuck this join require 3 as number of messages and not 2 as expected." + }, { "id": "debc0868.c889a", "type": "inject", @@ -382,7 +624,7 @@ "proxy": "", "authType": "", "x": 650, - "y": 420, + "y": 440, "wires": [ [ "337393c6.282844" @@ -394,16 +636,18 @@ "type": "function", "z": "34256184.7e2b8e", "name": "Leeto payload for reimbursement", - "func": "msg.headers = {};\nmsg.headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0';\nmsg.headers['Content-Type'] = 'multipart/form-data';\nmsg.headers['Accept'] = 'application/json, text/plain, */*';\nmsg.headers['Accept-Language'] = 'fr-FR,fr;q=0.5';\nmsg.headers['Authorization'] = 'Bearer ' + msg.leetoUser.token;\nmsg.payload = {\n 'reimbursement_request[user_id]': msg.leetoUser.id,\n 'reimbursement_request[grantable_id]': msg.leetoUser.id,\n 'reimbursement_request[grantable_type]': 'User',\n 'reimbursement_request[amount]': msg.invoice.amount,\n 'reimbursement_request[quotum_id]': msg.benefit.data.period.quota[0].id,\n 'reimbursement_request[receipts_attributes][][image]': {\n value: msg.invoice.file,\n options: {\n filename: msg.invoice.filename,\n contentType: msg.invoice.contentType,\n }\n }\n}\nreturn msg;", + "func": "msg.headers = {};\nmsg.headers['User-Agent'] = 'Mozilla/5.0 (X11; Linux x86_64; rv:82.0) Gecko/20100101 Firefox/82.0';\nmsg.headers['Content-Type'] = 'multipart/form-data';\nmsg.headers['Accept'] = 'application/json, text/plain, */*';\nmsg.headers['Accept-Language'] = 'fr-FR,fr;q=0.5';\nmsg.headers['Authorization'] = 'Bearer ' + msg.leetoUser.token;\n\nmsg.payload = {\n 'reimbursement_request[user_id]': msg.leetoUser.id,\n 'reimbursement_request[grantable_id]': msg.leetoUser.id,\n 'reimbursement_request[grantable_type]': 'User',\n 'reimbursement_request[quotum_id]': msg.benefit.data[0].quotumId,\n 'reimbursement_request[purchases][][generate_certificate_of_honour]': false,\n // 'reimbursement_request[purchases][][comments_attributes][][user_id]': msg.leetoUser.id,\n // 'reimbursement_request[purchases][][comments_attributes][][organisation_id]': msg.leetoUser.organisation.id,\n // 'reimbursement_request[purchases][][comments_attributes][][body]': 'This a comment',\n 'reimbursement_request[purchases][][purchase_name]': msg.invoice.name,\n 'reimbursement_request[purchases][][amount]': (msg.benefit.data[0].remainingAmount < msg.invoice.amount) ? msg.benefit.data[0].remainingAmount : msg.invoice.amount,\n 'reimbursement_request[purchases][][requested_amount]': msg.invoice.amount,\n 'reimbursement_request[purchases][][receipts_attributes][][image]': {\n value: msg.invoice.file,\n options: {\n filename: msg.invoice.filename,\n contentType: msg.invoice.contentType,\n }\n }\n}\nreturn msg;", "outputs": 1, "noerr": 0, "initialize": "", "finalize": "", + "libs": [], "x": 340, - "y": 420, + "y": 440, "wires": [ [ - "f5429dca.a2783" + "f5429dca.a2783", + "a689e897.689f68" ] ] }, @@ -421,7 +665,7 @@ "statusVal": "", "statusType": "auto", "x": 910, - "y": 420, + "y": 440, "wires": [] }, { @@ -472,7 +716,7 @@ "fieldType": "msg", "format": "handlebars", "syntax": "mustache", - "template": "https://api.leeto.co/api/v2/organisations/{{leetoUser.organisationId}}/users/{{leetoUser.id}}/benefits/{{leetoUser.authorizedBenefitIds.0}}?locale=fr", + "template": "https://api.leeto.co/api/v2/organisations/{{leetoUser.organisationId}}/users/{{leetoUser.id}}/quotum_usages?filter[by_state]=active&locale=fr", "output": "str", "x": 120, "y": 320, @@ -491,10 +735,10 @@ "fieldType": "msg", "format": "handlebars", "syntax": "mustache", - "template": "https://api.leeto.co/api/v2/organisations/{{leetoUser.organisationId}}/reimbursement_requests?locale=fr", + "template": "https://api.leeto.co/api/v2/organisations/{{leetoUser.organisationId}}/reimbursement_requests/bulk_create?locale=fr", "output": "str", "x": 120, - "y": 420, + "y": 440, "wires": [ [ "5571d225.8e12dc" @@ -506,7 +750,9 @@ "type": "link in", "z": "34256184.7e2b8e", "name": "Trigged from Netflix Last Invoice", - "links": [], + "links": [ + "4c6af2dc.2504bc" + ], "x": 475, "y": 120, "wires": [ @@ -570,236 +816,6 @@ ] ] }, - { - "id": "174093e7.8794e4", - "type": "html", - "z": "e79fd0e1.60f55", - "name": "", - "property": "payload", - "outproperty": "payload", - "tag": ".invoiceFooter dd", - "ret": "text", - "as": "multi", - "x": 170, - "y": 400, - "wires": [ - [ - "5f5a3dc9.eab71c" - ] - ] - }, - { - "id": "6af264f9.2f99dc", - "type": "join", - "z": "e79fd0e1.60f55", - "name": "", - "mode": "custom", - "build": "merged", - "property": "payload", - "propertyType": "msg", - "key": "topic", - "joiner": "\\n", - "joinerType": "str", - "accumulate": false, - "timeout": "", - "count": "2", - "reduceRight": false, - "reduceExp": "", - "reduceInit": "", - "reduceInitType": "num", - "reduceFixup": "", - "x": 110, - "y": 540, - "wires": [ - [ - "643c6d17.cec784" - ] - ] - }, - { - "id": "d2bf2b71.93482", - "type": "change", - "z": "e79fd0e1.60f55", - "name": "Move as payload.file", - "rules": [ - { - "t": "move", - "p": "payload", - "pt": "msg", - "to": "payload.file", - "tot": "msg" - }, - { - "t": "set", - "p": "payload.filename", - "pt": "msg", - "to": "netflix-invoice.pdf", - "tot": "str" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 660, - "y": 400, - "wires": [ - [ - "6af264f9.2f99dc" - ] - ] - }, - { - "id": "73c55749.b6bf7", - "type": "debug", - "z": "e79fd0e1.60f55", - "name": "", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "true", - "targetType": "full", - "statusVal": "", - "statusType": "auto", - "x": 490, - "y": 520, - "wires": [] - }, - { - "id": "8e61a051.3430c8", - "type": "debug", - "z": "e79fd0e1.60f55", - "name": "Last invoice url", - "active": true, - "tosidebar": true, - "console": false, - "tostatus": false, - "complete": "url", - "targetType": "msg", - "statusVal": "", - "statusType": "auto", - "x": 800, - "y": 100, - "wires": [] - }, - { - "id": "5f5a3dc9.eab71c", - "type": "change", - "z": "e79fd0e1.60f55", - "name": "parse amount", - "rules": [ - { - "t": "move", - "p": "payload", - "pt": "msg", - "to": "textAmount", - "tot": "msg" - }, - { - "t": "set", - "p": "payload", - "pt": "msg", - "to": "{}", - "tot": "json" - }, - { - "t": "set", - "p": "payload.amount", - "pt": "msg", - "to": "$number($replace($match(textAmount, /[0-9\\,]+/, 1).match, ',', '.'))", - "tot": "jsonata" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 360, - "y": 400, - "wires": [ - [ - "6af264f9.2f99dc" - ] - ] - }, - { - "id": "643c6d17.cec784", - "type": "function", - "z": "e79fd0e1.60f55", - "name": "Cleaning payload", - "func": "return {payload: msg.payload};", - "outputs": 1, - "noerr": 0, - "initialize": "", - "finalize": "", - "x": 290, - "y": 540, - "wires": [ - [ - "73c55749.b6bf7", - "4c6af2dc.2504bc" - ] - ] - }, - { - "id": "2d46da6b.8dc806", - "type": "subflow:fdd2168a.835ab8", - "z": "e79fd0e1.60f55", - "name": "Netflix HTTP Auth", - "env": [], - "x": 170, - "y": 100, - "wires": [ - [ - "a9e0512d.6cdd7" - ] - ] - }, - { - "id": "797b306d.21639", - "type": "subflow:fdd2168a.835ab8", - "z": "e79fd0e1.60f55", - "name": "Netflix HTTP Auth", - "env": [], - "x": 170, - "y": 160, - "wires": [ - [ - "7ddcb19f.58034" - ] - ] - }, - { - "id": "41d7704e.4f4a48", - "type": "change", - "z": "e79fd0e1.60f55", - "name": "Extract last invoice url", - "rules": [ - { - "t": "set", - "p": "url", - "pt": "msg", - "to": "$join([\t 'https://www.netflix.com',\t $lookup($match(payload, /\\/invoice\\/print\\/[^\"]*/), 'match')[0]\t])", - "tot": "jsonata" - } - ], - "action": "", - "property": "", - "from": "", - "to": "", - "reg": false, - "x": 540, - "y": 100, - "wires": [ - [ - "8e61a051.3430c8", - "797b306d.21639" - ] - ] - }, { "id": "8a75e7ed.ee7658", "type": "change", @@ -866,6 +882,41 @@ "pt": "msg", "to": "benefit", "tot": "msg" + }, + { + "t": "set", + "p": "parts.id", + "pt": "msg", + "to": "leeto-infos", + "tot": "str" + }, + { + "t": "set", + "p": "parts.index", + "pt": "msg", + "to": "0", + "tot": "str" + }, + { + "t": "set", + "p": "parts.count", + "pt": "msg", + "to": "2", + "tot": "str" + }, + { + "t": "set", + "p": "parts.type", + "pt": "msg", + "to": "object", + "tot": "str" + }, + { + "t": "set", + "p": "parts.key", + "pt": "msg", + "to": "benefit", + "tot": "str" } ], "action": "", @@ -880,5 +931,22 @@ "17d03aa5.9a232d" ] ] + }, + { + "id": "a689e897.689f68", + "type": "debug", + "z": "34256184.7e2b8e", + "name": "Payload for leeto", + "active": true, + "tosidebar": true, + "console": false, + "tostatus": false, + "complete": "true", + "targetType": "full", + "statusVal": "", + "statusType": "auto", + "x": 770, + "y": 580, + "wires": [] } ] \ No newline at end of file diff --git a/node_red_data/package-lock.json b/node_red_data/package-lock.json index 651f0bd..cdc22fb 100644 --- a/node_red_data/package-lock.json +++ b/node_red_data/package-lock.json @@ -1,8 +1,22 @@ { - "name": "node-red-project", + "name": "auto-invoice-leeto", "version": "0.0.1", - "lockfileVersion": 1, + "lockfileVersion": 2, "requires": true, + "packages": { + "": { + "name": "auto-invoice-leeto", + "version": "0.0.1", + "dependencies": { + "node-red-contrib-credentials": "^0.2.1" + } + }, + "node_modules/node-red-contrib-credentials": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-red-contrib-credentials/-/node-red-contrib-credentials-0.2.1.tgz", + "integrity": "sha512-fn82zQ2Tn/tsj/i634s+Oso2hEgMWC61lMU315cfwplURW+UIeZ5+dFCWOPYs4qlI2VUPW5qfzHPHg4OVjV/UQ==" + } + }, "dependencies": { "node-red-contrib-credentials": { "version": "0.2.1", diff --git a/node_red_data/package.json b/node_red_data/package.json index 966946f..46288a5 100644 --- a/node_red_data/package.json +++ b/node_red_data/package.json @@ -1,6 +1,6 @@ { - "name": "node-red-project", - "description": "A Node-RED Project", + "name": "auto-invoice-leeto", + "description": "A Node-RED Project to send Netflix invoice to leeto automatically", "version": "0.0.1", "private": true, "dependencies": {