From 7d141a4ab79adff9318bca5076474df6072c37d9 Mon Sep 17 00:00:00 2001 From: Aleksey Razbakov Date: Thu, 29 Dec 2022 20:59:18 +0100 Subject: [PATCH 01/20] weekly newsletter --- services/firebase/package.json | 9 +- services/firebase/src/cli.ts | 11 + services/firebase/src/templates/weekly.mjml | 176 +++ services/firebase/yarn.lock | 1149 ++++++++++++++++++- 4 files changed, 1339 insertions(+), 6 deletions(-) create mode 100644 services/firebase/src/templates/weekly.mjml diff --git a/services/firebase/package.json b/services/firebase/package.json index cca0ce0a..e7d54fb0 100644 --- a/services/firebase/package.json +++ b/services/firebase/package.json @@ -2,7 +2,9 @@ "name": "functions", "scripts": { "lint": "tslint --project tsconfig.json", - "build": "tsc", + "build": "yarn clean && tsc && yarn copy-files", + "copy-files": "copyfiles -u 1 src/**/*.js src/**/*.mjml dist/", + "clean": "rimraf dist/", "test": "npm run build && node dist/test.js", "cli": "npm run build && node dist/cli.js", "serve": "npm run build && firebase emulators:start --only functions", @@ -30,21 +32,26 @@ "express": "^4.17.1", "firebase-admin": "^8.13.0", "firebase-functions": "^3.22.0", + "fs": "^0.0.1-security", "handlebars": "^4.7.6", "https-proxy-agent": "^5.0.0", "instagram-private-api": "^1.45.3", "mailgun-js": "^0.22.0", "markdown-it": "^10.0.0", + "mjml": "^4.13.0", "node-fetch": "^3.2.10", "png-to-jpeg": "^1.0.1", "puppeteer": "^7.0.0", "telegraf": "^4.8.6", + "vue": "^3.2.45", "yargs": "^17.3.1" }, "devDependencies": { "@types/dotenv": "^8.2.0", "@types/puppeteer": "^5.4.2", + "copyfiles": "^2.4.1", "firebase-functions-test": "^0.1.6", + "rimraf": "^3.0.2", "tslint": "^5.12.0", "typescript": "^3.9.10" }, diff --git a/services/firebase/src/cli.ts b/services/firebase/src/cli.ts index 9cfee2f2..c0e44008 100644 --- a/services/firebase/src/cli.ts +++ b/services/firebase/src/cli.ts @@ -15,6 +15,7 @@ const { hideBin } = require('yargs/helpers') import { firestore } from './firebase' import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' +const { getWeeklyData, renderEmail } = require('./lib/digest.js') yargs(hideBin(process.argv)) .command( @@ -173,6 +174,16 @@ yargs(hideBin(process.argv)) await getCities() } ) + .command( + 'newsletter', + 'Generate Weekly Newsletter', + () => undefined, + async (argv: any) => { + const data = await getWeeklyData('Munich') + const html = await renderEmail('weekly', data) + console.log(html) + } + ) .command( 'cleanup:cities', 'Cleanup cities', diff --git a/services/firebase/src/templates/weekly.mjml b/services/firebase/src/templates/weekly.mjml new file mode 100644 index 00000000..b0b16cee --- /dev/null +++ b/services/firebase/src/templates/weekly.mjml @@ -0,0 +1,176 @@ + + + + + + + + + + View calendar in browser + + + + + + + + + + + {{ intro }} + {{ title }} + + + +
+ {{ group.day }} • {{ + group.date }} +
+ + + + + {{ event.time }} + + + +
+ {{ event.organizer }} • {{ event.venue }} +
+
+ {{ event.format }} • {{ + event.styles.join(' • ') }} +
+ + + + + +
+ + Show More + + Something is missing? + Add event. + +
+
+ + + + Follow us + + + We regularly post event announcements and introduce new members on + our social media. + + + + + + + + + +
+ + + + You are subscribed to + {{ title }}. Visit the page to Unsubscribe. + + + +
+
diff --git a/services/firebase/yarn.lock b/services/firebase/yarn.lock index 9f248008..aeb5605f 100644 --- a/services/firebase/yarn.lock +++ b/services/firebase/yarn.lock @@ -127,6 +127,18 @@ chalk "^2.0.0" js-tokens "^4.0.0" +"@babel/parser@^7.16.4": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.20.7.tgz#66fe23b3c8569220817d5feb8b9dcdc95bb4f71b" + integrity sha512-T3Z9oHybU+0vZlY9CiDSJQTD5ZapcW18ZctFMi0MOAl/4BjFF4ul7NVSARLdbGO5vDqy9eQiGTV0LtKfvCYvcg== + +"@babel/runtime@^7.14.6": + version "7.20.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.7.tgz#fcb41a5a70550e04a7b708037c7c32f7f356d8fd" + integrity sha512-UF0tvkUtxwAgZ5W/KrkHf0Rn0fdnLDU9ScxBrEVNUprE/MzirjK4MJUX1/BVDv00Sv8cljtukVK1aky++X1SjQ== + dependencies: + regenerator-runtime "^0.13.11" + "@firebase/app-types@0.6.1": version "0.6.1" resolved "https://registry.yarnpkg.com/@firebase/app-types/-/app-types-0.6.1.tgz#dcbd23030a71c0c74fc95d4a3f75ba81653850e9" @@ -657,6 +669,101 @@ dependencies: "@types/node" "*" +"@vue/compiler-core@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/compiler-core/-/compiler-core-3.2.45.tgz#d9311207d96f6ebd5f4660be129fb99f01ddb41b" + integrity sha512-rcMj7H+PYe5wBV3iYeUgbCglC+pbpN8hBLTJvRiK2eKQiWqu+fG9F+8sW99JdL4LQi7Re178UOxn09puSXvn4A== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/shared" "3.2.45" + estree-walker "^2.0.2" + source-map "^0.6.1" + +"@vue/compiler-dom@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.2.45.tgz#c43cc15e50da62ecc16a42f2622d25dc5fd97dce" + integrity sha512-tyYeUEuKqqZO137WrZkpwfPCdiiIeXYCcJ8L4gWz9vqaxzIQRccTSwSWZ/Axx5YR2z+LvpUbmPNXxuBU45lyRw== + dependencies: + "@vue/compiler-core" "3.2.45" + "@vue/shared" "3.2.45" + +"@vue/compiler-sfc@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/compiler-sfc/-/compiler-sfc-3.2.45.tgz#7f7989cc04ec9e7c55acd406827a2c4e96872c70" + integrity sha512-1jXDuWah1ggsnSAOGsec8cFjT/K6TMZ0sPL3o3d84Ft2AYZi2jWJgRMjw4iaK0rBfA89L5gw427H4n1RZQBu6Q== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.45" + "@vue/compiler-dom" "3.2.45" + "@vue/compiler-ssr" "3.2.45" + "@vue/reactivity-transform" "3.2.45" + "@vue/shared" "3.2.45" + estree-walker "^2.0.2" + magic-string "^0.25.7" + postcss "^8.1.10" + source-map "^0.6.1" + +"@vue/compiler-ssr@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/compiler-ssr/-/compiler-ssr-3.2.45.tgz#bd20604b6e64ea15344d5b6278c4141191c983b2" + integrity sha512-6BRaggEGqhWht3lt24CrIbQSRD5O07MTmd+LjAn5fJj568+R9eUD2F7wMQJjX859seSlrYog7sUtrZSd7feqrQ== + dependencies: + "@vue/compiler-dom" "3.2.45" + "@vue/shared" "3.2.45" + +"@vue/reactivity-transform@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/reactivity-transform/-/reactivity-transform-3.2.45.tgz#07ac83b8138550c83dfb50db43cde1e0e5e8124d" + integrity sha512-BHVmzYAvM7vcU5WmuYqXpwaBHjsS8T63jlKGWVtHxAHIoMIlmaMyurUSEs1Zcg46M4AYT5MtB1U274/2aNzjJQ== + dependencies: + "@babel/parser" "^7.16.4" + "@vue/compiler-core" "3.2.45" + "@vue/shared" "3.2.45" + estree-walker "^2.0.2" + magic-string "^0.25.7" + +"@vue/reactivity@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/reactivity/-/reactivity-3.2.45.tgz#412a45b574de601be5a4a5d9a8cbd4dee4662ff0" + integrity sha512-PRvhCcQcyEVohW0P8iQ7HDcIOXRjZfAsOds3N99X/Dzewy8TVhTCT4uXpAHfoKjVTJRA0O0K+6QNkDIZAxNi3A== + dependencies: + "@vue/shared" "3.2.45" + +"@vue/runtime-core@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/runtime-core/-/runtime-core-3.2.45.tgz#7ad7ef9b2519d41062a30c6fa001ec43ac549c7f" + integrity sha512-gzJiTA3f74cgARptqzYswmoQx0fIA+gGYBfokYVhF8YSXjWTUA2SngRzZRku2HbGbjzB6LBYSbKGIaK8IW+s0A== + dependencies: + "@vue/reactivity" "3.2.45" + "@vue/shared" "3.2.45" + +"@vue/runtime-dom@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/runtime-dom/-/runtime-dom-3.2.45.tgz#1a2ef6ee2ad876206fbbe2a884554bba2d0faf59" + integrity sha512-cy88YpfP5Ue2bDBbj75Cb4bIEZUMM/mAkDMfqDTpUYVgTf/kuQ2VQ8LebuZ8k6EudgH8pYhsGWHlY0lcxlvTwA== + dependencies: + "@vue/runtime-core" "3.2.45" + "@vue/shared" "3.2.45" + csstype "^2.6.8" + +"@vue/server-renderer@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/server-renderer/-/server-renderer-3.2.45.tgz#ca9306a0c12b0530a1a250e44f4a0abac6b81f3f" + integrity sha512-ebiMq7q24WBU1D6uhPK//2OTR1iRIyxjF5iVq/1a5I1SDMDyDu4Ts6fJaMnjrvD3MqnaiFkKQj+LKAgz5WIK3g== + dependencies: + "@vue/compiler-ssr" "3.2.45" + "@vue/shared" "3.2.45" + +"@vue/shared@3.2.45": + version "3.2.45" + resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.2.45.tgz#a3fffa7489eafff38d984e23d0236e230c818bc2" + integrity sha512-Ewzq5Yhimg7pSztDV+RH1UDKBzmtqieXQlpTVm2AwraoRL/Rks96mvd8Vgi7Lj+h+TH8dv7mXD3FRZR3TUvbSg== + +abbrev@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" + integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== + abort-controller@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" @@ -723,6 +830,11 @@ algoliasearch@^4.8.6: "@algolia/requester-node-http" "4.12.1" "@algolia/transporter" "4.12.1" +ansi-colors@^4.1.1: + version "4.1.3" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.3.tgz#37611340eb2243e70cc604cad35d63270d48781b" + integrity sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw== + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -742,6 +854,14 @@ ansi-styles@^4.0.0: dependencies: color-convert "^2.0.1" +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -839,6 +959,11 @@ bignumber.js@^9.0.0: resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-9.0.2.tgz#71c6c6bed38de64e24a65ebe16cfcf23ae693673" integrity sha512-GAcQvbpsM0pUb0zw1EI0KhQEZ+lRwR5fYaAp3vPOYuP7aDvGy6cVN6XHLauvF8SOga2y0dcLcjt3iQDTSEliyw== +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + bl@^4.0.3: version "4.1.0" resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" @@ -869,6 +994,11 @@ body-parser@1.19.1: raw-body "2.4.2" type-is "~1.6.18" +boolbase@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -877,6 +1007,20 @@ brace-expansion@^1.1.7: balanced-match "^1.0.0" concat-map "0.0.1" +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + buffer-alloc-unsafe@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz#bd7dc26ae2972d0eda253be061dba992349c19f0" @@ -936,6 +1080,14 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +camel-case@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-3.0.0.tgz#ca3c3688a4e9cf3a4cda777dc4dcbc713249cf73" + integrity sha512-+MbKztAYHXPr1jNTSKQF52VpcFjwY5RkR7fxksV8Doo4KAYc5Fl4UJRgthBbTmEx8C54DqahhbLJkDwjI3PI/w== + dependencies: + no-case "^2.2.0" + upper-case "^1.1.1" + caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" @@ -955,6 +1107,70 @@ chance@^1.0.18: resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.8.tgz#5d6c2b78c9170bf6eb9df7acdda04363085be909" integrity sha512-v7fi5Hj2VbR6dJEGRWLmJBA83LJMS47pkAbmROFxHWd9qmE1esHRZW8Clf1Fhzr3rjxnNZVCjOEv/ivFxeIMtg== +cheerio-select@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-1.6.0.tgz#489f36604112c722afa147dedd0d4609c09e1696" + integrity sha512-eq0GdBvxVFbqWgmCm7M3XGs1I8oLy/nExUnh6oLqmBditPO9AqQJrkslDpMun/hZ0yyTs8L0m85OHp4ho6Qm9g== + dependencies: + css-select "^4.3.0" + css-what "^6.0.1" + domelementtype "^2.2.0" + domhandler "^4.3.1" + domutils "^2.8.0" + +cheerio-select@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cheerio-select/-/cheerio-select-2.1.0.tgz#4d8673286b8126ca2a8e42740d5e3c4884ae21b4" + integrity sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g== + dependencies: + boolbase "^1.0.0" + css-select "^5.1.0" + css-what "^6.1.0" + domelementtype "^2.3.0" + domhandler "^5.0.3" + domutils "^3.0.1" + +cheerio@1.0.0-rc.10: + version "1.0.0-rc.10" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.10.tgz#2ba3dcdfcc26e7956fc1f440e61d51c643379f3e" + integrity sha512-g0J0q/O6mW8z5zxQ3A8E8J1hUgp4SMOvEoW/x84OwyHKe/Zccz83PVT4y5Crcr530FV6NgmKI1qvGTKVl9XXVw== + dependencies: + cheerio-select "^1.5.0" + dom-serializer "^1.3.2" + domhandler "^4.2.0" + htmlparser2 "^6.1.0" + parse5 "^6.0.1" + parse5-htmlparser2-tree-adapter "^6.0.1" + tslib "^2.2.0" + +cheerio@^1.0.0-rc.3: + version "1.0.0-rc.12" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.12.tgz#788bf7466506b1c6bf5fae51d24a2c4d62e47683" + integrity sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q== + dependencies: + cheerio-select "^2.1.0" + dom-serializer "^2.0.0" + domhandler "^5.0.3" + domutils "^3.0.1" + htmlparser2 "^8.0.1" + parse5 "^7.0.0" + parse5-htmlparser2-tree-adapter "^7.0.0" + +chokidar@^3.0.0: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + chownr@^1.1.1: version "1.1.4" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.4.tgz#6fc9d7b42d32a583596337666e7d08084da2cc6b" @@ -965,6 +1181,13 @@ class-transformer@^0.3.1: resolved "https://registry.yarnpkg.com/class-transformer/-/class-transformer-0.3.2.tgz#779ef5f124784324b40f8e927c774bd96cdecd4b" integrity sha512-9QY6QXBH/+Gt1C3HBmJCrgY6+EFpIa6aLjfDnlXFx0zQl/HjrCE7qoaI0srNrxpMIfsobCpgUdDG5JYtJOpVsw== +clean-css@^4.2.1: + version "4.2.4" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.2.4.tgz#733bf46eba4e607c6891ea57c24a989356831178" + integrity sha512-EJUDT7nDVFDvaQgAo2G/PJvxmp1o/c6iXLbswsBbUFXi1Nr+AjA2cKmfbKDMjMvzEe75g3P6JkaDDAKk96A85A== + dependencies: + source-map "~0.6.0" + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -1010,11 +1233,16 @@ combined-stream@^1.0.6, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" -commander@^2.12.1: +commander@^2.12.1, commander@^2.19.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== +commander@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae" + integrity sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg== + compressible@^2.0.12: version "2.0.18" resolved "https://registry.yarnpkg.com/compressible/-/compressible-2.0.18.tgz#af53cca6b070d4c3c0750fbd77286a6d7cc46fba" @@ -1037,6 +1265,14 @@ concat-stream@^2.0.0: readable-stream "^3.0.2" typedarray "^0.0.6" +config-chain@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/config-chain/-/config-chain-1.1.13.tgz#fad0795aa6a6cdaff9ed1b68e9dff94372c232f4" + integrity sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ== + dependencies: + ini "^1.3.4" + proto-list "~1.2.1" + configstore@^5.0.0: version "5.0.1" resolved "https://registry.yarnpkg.com/configstore/-/configstore-5.0.1.tgz#d365021b5df4b98cdd187d6a3b0e3f6a7cc5ed96" @@ -1076,6 +1312,19 @@ cookie@^0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +copyfiles@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5" + integrity sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg== + dependencies: + glob "^7.0.5" + minimatch "^3.0.3" + mkdirp "^1.0.4" + noms "0.0.0" + through2 "^2.0.1" + untildify "^4.0.0" + yargs "^16.1.0" + core-util-is@1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" @@ -1099,6 +1348,38 @@ crypto-random-string@^2.0.0: resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" integrity sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA== +css-select@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-4.3.0.tgz#db7129b2846662fd8628cfc496abb2b59e41529b" + integrity sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ== + dependencies: + boolbase "^1.0.0" + css-what "^6.0.1" + domhandler "^4.3.1" + domutils "^2.8.0" + nth-check "^2.0.1" + +css-select@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-5.1.0.tgz#b8ebd6554c3637ccc76688804ad3f6a6fdaea8a6" + integrity sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg== + dependencies: + boolbase "^1.0.0" + css-what "^6.1.0" + domhandler "^5.0.2" + domutils "^3.0.1" + nth-check "^2.0.1" + +css-what@^6.0.1, css-what@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-6.1.0.tgz#fb5effcf76f1ddea2c81bdfaa4de44e79bac70f4" + integrity sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw== + +csstype@^2.6.8: + version "2.6.21" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.21.tgz#2efb85b7cc55c80017c66a5ad7cbd931fda3a90e" + integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== + dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -1213,6 +1494,11 @@ destroy@~1.0.4: resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" integrity sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA= +detect-node@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c" + integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw== + devtools-protocol@0.0.847576: version "0.0.847576" resolved "https://registry.yarnpkg.com/devtools-protocol/-/devtools-protocol-0.0.847576.tgz#2f201bfb68aa9ef4497199fbd7f5d5dfde3b200b" @@ -1230,6 +1516,68 @@ diff@^4.0.1: resolved "https://registry.yarnpkg.com/diff/-/diff-4.0.2.tgz#60f3aecb89d5fae520c11aa19efc2bb982aade7d" integrity sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A== +dom-serializer@^1.0.1, dom-serializer@^1.3.2: + version "1.4.1" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-1.4.1.tgz#de5d41b1aea290215dc45a6dae8adcf1d32e2d30" + integrity sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.2.0" + entities "^2.0.0" + +dom-serializer@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-2.0.0.tgz#e41b802e1eedf9f6cae183ce5e622d789d7d8e53" + integrity sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + entities "^4.2.0" + +domelementtype@^2.0.1, domelementtype@^2.2.0, domelementtype@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.3.0.tgz#5c45e8e869952626331d7aab326d01daf65d589d" + integrity sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw== + +domhandler@^3.0.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-3.3.0.tgz#6db7ea46e4617eb15cf875df68b2b8524ce0037a" + integrity sha512-J1C5rIANUbuYK+FuFL98650rihynUOEzRLxW+90bKZRWB6A1X1Tf82GxR1qAWLyfNPRvjqfip3Q5tdYlmAa9lA== + dependencies: + domelementtype "^2.0.1" + +domhandler@^4.0.0, domhandler@^4.2.0, domhandler@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-4.3.1.tgz#8d792033416f59d68bc03a5aa7b018c1ca89279c" + integrity sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ== + dependencies: + domelementtype "^2.2.0" + +domhandler@^5.0.1, domhandler@^5.0.2, domhandler@^5.0.3: + version "5.0.3" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-5.0.3.tgz#cc385f7f751f1d1fc650c21374804254538c7d31" + integrity sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w== + dependencies: + domelementtype "^2.3.0" + +domutils@^2.0.0, domutils@^2.5.2, domutils@^2.8.0: + version "2.8.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-2.8.0.tgz#4437def5db6e2d1f5d6ee859bd95ca7d02048135" + integrity sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A== + dependencies: + dom-serializer "^1.0.1" + domelementtype "^2.2.0" + domhandler "^4.2.0" + +domutils@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-3.0.1.tgz#696b3875238338cb186b6c0612bd4901c89a4f1c" + integrity sha512-z08c1l761iKhDFtfXO04C7kTdPBLi41zwOZl00WS8b5eiaebNpY00HKbztwBq+e3vyqWNwWF3mP9YLUeqIrF+Q== + dependencies: + dom-serializer "^2.0.0" + domelementtype "^2.3.0" + domhandler "^5.0.1" + dot-prop@^5.2.0: version "5.3.0" resolved "https://registry.yarnpkg.com/dot-prop/-/dot-prop-5.3.0.tgz#90ccce708cd9cd82cc4dc8c3ddd9abdd55b20e88" @@ -1282,6 +1630,16 @@ ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: dependencies: safe-buffer "^5.0.1" +editorconfig@^0.15.3: + version "0.15.3" + resolved "https://registry.yarnpkg.com/editorconfig/-/editorconfig-0.15.3.tgz#bef84c4e75fb8dcb0ce5cee8efd51c15999befc5" + integrity sha512-M9wIMFx96vq0R4F+gRpY3o2exzb8hEj/n9S8unZtHSvYjibBp/iMufSzvmOcV/laG0ZtuTVGtiJggPOSW2r93g== + dependencies: + commander "^2.19.0" + lru-cache "^4.1.5" + semver "^5.6.0" + sigmund "^1.0.1" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -1309,6 +1667,16 @@ ent@^2.2.0: resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" integrity sha1-6WQhkyWiHQX0RGai9obtbOX13R0= +entities@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.2.0.tgz#098dc90ebb83d8dffa089d55256b351d34c4da55" + integrity sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A== + +entities@^4.2.0, entities@^4.3.0, entities@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.4.0.tgz#97bdaba170339446495e653cfd2db78962900174" + integrity sha512-oYp7156SP8LkeGD0GF85ad1X9Ai79WtRsZ2gxJqtBuzH+98YUV6jkHEKlZkMbcrjJjIVJNIDP/3WL9wQkoPbWA== + entities@~2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" @@ -1380,6 +1748,11 @@ escalade@^3.1.1: resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.1.tgz#d8cfdc7000965c5a0174b4a82eaa5c0552742e40" integrity sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw== +escape-goat@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-3.0.0.tgz#e8b5fb658553fe8a3c4959c316c6ebb8c842b19c" + integrity sha512-w3PwNZJwRxlp47QGzhuEBldEqVHHhh8/tIPcl6ecf2Bou99cdAt0knihBV0Ecc7CGxYduXVBDheH1K2oADRlvw== + escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" @@ -1417,6 +1790,11 @@ estraverse@^4.2.0: resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== +estree-walker@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/estree-walker/-/estree-walker-2.0.2.tgz#52f010178c2a4c117a7757cfe942adb7d2da4cac" + integrity sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w== + esutils@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" @@ -1551,6 +1929,13 @@ file-uri-to-path@1: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" @@ -1671,6 +2056,16 @@ fs.realpath@^1.0.0: resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" integrity sha1-FQStJSMVjKpA20onh8sBQRmU6k8= +fs@^0.0.1-security: + version "0.0.1-security" + resolved "https://registry.yarnpkg.com/fs/-/fs-0.0.1-security.tgz#8a7bd37186b6dddf3813f23858b57ecaaf5e41d4" + integrity sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w== + +fsevents@~2.3.2: + version "2.3.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a" + integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA== + ftp@~0.3.10: version "0.3.10" resolved "https://registry.yarnpkg.com/ftp/-/ftp-0.3.10.tgz#9197d861ad8142f3e63d5a83bfe4c59f7330885d" @@ -1779,6 +2174,25 @@ getpass@^0.1.1: dependencies: assert-plus "^1.0.0" +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob@^7.0.5: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + glob@^7.1.1, glob@^7.1.3: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" @@ -1791,6 +2205,17 @@ glob@^7.1.1, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" +glob@^8.0.3: + version "8.0.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-8.0.3.tgz#415c6eb2deed9e502c68fa44a272e6da6eeca42e" + integrity sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^5.0.1" + once "^1.3.0" + google-auth-library@^5.0.0, google-auth-library@^5.5.0: version "5.10.1" resolved "https://registry.yarnpkg.com/google-auth-library/-/google-auth-library-5.10.1.tgz#504ec75487ad140e68dd577c21affa363c87ddff" @@ -1908,6 +2333,54 @@ hash-stream-validation@^0.2.2: resolved "https://registry.yarnpkg.com/hash-stream-validation/-/hash-stream-validation-0.2.4.tgz#ee68b41bf822f7f44db1142ec28ba9ee7ccb7512" integrity sha512-Gjzu0Xn7IagXVkSu9cSFuK1fqzwtLwFhNhVL8IFJijRNMgUttFbBSIAzKuSIrsFMO1+g1RlsoN49zPIbwPDMGQ== +he@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +html-minifier@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/html-minifier/-/html-minifier-4.0.0.tgz#cca9aad8bce1175e02e17a8c33e46d8988889f56" + integrity sha512-aoGxanpFPLg7MkIl/DDFYtb0iWz7jMFGqFhvEDZga6/4QTjneiD8I/NXL1x5aaoCp7FSIT6h/OhykDdPsbtMig== + dependencies: + camel-case "^3.0.0" + clean-css "^4.2.1" + commander "^2.19.0" + he "^1.2.0" + param-case "^2.1.1" + relateurl "^0.2.7" + uglify-js "^3.5.1" + +htmlparser2@^4.0.0, htmlparser2@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-4.1.0.tgz#9a4ef161f2e4625ebf7dfbe6c0a2f52d18a59e78" + integrity sha512-4zDq1a1zhE4gQso/c5LP1OtrhYTncXNSpvJYtWJBtXAETPlMfi3IFNjGuQbYLuVY4ZR0QMqRVvo4Pdy9KLyP8Q== + dependencies: + domelementtype "^2.0.1" + domhandler "^3.0.0" + domutils "^2.0.0" + entities "^2.0.0" + +htmlparser2@^6.1.0: + version "6.1.0" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7" + integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A== + dependencies: + domelementtype "^2.0.1" + domhandler "^4.0.0" + domutils "^2.5.2" + entities "^2.0.0" + +htmlparser2@^8.0.1: + version "8.0.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-8.0.1.tgz#abaa985474fcefe269bc761a779b544d7196d010" + integrity sha512-4lVbmc1diZC7GUJQtRQ5yBAeUCL1exyMwmForWkRLnwyzWBFxN633SALPMGYaWZvKe9j1pRZJpauvmxENSp/EA== + dependencies: + domelementtype "^2.3.0" + domhandler "^5.0.2" + domutils "^3.0.1" + entities "^4.3.0" + http-errors@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" @@ -2011,6 +2484,11 @@ inherits@2, inherits@2.0.4, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, i resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== +ini@^1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.8.tgz#a29da425b48806f34767a4efce397269af28432c" + integrity sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew== + instagram-private-api@^1.45.3: version "1.45.3" resolved "https://registry.yarnpkg.com/instagram-private-api/-/instagram-private-api-1.45.3.tgz#c5273b278c967645fae4b16cb4aac58a45f88567" @@ -2077,6 +2555,13 @@ is-bigint@^1.0.1: dependencies: has-bigints "^1.0.1" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-boolean-object@^1.1.0: version "1.1.2" resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.1.2.tgz#5c6dc200246dd9321ae4b885a114bb1f75f63719" @@ -2104,11 +2589,23 @@ is-date-object@^1.0.1, is-date-object@^1.0.2: dependencies: has-tostringtag "^1.0.0" +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + is-fullwidth-code-point@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + is-map@^2.0.1, is-map@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" @@ -2126,6 +2623,11 @@ is-number-object@^1.0.4: dependencies: has-tostringtag "^1.0.0" +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-obj@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-2.0.0.tgz#473fb05d973705e3fd9620545018ca8e22ef4982" @@ -2239,6 +2741,16 @@ jpeg-js@^0.1.2: resolved "https://registry.yarnpkg.com/jpeg-js/-/jpeg-js-0.1.2.tgz#135b992c0575c985cfa0f494a3227ed238583ece" integrity sha512-CiRVjMKBUp6VYtGwzRjrdnro41yMwKGOWdP9ATXqmixdz2n7KHNwdqthTYSSbOKj/Ha79Gct1sA8ZQpse55TYA== +js-beautify@^1.6.14: + version "1.14.7" + resolved "https://registry.yarnpkg.com/js-beautify/-/js-beautify-1.14.7.tgz#9206296de33f86dc106d3e50a35b7cf8729703b2" + integrity sha512-5SOX1KXPFKx+5f6ZrPsIPEY7NwKeQz47n3jm2i+XeHx9MoRsfQenlOP13FQhWvg8JRS0+XLO6XYUQ2GX+q+T9A== + dependencies: + config-chain "^1.1.13" + editorconfig "^0.15.3" + glob "^8.0.3" + nopt "^6.0.0" + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -2312,6 +2824,17 @@ jsprim@^1.2.2: json-schema "0.4.0" verror "1.10.0" +juice@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/juice/-/juice-7.0.0.tgz#509bed6adbb6e4bbaa7fbfadac4e2e83e8c89ba3" + integrity sha512-AjKQX31KKN+uJs+zaf+GW8mBO/f/0NqSh2moTMyvwBY+4/lXIYTU8D8I2h6BAV3Xnz6GGsbalUyFqbYMe+Vh+Q== + dependencies: + cheerio "^1.0.0-rc.3" + commander "^5.1.0" + mensch "^0.3.4" + slick "^1.12.2" + web-resource-inliner "^5.0.0" + jwa@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" @@ -2418,7 +2941,7 @@ lodash.once@^4.0.0: resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= -lodash@^4.17.14, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.5: +lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20, lodash@^4.17.21, lodash@^4.17.5: version "4.17.21" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== @@ -2428,6 +2951,19 @@ long@^4.0.0: resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== +lower-case@^1.1.1: + version "1.1.4" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac" + integrity sha512-2Fgx1Ycm599x+WGpIYwJOvsjmXFzTSc34IwDWALRA/8AopUKAVPwfJ+h5+f85BCp0PWmmJcWzEpxOpoXycMpdA== + +lru-cache@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lru-cache@^5.0.0, lru-cache@^5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-5.1.1.tgz#1da27e6710271947695daf6848e847f01d84b920" @@ -2445,6 +2981,13 @@ luxon@^1.12.1: resolved "https://registry.yarnpkg.com/luxon/-/luxon-1.28.0.tgz#e7f96daad3938c06a62de0fb027115d251251fbf" integrity sha512-TfTiyvZhwBYM/7QdAVDh+7dBTBA29v4ik0Ce9zda3Mnf8on1S5KJI8P2jKFZ8+5C0jhmr0KwJEO/Wdpm0VeWJQ== +magic-string@^0.25.7: + version "0.25.9" + resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.25.9.tgz#de7f9faf91ef8a1c91d02c2e5314c8277dbcdd1c" + integrity sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ== + dependencies: + sourcemap-codec "^1.4.8" + mailgun-js@^0.22.0: version "0.22.0" resolved "https://registry.yarnpkg.com/mailgun-js/-/mailgun-js-0.22.0.tgz#128942b5e47a364a470791608852bf68c96b3a05" @@ -2493,6 +3036,11 @@ media-typer@0.3.0: resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" integrity sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= +mensch@^0.3.4: + version "0.3.4" + resolved "https://registry.yarnpkg.com/mensch/-/mensch-0.3.4.tgz#770f91b46cb16ea5b204ee735768c3f0c491fecd" + integrity sha512-IAeFvcOnV9V0Yk+bFhYR07O3yNina9ANIN5MoXBKYJ/RLYPurd2d0yw14MDhpr9/momp0WofT1bPUh3hkzdi/g== + merge-descriptors@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.1.tgz#b00aaa556dd8b44568150ec9d1b953f3f90cbb61" @@ -2532,7 +3080,7 @@ mime@1.6.0: resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1" integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg== -mime@^2.2.0: +mime@^2.2.0, mime@^2.4.6: version "2.6.0" resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== @@ -2542,6 +3090,13 @@ mimic-fn@^2.1.0: resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b" integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg== +minimatch@^3.0.3, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimatch@^3.0.4: version "3.0.4" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.4.tgz#5166e286457f03306064be5497e8dbb0c3d32083" @@ -2549,6 +3104,13 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@^5.0.1: + version "5.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.1.2.tgz#0939d7d6f0898acbd1508abe534d1929368a8fff" + integrity sha512-bNH9mmM9qsJ2X4r2Nat1B//1dJVcn3+iBLa3IgqJ7EbGaDNepL9QSHOxN4ng33s52VMMhhIfgCYDk3C4ZmlDAg== + dependencies: + brace-expansion "^2.0.1" + minimist@^1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602" @@ -2559,6 +3121,338 @@ minimist@^1.2.6: resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" integrity sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q== +mjml-accordion@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-accordion/-/mjml-accordion-4.13.0.tgz#76ec0b5efe372e129077beaa0b2147c99d76eb7a" + integrity sha512-E3yihZW5Oq2p+sWOcr8kWeRTROmiTYOGxB4IOxW/jTycdY07N3FX3e6vuh7Fv3rryHEUaydUQYto3ICVyctI7w== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-body@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-body/-/mjml-body-4.13.0.tgz#35049f014f05c4a00ca1a29d3ba732bab2a40269" + integrity sha512-S4HgwAuO9dEsyX9sr6WBf9/xr+H2ASVaLn22aurJm1S2Lvc1wifLPYBQgFmNdCjaesTCNtOMUDpG+Rbnavyaqg== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-button@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-button/-/mjml-button-4.13.0.tgz#0d72a26ac5474be0fb8a921381acb5b6096b9542" + integrity sha512-3y8IAHCCxh7ESHh1aOOqobZKUgyNxOKAGQ9TlJoyaLpsKUFzkN8nmrD0KXF0ADSuzvhMZ1CdRIJuZ5mjv2TwWQ== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-carousel@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-carousel/-/mjml-carousel-4.13.0.tgz#c43dbf0be48f77096048019f197575b8fad62826" + integrity sha512-ORSY5bEYlMlrWSIKI/lN0Tz3uGltWAjG8DQl2Yr3pwjwOaIzGE+kozrDf+T9xItfiIIbvKajef1dg7B7XgP0zg== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-cli@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-cli/-/mjml-cli-4.13.0.tgz#fbd78ea77c63579990edd665a376e95696e2dd37" + integrity sha512-kAZxpH0QqlTF/CcLzELgKw1ljKRxrmWJ310CJQhbPAxHvwQ/nIb+q82U+zRJAelRPPKjnOb+hSrMRqTgk9rH3w== + dependencies: + "@babel/runtime" "^7.14.6" + chokidar "^3.0.0" + glob "^7.1.1" + html-minifier "^4.0.0" + js-beautify "^1.6.14" + lodash "^4.17.21" + mjml-core "4.13.0" + mjml-migrate "4.13.0" + mjml-parser-xml "4.13.0" + mjml-validator "4.13.0" + yargs "^16.1.0" + +mjml-column@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-column/-/mjml-column-4.13.0.tgz#c1ec19fe2567594d45abcea4de8a80e4badf1fb8" + integrity sha512-O8FrWKK/bCy9XpKxrKRYWNdgWNaVd4TK4RqMeVI/I70IbnYnc1uf15jnsPMxCBSbT+NyXyk8k7fn099797uwpw== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-core@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-core/-/mjml-core-4.13.0.tgz#24193b4b17d0fbb4c72f17c7317ce7b7db2a8a47" + integrity sha512-kU5AoVTlZaXR/EDi3ix66xpzUe+kScYus71lBH/wo/B+LZW70GHE1AYWtsog5oJp1MuTHpMFTNuBD/wePeEgWg== + dependencies: + "@babel/runtime" "^7.14.6" + cheerio "1.0.0-rc.10" + detect-node "2.0.4" + html-minifier "^4.0.0" + js-beautify "^1.6.14" + juice "^7.0.0" + lodash "^4.17.21" + mjml-migrate "4.13.0" + mjml-parser-xml "4.13.0" + mjml-validator "4.13.0" + +mjml-divider@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-divider/-/mjml-divider-4.13.0.tgz#6b88dd8cd97e6c424f793adc6ed123c2b3ef24b0" + integrity sha512-ooPCwfmxEC+wJduqObYezMp7W5UCHjL9Y1LPB5FGna2FrOejgfd6Ix3ij8Wrmycmlol7E2N4D7c5NDH5DbRCJg== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-group@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-group/-/mjml-group-4.13.0.tgz#bac75294bbe147ee44bfcd0788e778c5ca270bd7" + integrity sha512-U7E8m8aaoAE/dMqjqXPjjrKcwO36B4cquAy9ASldECrIZJBcpFYO6eYf5yLXrNCUM2P0id8pgVjrUq23s00L7Q== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-head-attributes@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-head-attributes/-/mjml-head-attributes-4.13.0.tgz#ad6aa1488f46872c10135b4fbd916c47e9faf19d" + integrity sha512-haggCafno+0lQylxJStkINCVCPMwfTpwE6yjCHeGOpQl/TkoNmjNkDr7DEEbNTZbt4Ekg070lQFn7clDy38EoA== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-head-breakpoint@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-head-breakpoint/-/mjml-head-breakpoint-4.13.0.tgz#45a0b499da97c9e88bedc8c2ce7f78168b699cca" + integrity sha512-D2iPDeUKQK1+rYSNa2HGOvgfPxZhNyndTG0iBEb/FxdGge2hbeDCZEN0mwDYE3wWB+qSBqlCuMI+Vr4pEjZbKg== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-head-font@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-head-font/-/mjml-head-font-4.13.0.tgz#86d72119f639ef153a10cc8f5c8c8404345e66d0" + integrity sha512-mYn8aWnbrEap5vX2b4662hkUv6WifcYzYn++Yi6OHrJQi55LpzcU+myAGpfQEXXrpU8vGwExMTFKsJq5n2Kaow== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-head-html-attributes@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-head-html-attributes/-/mjml-head-html-attributes-4.13.0.tgz#28a668c33a01513e0d4699ced700df30cce243e8" + integrity sha512-m30Oro297+18Zou/1qYjagtmCOWtYXeoS38OABQ5zOSzMItE3TcZI9JNcOueIIWIyFCETe8StrTAKcQ2GHwsDw== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-head-preview@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-head-preview/-/mjml-head-preview-4.13.0.tgz#685f8e430ae3e9883ef59a39850119c460c7f5d9" + integrity sha512-v0K/NocjFCbaoF/0IMVNmiqov91HxqT07vNTEl0Bt9lKFrTKVC01m1S4K7AB78T/bEeJ/HwmNjr1+TMtVNGGow== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-head-style@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-head-style/-/mjml-head-style-4.13.0.tgz#6c27b9f38a0a348b7951a878739abe9ffff7a0dd" + integrity sha512-tBa33GL9Atn5bAM2UwE+uxv4rI29WgX/e5lXX+5GWlsb4thmiN6rxpFTNqBqWbBNRbZk4UEZF78M7Da8xC1ZGQ== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-head-title@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-head-title/-/mjml-head-title-4.13.0.tgz#239e6abe15f1da062e4369f03039720e97cd6c09" + integrity sha512-Mq0bjuZXJlwxfVcjuYihQcigZSDTKeQaG3nORR1D0jsOH2BXU4XgUK1UOcTXn2qCBIfRoIMq7rfzYs+L0CRhdw== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-head@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-head/-/mjml-head-4.13.0.tgz#bd65594d1938f6c7b32de478b504f9a873340c83" + integrity sha512-sL2qQuoVALXBCiemu4DPo9geDr8DuUdXVJxm+4nd6k5jpLCfSDmFlNhgSsLPzsYn7VEac3/sxsjLtomQ+6/BHg== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-hero@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-hero/-/mjml-hero-4.13.0.tgz#9eec607368510a3f96c6b8d3ccfe86a12b878964" + integrity sha512-aWEOScdrhyjwdKBWG4XQaElRHP8LU5PtktkpMeBXa4yxrxNs25qRnDqMNkjSrnnmFKWZmQ166tfboY6RBNf0UA== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-image@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-image/-/mjml-image-4.13.0.tgz#4987c77cff697dac09184cf82e7741842e65d2eb" + integrity sha512-agMmm2wRZTIrKwrUnYFlnAbtrKYSP0R2en+Vf92HPspAwmaw3/AeOW/QxmSiMhfGf+xsEJyzVvR/nd33jbT3sg== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-migrate@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-migrate/-/mjml-migrate-4.13.0.tgz#f75c010fb862cf6a66713144aa8742c64101091c" + integrity sha512-I1euHiAyNpaz+B5vH+Z4T+hg/YtI5p3PqQ3/zTLv8gi24V6BILjTaftWhH5+3R/gQkQhH0NUaWNnRmds+Mq5DQ== + dependencies: + "@babel/runtime" "^7.14.6" + js-beautify "^1.6.14" + lodash "^4.17.21" + mjml-core "4.13.0" + mjml-parser-xml "4.13.0" + yargs "^16.1.0" + +mjml-navbar@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-navbar/-/mjml-navbar-4.13.0.tgz#93caf034e1e36780df5603845554a101786f5a5b" + integrity sha512-0Oqyyk+OdtXfsjswRb/7Ql1UOjN4MbqFPKoyltJqtj+11MRpF5+Wjd74Dj9H7l81GFwkIB9OaP+ZMiD+TPECgg== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-parser-xml@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-parser-xml/-/mjml-parser-xml-4.13.0.tgz#9a0d4d4c176ebfb28f31ac935a7ac7be673bc050" + integrity sha512-phljtI8DaW++q0aybR/Ykv9zCyP/jCFypxVNo26r2IQo//VYXyc7JuLZZT8N/LAI8lZcwbTVxQPBzJTmZ5IfwQ== + dependencies: + "@babel/runtime" "^7.14.6" + detect-node "2.0.4" + htmlparser2 "^4.1.0" + lodash "^4.17.15" + +mjml-preset-core@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-preset-core/-/mjml-preset-core-4.13.0.tgz#aee549b6e065d71104598d1ae836cb80367f3f37" + integrity sha512-gxzYaKkvUrHuzT1oqjEPSDtdmgEnN99Hf5f1r2CR5aMOB1x66EA3T8ATvF1o7qrBTVV4KMVlQem3IubMSYJZRw== + dependencies: + "@babel/runtime" "^7.14.6" + mjml-accordion "4.13.0" + mjml-body "4.13.0" + mjml-button "4.13.0" + mjml-carousel "4.13.0" + mjml-column "4.13.0" + mjml-divider "4.13.0" + mjml-group "4.13.0" + mjml-head "4.13.0" + mjml-head-attributes "4.13.0" + mjml-head-breakpoint "4.13.0" + mjml-head-font "4.13.0" + mjml-head-html-attributes "4.13.0" + mjml-head-preview "4.13.0" + mjml-head-style "4.13.0" + mjml-head-title "4.13.0" + mjml-hero "4.13.0" + mjml-image "4.13.0" + mjml-navbar "4.13.0" + mjml-raw "4.13.0" + mjml-section "4.13.0" + mjml-social "4.13.0" + mjml-spacer "4.13.0" + mjml-table "4.13.0" + mjml-text "4.13.0" + mjml-wrapper "4.13.0" + +mjml-raw@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-raw/-/mjml-raw-4.13.0.tgz#3c70076aa0c3ab3751622f9e89c2bea1be86828d" + integrity sha512-JbBYxwX1a/zbqnCrlDCRNqov2xqUrMCaEdTHfqE2athj479aQXvLKFM20LilTMaClp/dR0yfvFLfFVrC5ej4FQ== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-section@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-section/-/mjml-section-4.13.0.tgz#ce742b4b4d2a1921bee36dbe735e9a6471cb6dd1" + integrity sha512-BLcqlhavtRakKtzDQPLv6Ae4Jt4imYWq/P0jo+Sjk7tP4QifgVA2KEQOirPK5ZUqw/lvK7Afhcths5rXZ2ItnQ== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-social@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-social/-/mjml-social-4.13.0.tgz#dcb69cc04853e1f74bc94fbdb0634c022e4c639b" + integrity sha512-zL2a7Wwsk8OXF0Bqu+1B3La1UPwdTMcEXptO8zdh2V5LL6Xb7Gfyvx6w0CmmBtG5IjyCtqaKy5wtrcpG9Hvjfg== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-spacer@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-spacer/-/mjml-spacer-4.13.0.tgz#e462f099ae40b8de9a2a6e438309d471fe42e2f2" + integrity sha512-Acw4QJ0MJ38W4IewXuMX7hLaW1BZaln+gEEuTfrv0xwPdTxX1ILqz4r+s9mYMxYkIDLWMCjBvXyQK6aWlid13A== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-table@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-table/-/mjml-table-4.13.0.tgz#0ef5baf64fe54b1d4c36f40d1cd0965b8c0218b4" + integrity sha512-UAWPVMaGReQhf776DFdiwdcJTIHTek3zzQ1pb+E7VlypEYgIpFvdUJ39UIiiflhqtdBATmHwKBOtePwU0MzFMg== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-text@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-text/-/mjml-text-4.13.0.tgz#a942508de7e5e19e87df94c40b12a22296fc4383" + integrity sha512-uDuraaQFdu+6xfuigCimbeznnOnJfwRdcCL1lTBTusTuEvW/5Va6m2D3mnMeEpl+bp4+cxesXIz9st6A9pcg5A== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + +mjml-validator@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-validator/-/mjml-validator-4.13.0.tgz#a05bac51535cb8073a253304105ffbaf88f85d26" + integrity sha512-uURYfyQYtHJ6Qz/1A7/+E9ezfcoISoLZhYK3olsxKRViwaA2Mm8gy/J3yggZXnsUXWUns7Qymycm5LglLEIiQg== + dependencies: + "@babel/runtime" "^7.14.6" + +mjml-wrapper@4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml-wrapper/-/mjml-wrapper-4.13.0.tgz#5102145f73ac4f634bc95967959948129e656afa" + integrity sha512-p/44JvHg04rAFR7QDImg8nZucEokIjFH6KJMHxsO0frJtLZ+IuakctzlZAADHsqiR52BwocDsXSa+o9SE2l6Ng== + dependencies: + "@babel/runtime" "^7.14.6" + lodash "^4.17.21" + mjml-core "4.13.0" + mjml-section "4.13.0" + +mjml@^4.13.0: + version "4.13.0" + resolved "https://registry.yarnpkg.com/mjml/-/mjml-4.13.0.tgz#8e6a276f017cb1372c154875da8acb566cfd7ad7" + integrity sha512-OnFKESouLshz8DPFSb6M/dE8GkhiJnoy6LAam5TiLA1anAj24yQ2ZH388LtQoEkvTisqwiTmc9ejDh5ctnFaJQ== + dependencies: + "@babel/runtime" "^7.14.6" + mjml-cli "4.13.0" + mjml-core "4.13.0" + mjml-migrate "4.13.0" + mjml-preset-core "4.13.0" + mjml-validator "4.13.0" + mkdirp-classic@^0.5.2: version "0.5.3" resolved "https://registry.yarnpkg.com/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz#fa10c9115cc6d8865be221ba47ee9bed78601113" @@ -2571,6 +3465,11 @@ mkdirp@^0.5.1: dependencies: minimist "^1.2.5" +mkdirp@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" + integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== + module-alias@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" @@ -2591,6 +3490,11 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +nanoid@^3.3.4: + version "3.3.4" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.4.tgz#730b67e3cd09e2deacf03c027c81c9d9dbc5e8ab" + integrity sha512-MqBkQh/OHTS2egovRtLk45wEyNXwF+cokD+1YPf9u5VfJiRdAiRwB2froX5Co9Rh20xs4siNPm8naNotSD6RBw== + negotiator@0.6.3: version "0.6.3" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" @@ -2606,6 +3510,13 @@ netmask@^1.0.6: resolved "https://registry.yarnpkg.com/netmask/-/netmask-1.0.6.tgz#20297e89d86f6f6400f250d9f4f6b4c1945fcd35" integrity sha1-ICl+idhvb2QA8lDZ9Pa0wZRfzTU= +no-case@^2.2.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-2.3.2.tgz#60b813396be39b3f1288a4c1ed5d1e7d28b464ac" + integrity sha512-rmTZ9kz+f3rCvK2TD1Ue/oZlns7OGoIWP4fc3llxxRXlOkHKoWPPWJOfFYpITabSow43QJbRIoHQXtt10VldyQ== + dependencies: + lower-case "^1.1.1" + node-domexception@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" @@ -2637,6 +3548,33 @@ node-forge@^0.7.6: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.7.6.tgz#fdf3b418aee1f94f0ef642cd63486c77ca9724ac" integrity sha512-sol30LUpz1jQFBjOKwbjxijiE3b6pjd74YwfD0fJOKPjF+fONKb2Yg8rYgS6+bK6VDl+/wfr4IYpC7jDzLUIfw== +noms@0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859" + integrity sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow== + dependencies: + inherits "^2.0.1" + readable-stream "~1.0.31" + +nopt@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-6.0.0.tgz#245801d8ebf409c6df22ab9d95b65e1309cdb16d" + integrity sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g== + dependencies: + abbrev "^1.0.0" + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +nth-check@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-2.1.1.tgz#c9eab428effce36cd6b92c924bdb000ef1f1ed1d" + integrity sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w== + dependencies: + boolbase "^1.0.0" + oauth-sign@~0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455" @@ -2795,6 +3733,40 @@ pac-resolver@^3.0.0: netmask "^1.0.6" thunkify "^2.1.2" +param-case@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247" + integrity sha512-eQE845L6ot89sk2N8liD8HAuH4ca6Vvr7VWAWwt7+kvvG5aBcPmmphQ68JsEG2qa9n1TykS2DLeMt363AAH8/w== + dependencies: + no-case "^2.2.0" + +parse5-htmlparser2-tree-adapter@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz#2cdf9ad823321140370d4dbf5d3e92c7c8ddc6e6" + integrity sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA== + dependencies: + parse5 "^6.0.1" + +parse5-htmlparser2-tree-adapter@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.0.0.tgz#23c2cc233bcf09bb7beba8b8a69d46b08c62c2f1" + integrity sha512-B77tOZrqqfUfnVcOrUvfdLbz4pu4RopLD/4vmu3HUPswwTA8OH0EMW9BlWR2B0RCoiZRAHEUu7IxeP1Pd1UU+g== + dependencies: + domhandler "^5.0.2" + parse5 "^7.0.0" + +parse5@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" + integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== + +parse5@^7.0.0: + version "7.1.2" + resolved "https://registry.yarnpkg.com/parse5/-/parse5-7.1.2.tgz#0736bebbfd77793823240a23b7fc5e010b7f8e32" + integrity sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw== + dependencies: + entities "^4.4.0" + parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" @@ -2837,6 +3809,16 @@ performance-now@^2.1.0: resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-2.1.0.tgz#6309f4e0e5fa913ec1c69307ae364b4b377c9e7b" integrity sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow== +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + pify@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/pify/-/pify-2.3.0.tgz#ed141a6ac043a849ea588498e7dca8b15330e90c" @@ -2863,6 +3845,15 @@ png-to-jpeg@^1.0.1: pify "^2.3.0" png-js "^0.1.1" +postcss@^8.1.10: + version "8.4.20" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.4.20.tgz#64c52f509644cecad8567e949f4081d98349dc56" + integrity sha512-6Q04AXR1212bXr5fh03u8aAwbLxAQNGQ/Q1LNa0VfOI06ZAlhPHtQvE4OIdpj4kLThXilalPnmDSOD65DcHt+g== + dependencies: + nanoid "^3.3.4" + picocolors "^1.0.0" + source-map-js "^1.0.2" + prelude-ls@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" @@ -2885,6 +3876,11 @@ promisify-call@^2.0.2: dependencies: with-callback "^1.0.2" +proto-list@~1.2.1: + version "1.2.4" + resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" + integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== + protobufjs@^6.8.6, protobufjs@^6.8.9: version "6.11.2" resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.2.tgz#de39fabd4ed32beaa08e9bb1e30d08544c1edf8b" @@ -2931,6 +3927,11 @@ proxy-from-env@^1.0.0, proxy-from-env@^1.1.0: resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ== + psl@^1.1.28: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -3011,7 +4012,7 @@ readable-stream@1.1.x: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@2, readable-stream@^2.0.0: +readable-stream@2, readable-stream@^2.0.0, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -3033,11 +4034,33 @@ readable-stream@2, readable-stream@^2.0.0: string_decoder "^1.1.1" util-deprecate "^1.0.1" +readable-stream@~1.0.31: + version "1.0.34" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" + integrity sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg== + dependencies: + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "0.0.1" + string_decoder "~0.10.x" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + reflect-metadata@^0.1.13: version "0.1.13" resolved "https://registry.yarnpkg.com/reflect-metadata/-/reflect-metadata-0.1.13.tgz#67ae3ca57c972a2aa1642b10fe363fe32d49dc08" integrity sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg== +regenerator-runtime@^0.13.11: + version "0.13.11" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" + integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== + regexp.prototype.flags@^1.3.0: version "1.4.1" resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.4.1.tgz#b3f4c0059af9e47eca9f3f660e51d81307e72307" @@ -3046,6 +4069,11 @@ regexp.prototype.flags@^1.3.0: call-bind "^1.0.2" define-properties "^1.1.3" +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -3210,11 +4238,21 @@ side-channel@^1.0.3, side-channel@^1.0.4: get-intrinsic "^1.0.2" object-inspect "^1.9.0" +sigmund@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + integrity sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g== + signal-exit@^3.0.2: version "3.0.6" resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== +slick@^1.12.2: + version "1.12.2" + resolved "https://registry.yarnpkg.com/slick/-/slick-1.12.2.tgz#bd048ddb74de7d1ca6915faa4a57570b3550c2d7" + integrity sha512-4qdtOGcBjral6YIBCWJ0ljFSKNLz9KkhbWtuGvUyRowl1kxfuE1x/Z/aJcaiilpb3do9bl5K7/1h9XC5wWpY/A== + smart-buffer@^4.1.0: version "4.2.0" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.2.0.tgz#6e1d71fa4f18c05f7d0ff216dd16a481d0e8d9ae" @@ -3249,11 +4287,21 @@ socks@~2.3.2: ip "1.1.5" smart-buffer "^4.1.0" -source-map@^0.6.1, source-map@~0.6.1: +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +sourcemap-codec@^1.4.8: + version "1.4.8" + resolved "https://registry.yarnpkg.com/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz#ea804bd94857402e6992d05a38ef1ae35a9ab4c4" + integrity sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA== + sprintf-js@~1.0.2: version "1.0.3" resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" @@ -3416,6 +4464,14 @@ telegraf@^4.8.6: sandwich-stream "^2.0.2" typegram "^3.9.0" +through2@^2.0.1: + version "2.0.5" + resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" + integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== + dependencies: + readable-stream "~2.3.6" + xtend "~4.0.1" + through2@^3.0.0: version "3.0.2" resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.2.tgz#99f88931cfc761ec7678b41d5d7336b5b6a07bf4" @@ -3444,6 +4500,13 @@ to-no-case@^1.0.0: resolved "https://registry.yarnpkg.com/to-no-case/-/to-no-case-1.0.2.tgz#c722907164ef6b178132c8e69930212d1b4aa16a" integrity sha512-Z3g735FxuZY8rodxV4gH7LxClE4H0hTIyHNIHdk+vpQxjLm0cwnKXq/OFVZ76SOQmto7txVcwSCwkU5kqp+FKg== +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-snake-case@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/to-snake-case/-/to-snake-case-1.0.0.tgz#ce746913897946019a87e62edfaeaea4c608ab8c" @@ -3496,6 +4559,11 @@ tslib@^2.0.1: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.3.1.tgz#e8a335add5ceae51aa261d32a490158ef042ef01" integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw== +tslib@^2.2.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" + integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== + tslint@^5.12.0: version "5.20.1" resolved "https://registry.yarnpkg.com/tslint/-/tslint-5.20.1.tgz#e401e8aeda0152bc44dd07e614034f3f80c67b7d" @@ -3586,6 +4654,11 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.15.0.tgz#2d6a689d94783cab43975721977a13c2afec28f1" integrity sha512-x+xdeDWq7FiORDvyIJ0q/waWd4PhjBNOm5dQUOq2AKC0IEjxOS66Ha9tctiVDGcRQuh69K7fgU5oRuTK4cysSg== +uglify-js@^3.5.1: + version "3.17.4" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.17.4.tgz#61678cf5fa3f5b7eb789bb345df29afb8257c22c" + integrity sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g== + unbox-primitive@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" @@ -3616,6 +4689,16 @@ unpipe@1.0.0, unpipe@~1.0.0: resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" integrity sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw= +untildify@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/untildify/-/untildify-4.0.0.tgz#2bc947b953652487e4600949fb091e3ae8cd919b" + integrity sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw== + +upper-case@^1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/upper-case/-/upper-case-1.1.3.tgz#f6b4501c2ec4cdd26ba78be7222961de77621598" + integrity sha512-WRbjgmYzgXkCV7zNVpy5YgrHgbBv126rMALQQMrmzOVC4GM2waQ9x7xtm8VU+1yF2kWyPzI9zbZ48n4vSxwfSA== + uri-js@^4.2.2: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" @@ -3656,6 +4739,11 @@ uuid@^7.0.0: resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b" integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg== +valid-data-url@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/valid-data-url/-/valid-data-url-3.0.1.tgz#826c1744e71b5632e847dd15dbd45b9fb38aa34f" + integrity sha512-jOWVmzVceKlVVdwjNSenT4PbGghU0SBIizAev8ofZVgivk/TVHXSbNL8LP6M3spZvkR9/QolkyJavGSX5Cs0UA== + vary@^1, vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" @@ -3670,11 +4758,34 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" +vue@^3.2.45: + version "3.2.45" + resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.45.tgz#94a116784447eb7dbd892167784619fef379b3c8" + integrity sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA== + dependencies: + "@vue/compiler-dom" "3.2.45" + "@vue/compiler-sfc" "3.2.45" + "@vue/runtime-dom" "3.2.45" + "@vue/server-renderer" "3.2.45" + "@vue/shared" "3.2.45" + walkdir@^0.4.0: version "0.4.1" resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.4.1.tgz#dc119f83f4421df52e3061e514228a2db20afa39" integrity sha512-3eBwRyEln6E1MSzcxcVpQIhRG8Q1jLvEqRmCZqS3dsfXEDR/AhOF4d+jHg1qvDCpYaVRZjENPQyrVxAkQqxPgQ== +web-resource-inliner@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/web-resource-inliner/-/web-resource-inliner-5.0.0.tgz#ac30db8096931f20a7c1b3ade54ff444e2e20f7b" + integrity sha512-AIihwH+ZmdHfkJm7BjSXiEClVt4zUFqX4YlFAzjL13wLtDuUneSaFvDBTbdYRecs35SiU7iNKbMnN+++wVfb6A== + dependencies: + ansi-colors "^4.1.1" + escape-goat "^3.0.0" + htmlparser2 "^4.0.0" + mime "^2.4.6" + node-fetch "^2.6.0" + valid-data-url "^3.0.0" + web-streams-polyfill@^3.0.3: version "3.2.1" resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz#71c2718c52b45fd49dbeee88634b3a60ceab42a6" @@ -3801,21 +4912,49 @@ xregexp@2.0.0: resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-2.0.0.tgz#52a63e56ca0b84a7f3a5f3d61872f126ad7a5943" integrity sha1-UqY+VsoLhKfzpfPWGHLxJq16WUM= +xtend@~4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" + integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== + y18n@^5.0.5: version "5.0.8" resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A== + yallist@^3.0.2: version "3.1.1" resolved "https://registry.yarnpkg.com/yallist/-/yallist-3.1.1.tgz#dbb7daf9bfd8bac9ab45ebf602b8cbad0d5d08fd" integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g== +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + yargs-parser@^21.0.0: version "21.0.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-21.0.0.tgz#a485d3966be4317426dd56bdb6a30131b281dc55" integrity sha512-z9kApYUOCwoeZ78rfRYYWdiU/iNL6mwwYlkkZfJoyMR1xps+NEBX5X7XmRpxkZHhXJ6+Ey00IwKxBBSW9FIjyA== +yargs@^16.1.0: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + yargs@^17.3.1: version "17.3.1" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" From 8d64a6cf6cbdae5b3b58400003c38ef690153f97 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Sat, 14 Jan 2023 12:53:11 +0600 Subject: [PATCH 02/20] render email in ts. --- services/firebase/emails/weekly.html | 573 ++++++++++++++++++ services/firebase/package.json | 4 +- services/firebase/src/cli.ts | 5 +- services/firebase/src/index.ts | 6 +- services/firebase/src/lib/digest.ts | 133 ++++ .../firebase/{src => }/templates/weekly.mjml | 0 services/firebase/tsconfig.json | 3 +- services/firebase/yarn.lock | 21 +- 8 files changed, 737 insertions(+), 8 deletions(-) create mode 100644 services/firebase/emails/weekly.html create mode 100644 services/firebase/src/lib/digest.ts rename services/firebase/{src => }/templates/weekly.mjml (100%) diff --git a/services/firebase/emails/weekly.html b/services/firebase/emails/weekly.html new file mode 100644 index 00000000..98b2ea9c --- /dev/null +++ b/services/firebase/emails/weekly.html @@ -0,0 +1,573 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + +
+ + + +
+ +
+ + +
+ +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + +
+ + + + + + + +
+ + + + + + + +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + + + + +
+ + + +
+ + + + + + + +
+ + +
+ + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ +
Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.
+ +
+ +
Munich Dance Calendar
+ +
+ + + +
Monday • 11 Nov
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
+ +
+ + + +
Tuesday • 12 Nov
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
+ +
+ + + + + + + +
+ + Show More + +
+ +
+ +
Something is missing? Add event.
+ +
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + + + + + + + + + +
+ +
Follow us
+ +
+ +
We regularly post event announcements and introduce new members on our social media.
+ +
+ + + + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + + + + + + + + +
+ + + + + + +
+ + + +
+
+ + + +
+ +
+ + +
+ +
+ + + +
+ +
+ + + + + +
+ + + + + + + +
+ + +
+ + + + + + + + + +
+ +
You are subscribed to Munich Dance Calendar. Visit the page to Unsubscribe.
+ +
+ +
+ + +
+ +
+ + + + + +
+ + + + \ No newline at end of file diff --git a/services/firebase/package.json b/services/firebase/package.json index e7d54fb0..2fa5de3c 100644 --- a/services/firebase/package.json +++ b/services/firebase/package.json @@ -48,7 +48,9 @@ }, "devDependencies": { "@types/dotenv": "^8.2.0", - "@types/puppeteer": "^5.4.2", + "@types/mjml": "^4.7.0", + "@types/puppeteer": "^5.4.2", + "@types/vue": "^2.0.0", "copyfiles": "^2.4.1", "firebase-functions-test": "^0.1.6", "rimraf": "^3.0.2", diff --git a/services/firebase/src/cli.ts b/services/firebase/src/cli.ts index c0e44008..17816fec 100644 --- a/services/firebase/src/cli.ts +++ b/services/firebase/src/cli.ts @@ -15,7 +15,8 @@ const { hideBin } = require('yargs/helpers') import { firestore } from './firebase' import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' -const { getWeeklyData, renderEmail } = require('./lib/digest.js') +import { getWeeklyData, renderEmail } from "./lib/digest"; +import * as fs from "fs"; yargs(hideBin(process.argv)) .command( @@ -181,7 +182,7 @@ yargs(hideBin(process.argv)) async (argv: any) => { const data = await getWeeklyData('Munich') const html = await renderEmail('weekly', data) - console.log(html) + fs.writeFileSync("./emails/weekly.html", html); } ) .command( diff --git a/services/firebase/src/index.ts b/services/firebase/src/index.ts index 2cf3f2db..276b0251 100644 --- a/services/firebase/src/index.ts +++ b/services/firebase/src/index.ts @@ -113,7 +113,7 @@ app.post('/track/:action', async (req, res) => { }) }) -app.get('/share/*', async (req, res) => { +app.get('/share/*', async (req:any, res:any) => { const path = req.params[0] const timezone = req.query.timezone as string @@ -142,7 +142,7 @@ app.get('/share/*', async (req, res) => { } catch (e) { return res.json({ success: false, - error: e.message, + error: (e as Error).message, }) } }) @@ -275,7 +275,7 @@ export const profileCreated = functions } catch (e) { await snapshot.ref.update({ import: 'failed', - importError: e.message, + importError: (e as Error).message, }) return diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts new file mode 100644 index 00000000..521d0cab --- /dev/null +++ b/services/firebase/src/lib/digest.ts @@ -0,0 +1,133 @@ +const { createSSRApp } = require('vue'); +const { renderToString } = require('vue/server-renderer'); +import mjml2html = require("mjml") +import * as fs from "fs"; + + +export async function renderEmail(type: string, data:any, customUtms = {}) { + const template = fs.readFileSync(`./templates/${type}.mjml`, "utf8"); + + const defaultUtms = { + campaign: type, + medium: "email", + source: "newsletter", + }; + + const utm = { + ...defaultUtms, + ...customUtms, + }; + + const app = createSSRApp({ + data: () => { + return data; + }, + template, + methods: { + link(url:string, utmContent = "") { + return ( + url + + "?utm_campaign=" + + utm.campaign + + "&utm_medium=" + + utm.medium + + "&utm_source=" + + utm.source + + "&utm_content=" + + utmContent + ); + }, + }, + }); + + app.config.compilerOptions.isCustomElement = (tag:any) => tag.startsWith("mj"); + + return mjml2html(await renderToString(app)).html; +} + + +export async function getWeeklyData(city:string) { + const data = { + intro: + "Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.", + title: "Munich Dance Calendar", + links: { + telegram: "https://t.me/WeDanceMunich", + instagram: "https://instagram.com/WeDanceMunich", + facebook: "https://fb.com/WeDanceMunich", + addEvent: "https://wedance.vip/events/-/edit", + city: "https://wedance.vip/Munich", + }, + days: [ + { + day: "Monday", + date: "11 Nov", + events: [ + { + title: "Kizomba Party", + organizer: "circulo", + venue: "Circulo Tanzschule", + format: "Party", + time: "21:30", + link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", + cover: + "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", + styles: ["Bachata", "Kizomba"], + }, + { + title: "Bachata Party", + organizer: "circulo", + venue: "Circulo Tanzschule", + format: "Party", + time: "21:30", + link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", + cover: + "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", + styles: ["Bachata", "Kizomba"], + }, + ], + }, + { + day: "Tuesday", + date: "12 Nov", + events: [ + { + title: "Kizomba Party", + organizer: "circulo", + venue: "Circulo Tanzschule", + format: "Party", + time: "21:30", + link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", + cover: + "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", + styles: ["Bachata", "Kizomba"], + }, + { + title: "Bachata Party", + organizer: "circulo", + venue: "Circulo Tanzschule", + format: "Party", + time: "21:30", + link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", + cover: + "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", + styles: ["Bachata", "Kizomba"], + }, + { + title: "Salsa Party", + organizer: "circulo", + venue: "Circulo Tanzschule", + format: "Party", + time: "21:30", + link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", + cover: + "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", + styles: ["Bachata", "Kizomba"], + }, + ], + }, + ], + }; + + return data; +} diff --git a/services/firebase/src/templates/weekly.mjml b/services/firebase/templates/weekly.mjml similarity index 100% rename from services/firebase/src/templates/weekly.mjml rename to services/firebase/templates/weekly.mjml diff --git a/services/firebase/tsconfig.json b/services/firebase/tsconfig.json index e4ae30fa..fa53cc90 100644 --- a/services/firebase/tsconfig.json +++ b/services/firebase/tsconfig.json @@ -7,7 +7,8 @@ "sourceMap": true, "strict": true, "target": "es2017", - "skipLibCheck": true + "skipLibCheck": true, + //"esModuleInterop":true }, "compileOnSave": true, "exclude": ["dist"], diff --git a/services/firebase/yarn.lock b/services/firebase/yarn.lock index aeb5605f..6b90b03d 100644 --- a/services/firebase/yarn.lock +++ b/services/firebase/yarn.lock @@ -587,6 +587,18 @@ resolved "https://registry.yarnpkg.com/@types/mime/-/mime-1.3.2.tgz#93e25bf9ee75fe0fd80b594bc4feb0e862111b5a" integrity sha512-YATxVxgRqNH6nHEIsvg6k2Boc1JHI9ZbH5iWFFv/MTkchz3b1ieGDa5T0a9RznNdI0KhVbdbWSN+KWWrQZRxTw== +"@types/mjml-core@*": + version "4.7.1" + resolved "https://registry.yarnpkg.com/@types/mjml-core/-/mjml-core-4.7.1.tgz#c2627499045b54eccfca38e2b532566fb0689189" + integrity sha512-k5IRafi93tyZBGF+0BTrcBDvG47OueI+Q7TC4V4UjGQn0AMVvL3Y+S26QF/UHMmMJW5r1hxLyv3StX2/+FatFg== + +"@types/mjml@^4.7.0": + version "4.7.0" + resolved "https://registry.yarnpkg.com/@types/mjml/-/mjml-4.7.0.tgz#ea31b58008f54119efda9e673af674757d35981b" + integrity sha512-aWWu8Lxq2SexXGs+lBPRUpN3kFf0sDRo3Y4jz7BQ15cQvMfyZOadgFJsNlHmDqI6D2Qjx0PIK+1f9IMXgq9vTA== + dependencies: + "@types/mjml-core" "*" + "@types/node@*", "@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@>=8.9.0": version "17.0.14" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.14.tgz#33b9b94f789a8fedd30a68efdbca4dbb06b61f20" @@ -655,6 +667,13 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== +"@types/vue@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@types/vue/-/vue-2.0.0.tgz#ec77b3d89591deb9ca5cb052368aa9c32be088e7" + integrity sha512-WDElkBv/o4lVwu6wYHB06AXs4Xo2fwDjJUpvPRc1QQdzkUSiGFjrYuSCy8raxLE5FObgKq8ND7R5gSZTFLK60w== + dependencies: + vue "*" + "@types/ws@^7.2.5": version "7.4.7" resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" @@ -4758,7 +4777,7 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vue@^3.2.45: +vue@*, vue@^3.2.45: version "3.2.45" resolved "https://registry.yarnpkg.com/vue/-/vue-3.2.45.tgz#94a116784447eb7dbd892167784619fef379b3c8" integrity sha512-9Nx/Mg2b2xWlXykmCwiTUCWHbWIj53bnkizBxKai1g61f2Xit700A1ljowpTIM11e3uipOeiPcSqnmBg6gyiaA== From 3a34a49ddb0076bba37351aa10fd0ecdf7efd547 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Wed, 1 Feb 2023 13:57:20 +0600 Subject: [PATCH 03/20] add firebase to get data. --- .env.example | 1 + .firebaserc | 5 +- .gitignore | 1 + firestore.indexes.json | 4 ++ firestore.rules | 9 +++ services/firebase/.gitignore | 2 + services/firebase/emails/weekly.html | 16 +---- services/firebase/package.json | 2 +- services/firebase/src/cli.ts | 2 +- services/firebase/src/firebase.ts | 5 +- services/firebase/src/lib/digest.ts | 100 +++++---------------------- services/firebase/tsconfig.json | 2 +- 12 files changed, 44 insertions(+), 105 deletions(-) create mode 100644 firestore.indexes.json create mode 100644 firestore.rules diff --git a/.env.example b/.env.example index 50406801..f447535d 100644 --- a/.env.example +++ b/.env.example @@ -7,3 +7,4 @@ FIREBASE_CONFIG= # Sentry configuration - https://docs.sentry.io/platforms/node/configuration/options/#dsn SENTRY_DSN= + diff --git a/.firebaserc b/.firebaserc index 006432f6..a322b973 100644 --- a/.firebaserc +++ b/.firebaserc @@ -1,7 +1,8 @@ { "projects": { - "live": "wedance-4abe3" + "live": "wedance-4abe3", + "we-dance-dev": "wedancedev-aa00e", }, "targets": {}, "etags": {} -} \ No newline at end of file +} diff --git a/.gitignore b/.gitignore index 9df9fa2e..929c3b7d 100644 --- a/.gitignore +++ b/.gitignore @@ -96,3 +96,4 @@ sw.* # Firebase .vscode + diff --git a/firestore.indexes.json b/firestore.indexes.json new file mode 100644 index 00000000..415027e5 --- /dev/null +++ b/firestore.indexes.json @@ -0,0 +1,4 @@ +{ + "indexes": [], + "fieldOverrides": [] +} diff --git a/firestore.rules b/firestore.rules new file mode 100644 index 00000000..a38f1aa2 --- /dev/null +++ b/firestore.rules @@ -0,0 +1,9 @@ +rules_version = '2'; +service cloud.firestore { + match /databases/{database}/documents { + match /{document=**} { + allow read, write: if + request.time < timestamp.date(2022, 11, 18); + } + } +} \ No newline at end of file diff --git a/services/firebase/.gitignore b/services/firebase/.gitignore index fb3f2b34..ad64b03b 100644 --- a/services/firebase/.gitignore +++ b/services/firebase/.gitignore @@ -7,3 +7,5 @@ typings/ node_modules/ var/ + +serviceAccountKey.json \ No newline at end of file diff --git a/services/firebase/emails/weekly.html b/services/firebase/emails/weekly.html index 98b2ea9c..166dcc4d 100644 --- a/services/firebase/emails/weekly.html +++ b/services/firebase/emails/weekly.html @@ -260,21 +260,7 @@ - -
Monday • 11 Nov
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
- - - - - - - - - +
Tuesday • 12 Nov
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
Tueday
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
diff --git a/services/firebase/package.json b/services/firebase/package.json index 2fa5de3c..071e71c9 100644 --- a/services/firebase/package.json +++ b/services/firebase/package.json @@ -49,7 +49,7 @@ "devDependencies": { "@types/dotenv": "^8.2.0", "@types/mjml": "^4.7.0", - "@types/puppeteer": "^5.4.2", + "@types/puppeteer": "^5.4.2", "@types/vue": "^2.0.0", "copyfiles": "^2.4.1", "firebase-functions-test": "^0.1.6", diff --git a/services/firebase/src/cli.ts b/services/firebase/src/cli.ts index 17816fec..57ab5868 100644 --- a/services/firebase/src/cli.ts +++ b/services/firebase/src/cli.ts @@ -180,7 +180,7 @@ yargs(hideBin(process.argv)) 'Generate Weekly Newsletter', () => undefined, async (argv: any) => { - const data = await getWeeklyData('Munich') + const data = await getWeeklyData('Munich'); const html = await renderEmail('weekly', data) fs.writeFileSync("./emails/weekly.html", html); } diff --git a/services/firebase/src/firebase.ts b/services/firebase/src/firebase.ts index 02b6f6ee..59594613 100644 --- a/services/firebase/src/firebase.ts +++ b/services/firebase/src/firebase.ts @@ -1,6 +1,9 @@ import * as admin from 'firebase-admin' +const serviceAccount = require('../serviceAccountKey.json'); -admin.initializeApp() +admin.initializeApp({ + credential:admin.credential.cert(serviceAccount) +}); const firestore = admin.firestore() diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 521d0cab..08b586e3 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -2,6 +2,7 @@ const { createSSRApp } = require('vue'); const { renderToString } = require('vue/server-renderer'); import mjml2html = require("mjml") import * as fs from "fs"; +import { firestore } from "../firebase"; export async function renderEmail(type: string, data:any, customUtms = {}) { @@ -42,92 +43,23 @@ export async function renderEmail(type: string, data:any, customUtms = {}) { app.config.compilerOptions.isCustomElement = (tag:any) => tag.startsWith("mj"); - return mjml2html(await renderToString(app)).html; + return mjml2html(await renderToString(app)).html; } - export async function getWeeklyData(city:string) { - const data = { - intro: - "Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.", - title: "Munich Dance Calendar", - links: { - telegram: "https://t.me/WeDanceMunich", - instagram: "https://instagram.com/WeDanceMunich", - facebook: "https://fb.com/WeDanceMunich", - addEvent: "https://wedance.vip/events/-/edit", - city: "https://wedance.vip/Munich", - }, - days: [ - { - day: "Monday", - date: "11 Nov", - events: [ - { - title: "Kizomba Party", - organizer: "circulo", - venue: "Circulo Tanzschule", - format: "Party", - time: "21:30", - link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", - cover: - "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", - styles: ["Bachata", "Kizomba"], - }, - { - title: "Bachata Party", - organizer: "circulo", - venue: "Circulo Tanzschule", - format: "Party", - time: "21:30", - link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", - cover: - "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", - styles: ["Bachata", "Kizomba"], - }, - ], - }, - { - day: "Tuesday", - date: "12 Nov", - events: [ - { - title: "Kizomba Party", - organizer: "circulo", - venue: "Circulo Tanzschule", - format: "Party", - time: "21:30", - link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", - cover: - "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", - styles: ["Bachata", "Kizomba"], - }, - { - title: "Bachata Party", - organizer: "circulo", - venue: "Circulo Tanzschule", - format: "Party", - time: "21:30", - link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", - cover: - "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", - styles: ["Bachata", "Kizomba"], - }, - { - title: "Salsa Party", - organizer: "circulo", - venue: "Circulo Tanzschule", - format: "Party", - time: "21:30", - link: "https://wedance.vip/events/C7YH4kKw5xz9QEARnSxI", - cover: - "https://firebasestorage.googleapis.com/v0/b/wedance-4abe3.appspot.com/o/media%2FtvR012ArEpQhCJdPHh6G7sLuqoO2%2Fc4439150-5e5c-4bbb-9f96-1ac1910f80bf?alt=media&token=64366542-00c1-43b8-a3cb-5f72f47a7f4c", - styles: ["Bachata", "Kizomba"], - }, - ], - }, - ], - }; - return data; + const eventDocs = ( + await firestore + .collection('events') + .get() + ).docs + + let data; + for (const doc of eventDocs) { + data = { + id: doc.id, + ...doc.data(), + }; + } + return data; } diff --git a/services/firebase/tsconfig.json b/services/firebase/tsconfig.json index fa53cc90..cc6f27ff 100644 --- a/services/firebase/tsconfig.json +++ b/services/firebase/tsconfig.json @@ -8,7 +8,7 @@ "strict": true, "target": "es2017", "skipLibCheck": true, - //"esModuleInterop":true + "resolveJsonModule": true, }, "compileOnSave": true, "exclude": ["dist"], From 9c9a9614a7b1278f2e85947c9ce0c8892bc5c783 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Tue, 7 Feb 2023 16:59:13 +0600 Subject: [PATCH 04/20] get events from firebase. --- .firebaserc | 4 +-- firestore.indexes.json | 4 --- firestore.rules | 9 ------ services/firebase/emails/weekly.html | 44 +++++++++++++++++++++++++++- services/firebase/src/firebase.ts | 3 +- services/firebase/src/lib/digest.ts | 42 ++++++++++++++++++++++---- 6 files changed, 82 insertions(+), 24 deletions(-) delete mode 100644 firestore.indexes.json delete mode 100644 firestore.rules diff --git a/.firebaserc b/.firebaserc index a322b973..b1bd369c 100644 --- a/.firebaserc +++ b/.firebaserc @@ -1,8 +1,8 @@ { "projects": { "live": "wedance-4abe3", - "we-dance-dev": "wedancedev-aa00e", + "dev": "wedancedev-aa00e" }, "targets": {}, "etags": {} -} +} \ No newline at end of file diff --git a/firestore.indexes.json b/firestore.indexes.json deleted file mode 100644 index 415027e5..00000000 --- a/firestore.indexes.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "indexes": [], - "fieldOverrides": [] -} diff --git a/firestore.rules b/firestore.rules deleted file mode 100644 index a38f1aa2..00000000 --- a/firestore.rules +++ /dev/null @@ -1,9 +0,0 @@ -rules_version = '2'; -service cloud.firestore { - match /databases/{database}/documents { - match /{document=**} { - allow read, write: if - request.time < timestamp.date(2022, 11, 18); - } - } -} \ No newline at end of file diff --git a/services/firebase/emails/weekly.html b/services/firebase/emails/weekly.html index 166dcc4d..c2acb6da 100644 --- a/services/firebase/emails/weekly.html +++ b/services/firebase/emails/weekly.html @@ -260,7 +260,49 @@ - + +
Tueday
21:30
circulo • Circulo Tanzschule
Party • Bachata • Kizomba
+ + + + + + + + + +
+ + + + + + + + + +
+ + + + + + + + +
diff --git a/services/firebase/src/firebase.ts b/services/firebase/src/firebase.ts index 59594613..924abc22 100644 --- a/services/firebase/src/firebase.ts +++ b/services/firebase/src/firebase.ts @@ -1,8 +1,7 @@ import * as admin from 'firebase-admin' -const serviceAccount = require('../serviceAccountKey.json'); admin.initializeApp({ - credential:admin.credential.cert(serviceAccount) + credential: admin.credential.applicationDefault() }); const firestore = admin.firestore() diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 08b586e3..9ea40fbf 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -48,18 +48,48 @@ export async function renderEmail(type: string, data:any, customUtms = {}) { export async function getWeeklyData(city:string) { + const today = new Date().toISOString().slice(0, 10) + const eventDocs = ( await firestore - .collection('events') + .collection('posts') + .where('startDate', '>', today) .get() ).docs - - let data; + + const data = [] + for (const doc of eventDocs) { - data = { + const event = { id: doc.id, ...doc.data(), - }; + } as any + + data.push(event) } - return data; + + const events: any = { + intro:'Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.', + title:'Munich Dance Calendar', + links: { + telegram: "https://t.me/WeDanceMunich", + instagram: "https://instagram.com/WeDanceMunich", + facebook: "https://fb.com/WeDanceMunich", + addEvent: "https://wedance.vip/events/-/edit", + city: "https://wedance.vip/Munich", + }, + days: data.map(event=>({ + title:event.name, + organizer:event.org.name, + venue:event.venue.name, + format:event.eventType, + time:event.startDate, + link:event.link, + cover:event.cover, + styles:Object.keys(event.styles) + })) + } + + return events; + } From fd68f7b00618d4a72fe1367afe4949dc608f5097 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Wed, 8 Feb 2023 16:11:25 +0600 Subject: [PATCH 05/20] get events between seven days of a specific place --- services/firebase/emails/weekly.html | 28 ------------------------ services/firebase/src/lib/digest.ts | 32 +++++++++++++++++++++++----- 2 files changed, 27 insertions(+), 33 deletions(-) diff --git a/services/firebase/emails/weekly.html b/services/firebase/emails/weekly.html index c2acb6da..92cab991 100644 --- a/services/firebase/emails/weekly.html +++ b/services/firebase/emails/weekly.html @@ -271,34 +271,6 @@ align="left" style="font-size:0px;padding:10px 25px;padding-top:0;word-break:break-word;" > - - -
- - - - - - - - - -
- - - - - - - diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 9ea40fbf..78824141 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -48,15 +48,38 @@ export async function renderEmail(type: string, data:any, customUtms = {}) { export async function getWeeklyData(city:string) { - const today = new Date().toISOString().slice(0, 10) + const today = new Date().toISOString().slice(0,10) + const then = new Date(); + then.setDate(then.getDate() + 7); + const sevenDaysFromNow = then.toISOString().slice(0,10); + const cityDocs = (await firestore + .collection('profiles') + .where('username', '==', city) + .get() +).docs + + const cityData: any = []; + + for(const doc of cityDocs ) { + const city = { + id: doc.id, + ...doc.data() + } as any + cityData.push(city) + } + + const eventDocs = ( await firestore - .collection('posts') + .collection('posts') .where('startDate', '>', today) + .where('startDate','<', sevenDaysFromNow) + .where('place','==',cityData[0].place) .get() ).docs + const data = [] for (const doc of eventDocs) { @@ -67,7 +90,7 @@ export async function getWeeklyData(city:string) { data.push(event) } - + const events: any = { intro:'Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.', title:'Munich Dance Calendar', @@ -81,7 +104,7 @@ export async function getWeeklyData(city:string) { days: data.map(event=>({ title:event.name, organizer:event.org.name, - venue:event.venue.name, + venue:event.venue?.name, format:event.eventType, time:event.startDate, link:event.link, @@ -91,5 +114,4 @@ export async function getWeeklyData(city:string) { } return events; - } From c5eb3676d6e1b521ddfce61354da94468ed92473 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Wed, 8 Feb 2023 18:38:27 +0600 Subject: [PATCH 06/20] get profile data. --- services/firebase/emails/weekly.html | 12 ++++---- services/firebase/src/lib/digest.ts | 43 ++++++++++++++++++---------- 2 files changed, 34 insertions(+), 21 deletions(-) diff --git a/services/firebase/emails/weekly.html b/services/firebase/emails/weekly.html index 92cab991..13ddce7e 100644 --- a/services/firebase/emails/weekly.html +++ b/services/firebase/emails/weekly.html @@ -407,11 +407,11 @@ @@ -438,11 +438,11 @@ @@ -469,11 +469,11 @@ diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 78824141..b5a222c9 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -52,36 +52,49 @@ export async function getWeeklyData(city:string) { const then = new Date(); then.setDate(then.getDate() + 7); const sevenDaysFromNow = then.toISOString().slice(0,10); + const data = [] + let cityData: any = {}; + let profile: any = {}; const cityDocs = (await firestore .collection('profiles') .where('username', '==', city) .get() -).docs - - const cityData: any = []; + ).docs for(const doc of cityDocs ) { const city = { id: doc.id, ...doc.data() } as any - cityData.push(city) + + cityData = {...city}; } - - + + const profileDocs = ( + await firestore + .collection('profiles') + .where('username', '==', city) + .get() + ).docs + + for(const doc of profileDocs ) { + const user = { + id:doc.id, + ...doc.data() + } + profile = {...user} + } + const eventDocs = ( await firestore .collection('posts') .where('startDate', '>', today) .where('startDate','<', sevenDaysFromNow) - .where('place','==',cityData[0].place) + .where('place','==',cityData.place) .get() ).docs - - const data = [] - for (const doc of eventDocs) { const event = { id: doc.id, @@ -93,13 +106,13 @@ export async function getWeeklyData(city:string) { const events: any = { intro:'Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.', - title:'Munich Dance Calendar', + title:`${city} Dance Calendar`, links: { - telegram: "https://t.me/WeDanceMunich", - instagram: "https://instagram.com/WeDanceMunich", - facebook: "https://fb.com/WeDanceMunich", + telegram: profile.telegram, + instagram: profile.instagram, + facebook: profile.facebook, addEvent: "https://wedance.vip/events/-/edit", - city: "https://wedance.vip/Munich", + city: `https://wedance.vip/${city}`, }, days: data.map(event=>({ title:event.name, From ba6c242f1b698f8edbc9b61a68d0bf579f2a3a01 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Wed, 8 Feb 2023 20:19:00 +0600 Subject: [PATCH 07/20] fix code formatting. --- services/firebase/src/lib/digest.ts | 123 +++++++++++++--------------- 1 file changed, 59 insertions(+), 64 deletions(-) diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index b5a222c9..c134b46e 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -1,11 +1,10 @@ -const { createSSRApp } = require('vue'); -const { renderToString } = require('vue/server-renderer'); -import mjml2html = require("mjml") -import * as fs from "fs"; +const { createSSRApp } = require("vue"); +const { renderToString } = require("vue/server-renderer"); +import mjml2html = require("mjml"); +import * as fs from "fs"; import { firestore } from "../firebase"; - -export async function renderEmail(type: string, data:any, customUtms = {}) { +export async function renderEmail(type: string, data: any, customUtms = {}) { const template = fs.readFileSync(`./templates/${type}.mjml`, "utf8"); const defaultUtms = { @@ -25,7 +24,7 @@ export async function renderEmail(type: string, data:any, customUtms = {}) { }, template, methods: { - link(url:string, utmContent = "") { + link(url: string, utmContent = "") { return ( url + "?utm_campaign=" + @@ -41,72 +40,68 @@ export async function renderEmail(type: string, data:any, customUtms = {}) { }, }); - app.config.compilerOptions.isCustomElement = (tag:any) => tag.startsWith("mj"); + app.config.compilerOptions.isCustomElement = (tag: any) => + tag.startsWith("mj"); - return mjml2html(await renderToString(app)).html; + return mjml2html(await renderToString(app)).html; } -export async function getWeeklyData(city:string) { - - const today = new Date().toISOString().slice(0,10) +export async function getWeeklyData(city: string) { + const today = new Date().toISOString().slice(0, 10); const then = new Date(); then.setDate(then.getDate() + 7); - const sevenDaysFromNow = then.toISOString().slice(0,10); - const data = [] - let cityData: any = {}; - let profile: any = {}; - - const cityDocs = (await firestore - .collection('profiles') - .where('username', '==', city) - .get() - ).docs - - for(const doc of cityDocs ) { - const city = { - id: doc.id, - ...doc.data() - } as any - - cityData = {...city}; - } + const sevenDaysFromNow = then.toISOString().slice(0, 10); + const data = []; + let cityData: any = {}; + let profile: any = {}; + + const cityDocs = ( + await firestore.collection("profiles").where("username", "==", city).get() + ).docs; + + for (const doc of cityDocs) { + const getCity = { + id: doc.id, + ...doc.data(), + } as any; + + cityData = { ...getCity }; + } const profileDocs = ( - await firestore - .collection('profiles') - .where('username', '==', city) - .get() - ).docs + await firestore.collection("profiles").where("username", "==", city).get() + ).docs; - for(const doc of profileDocs ) { + for (const doc of profileDocs) { const user = { - id:doc.id, - ...doc.data() - } - profile = {...user} + id: doc.id, + ...doc.data(), + }; + profile = { ...user }; } - + const eventDocs = ( await firestore - .collection('posts') - .where('startDate', '>', today) - .where('startDate','<', sevenDaysFromNow) - .where('place','==',cityData.place) + .collection("posts") + .where("startDate", ">", today) + .where("startDate", "<", sevenDaysFromNow) + .where("place", "==", cityData.place) .get() - ).docs + ).docs; for (const doc of eventDocs) { const event = { id: doc.id, ...doc.data(), - } as any + } as any; - data.push(event) + data.push(event); } - + const events: any = { - intro:'Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.', - title:`${city} Dance Calendar`, + intro: + "Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.", + title: `${city} Dance Calendar`, links: { telegram: profile.telegram, instagram: profile.instagram, @@ -114,17 +109,17 @@ export async function getWeeklyData(city:string) { addEvent: "https://wedance.vip/events/-/edit", city: `https://wedance.vip/${city}`, }, - days: data.map(event=>({ - title:event.name, - organizer:event.org.name, - venue:event.venue?.name, - format:event.eventType, - time:event.startDate, - link:event.link, - cover:event.cover, - styles:Object.keys(event.styles) - })) - } + days: data.map((event) => ({ + title: event.name, + organizer: event.org.name, + venue: event.venue?.name, + format: event.eventType, + time: event.startDate, + link: event.link, + cover: event.cover, + styles: Object.keys(event.styles), + })), + }; - return events; + return events; } From 2f54c20c57a6e2b0f7dc2aebde0cce9edc191076 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Wed, 8 Feb 2023 20:53:38 +0600 Subject: [PATCH 08/20] remove repetitive code. --- services/firebase/src/lib/digest.ts | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index c134b46e..6a19a62d 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -52,32 +52,14 @@ export async function getWeeklyData(city: string) { then.setDate(then.getDate() + 7); const sevenDaysFromNow = then.toISOString().slice(0, 10); const data = []; - let cityData: any = {}; let profile: any = {}; - const cityDocs = ( - await firestore.collection("profiles").where("username", "==", city).get() - ).docs; - - for (const doc of cityDocs) { - const getCity = { - id: doc.id, - ...doc.data(), - } as any; - - cityData = { ...getCity }; - } - const profileDocs = ( await firestore.collection("profiles").where("username", "==", city).get() ).docs; for (const doc of profileDocs) { - const user = { - id: doc.id, - ...doc.data(), - }; - profile = { ...user }; + profile = { id: doc.id, ...doc.data() }; } const eventDocs = ( @@ -85,7 +67,7 @@ export async function getWeeklyData(city: string) { .collection("posts") .where("startDate", ">", today) .where("startDate", "<", sevenDaysFromNow) - .where("place", "==", cityData.place) + .where("place", "==", profile.place) .get() ).docs; From e77b7297ebd1fb4150f2a84f7eaf965c4f92713a Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Thu, 9 Mar 2023 21:25:58 +0600 Subject: [PATCH 09/20] build scheduleEmail. --- services/firebase/emails/weekly.html | 4 +- services/firebase/package.json | 2 + services/firebase/src/index.ts | 83 +++++++++++++++++++++++++ services/firebase/src/lib/digest.ts | 62 ++++++++++-------- services/firebase/src/lib/sendEmail.ts | 1 - services/firebase/templates/weekly.mjml | 2 +- services/firebase/yarn.lock | 10 +++ 7 files changed, 135 insertions(+), 29 deletions(-) diff --git a/services/firebase/emails/weekly.html b/services/firebase/emails/weekly.html index 13ddce7e..ef799f76 100644 --- a/services/firebase/emails/weekly.html +++ b/services/firebase/emails/weekly.html @@ -260,7 +260,7 @@
- + - +
- + - +
- + - +
- +
Friday • 10 Mar
02:00
Alex Razbakov • Munich
Festival • AfroHouse
@@ -274,7 +274,7 @@ - +
Monday • 13 Mar
02:30
Aina • Munich
Party • Balboa • Afrobeats • AfroHouse
diff --git a/services/firebase/package.json b/services/firebase/package.json index 071e71c9..461f22b4 100644 --- a/services/firebase/package.json +++ b/services/firebase/package.json @@ -39,6 +39,7 @@ "mailgun-js": "^0.22.0", "markdown-it": "^10.0.0", "mjml": "^4.13.0", + "moment": "^2.29.4", "node-fetch": "^3.2.10", "png-to-jpeg": "^1.0.1", "puppeteer": "^7.0.0", @@ -50,6 +51,7 @@ "@types/dotenv": "^8.2.0", "@types/mjml": "^4.7.0", "@types/puppeteer": "^5.4.2", + "@types/uuid": "^9.0.1", "@types/vue": "^2.0.0", "copyfiles": "^2.4.1", "firebase-functions-test": "^0.1.6", diff --git a/services/firebase/src/index.ts b/services/firebase/src/index.ts index 276b0251..1250730e 100644 --- a/services/firebase/src/index.ts +++ b/services/firebase/src/index.ts @@ -19,6 +19,7 @@ import { wrap } from './sentry' import { announceEvent } from './lib/telegram' import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' +import { renderEmail, getWeeklyData } from './lib/digest' require('dotenv').config() @@ -656,6 +657,88 @@ export const matchNotification = functions.firestore await sendEmail(data) }) + export const scheduleEmail = functions.pubsub + .schedule('every monday 18:00') + .timeZone('America/New_York') + .onRun(async (context) => { + const cityDocs = ( + await firestore.collection('profiles').where('type', '==', 'City').get() + ).docs; + const cities: any = []; + for (let doc of cityDocs) { + cities.push({ id: doc.id, ...doc.data() }); + } + let data; + let recipients: any = {}; + for (let city of cities) { + + if (!city.watch?.list) { + continue; + } + + data = await getWeeklyData(city.username); + const html = await renderEmail('weekly', data); + + let subscribers = Object.keys(city.watch?.list); + + for (let subscriber of subscribers) { + const profilesOfSubscriber = ( + await firestore + .collection('profiles') + .where('username', '==', subscriber) + .get() + ).docs; + + if (profilesOfSubscriber.length !== 1) { + continue; + } + + const profileId = profilesOfSubscriber[0].id; + + const accountDoc = await firestore + .collection('accounts') + .doc(profileId) + .get(); + + if (!accountDoc.exists) { + continue; + } + + const account = accountDoc.data(); + + recipients[profileId] = { + name:account?.name, + email:account?.email + } + + let userIds = []; + userIds.push(profileId); + + const weeklyNewsLetter = await firestore + .collection('weekly-newsletters') + .add({ + city: city?.username, + createdAt: Date.now(), + sentAt: Date.now(), + scheduledAt: Date.now(), + userIds: [...userIds], + }); + + const email: any = { + from: `WeDance `, + recipients, + subject: 'Weekly Newsletter', + content: html, + id: weeklyNewsLetter.id, + type: 'City', + }; + return await sendEmail(email); + } + } + return null; +}); + + // export const taskRunner = functions // .runWith({ memory: '2GB' }) // .pubsub.schedule('* * * * *') diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 6a19a62d..20727d27 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -2,7 +2,9 @@ const { createSSRApp } = require("vue"); const { renderToString } = require("vue/server-renderer"); import mjml2html = require("mjml"); import * as fs from "fs"; +import * as moment from "moment"; import { firestore } from "../firebase"; + export async function renderEmail(type: string, data: any, customUtms = {}) { const template = fs.readFileSync(`./templates/${type}.mjml`, "utf8"); @@ -61,25 +63,29 @@ export async function getWeeklyData(city: string) { for (const doc of profileDocs) { profile = { id: doc.id, ...doc.data() }; } + + let usernames = Object.keys(profile.watch?.list); - const eventDocs = ( - await firestore - .collection("posts") - .where("startDate", ">", today) - .where("startDate", "<", sevenDaysFromNow) - .where("place", "==", profile.place) - .get() - ).docs; - - for (const doc of eventDocs) { - const event = { - id: doc.id, - ...doc.data(), - } as any; + for(let username of usernames) { + const eventDocs = ( + await firestore + .collection("posts") + .where("startDate", ">", today) + .where("startDate", "<", sevenDaysFromNow) + .where("username", "==", username) + .get() + ).docs; - data.push(event); + for (const doc of eventDocs) { + const event = { + id: doc.id, + ...doc.data(), + } as any; + + data.push(event); + } } - + const events: any = { intro: "Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.", @@ -92,16 +98,22 @@ export async function getWeeklyData(city: string) { city: `https://wedance.vip/${city}`, }, days: data.map((event) => ({ - title: event.name, - organizer: event.org.name, - venue: event.venue?.name, - format: event.eventType, - time: event.startDate, - link: event.link, - cover: event.cover, - styles: Object.keys(event.styles), + day: moment(event.startDate).format("dddd"), + date: moment(event.startDate).format("D MMM") , + events: [ + { + title: event.name, + organizer: event.org.name, + venue: event.venue?.name, + format: event.eventType, + time: moment(event.startDate).format("hh:mm") , + link: event.link, + cover: event.cover, + styles: Object.keys(event.styles), + } + ] })), }; return events; -} +} \ No newline at end of file diff --git a/services/firebase/src/lib/sendEmail.ts b/services/firebase/src/lib/sendEmail.ts index 65d483f9..1949693a 100644 --- a/services/firebase/src/lib/sendEmail.ts +++ b/services/firebase/src/lib/sendEmail.ts @@ -38,7 +38,6 @@ export default async (data: any) => { 'v:campaignId': data.id, 'v:type': data.type, }) - jobs.push(job) }) diff --git a/services/firebase/templates/weekly.mjml b/services/firebase/templates/weekly.mjml index b0b16cee..6297bc89 100644 --- a/services/firebase/templates/weekly.mjml +++ b/services/firebase/templates/weekly.mjml @@ -173,4 +173,4 @@ - + \ No newline at end of file diff --git a/services/firebase/yarn.lock b/services/firebase/yarn.lock index 6b90b03d..ee68e75b 100644 --- a/services/firebase/yarn.lock +++ b/services/firebase/yarn.lock @@ -667,6 +667,11 @@ resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.2.tgz#6286b4c7228d58ab7866d19716f3696e03a09397" integrity sha512-Q5vtl1W5ue16D+nIaW8JWebSSraJVlK+EthKn7e7UcD4KWsaSJ8BqGPXNaPghgtcn/fhvrN17Tv8ksUsQpiplw== +"@types/uuid@^9.0.1": + version "9.0.1" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-9.0.1.tgz#98586dc36aee8dacc98cc396dbca8d0429647aa6" + integrity sha512-rFT3ak0/2trgvp4yYZo5iKFEPsET7vKydKF+VRCxlQ9bpheehyAJH89dAkaLEq/j/RZXJIqcgsmPJKUP1Z28HA== + "@types/vue@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/vue/-/vue-2.0.0.tgz#ec77b3d89591deb9ca5cb052368aa9c32be088e7" @@ -3494,6 +3499,11 @@ module-alias@^2.2.2: resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q== +moment@^2.29.4: + version "2.29.4" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" + integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" From 620f326a658f2b5dbedc8741287edb29912d55f9 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Fri, 10 Mar 2023 18:03:32 +0600 Subject: [PATCH 10/20] refactor code. --- services/firebase/src/index.ts | 44 ++++++++++++++------------ services/firebase/src/lib/digest.ts | 35 ++++++++++---------- services/firebase/src/lib/sendEmail.ts | 40 +++++++++++------------ 3 files changed, 61 insertions(+), 58 deletions(-) diff --git a/services/firebase/src/index.ts b/services/firebase/src/index.ts index 1250730e..30b05ce0 100644 --- a/services/firebase/src/index.ts +++ b/services/firebase/src/index.ts @@ -657,35 +657,38 @@ export const matchNotification = functions.firestore await sendEmail(data) }) - export const scheduleEmail = functions.pubsub - .schedule('every monday 18:00') - .timeZone('America/New_York') + +export const scheduleEmail = functions.pubsub + .schedule("every monday 18:00") + .timeZone("Europe/Berlin") .onRun(async (context) => { const cityDocs = ( - await firestore.collection('profiles').where('type', '==', 'City').get() + await firestore.collection("profiles").where("type", "==", "City").get() ).docs; + const cities: any = []; + for (let doc of cityDocs) { cities.push({ id: doc.id, ...doc.data() }); } + let data; let recipients: any = {}; - for (let city of cities) { + for (let city of cities) { if (!city.watch?.list) { continue; } + data = await getWeeklyData(city.username); + const html = await renderEmail("weekly", data); - data = await getWeeklyData(city.username); - const html = await renderEmail('weekly', data); - - let subscribers = Object.keys(city.watch?.list); + const subscribers = Object.keys(city.watch?.list); for (let subscriber of subscribers) { const profilesOfSubscriber = ( await firestore - .collection('profiles') - .where('username', '==', subscriber) + .collection("profiles") + .where("username", "==", subscriber) .get() ).docs; @@ -696,7 +699,7 @@ export const matchNotification = functions.firestore const profileId = profilesOfSubscriber[0].id; const accountDoc = await firestore - .collection('accounts') + .collection("accounts") .doc(profileId) .get(); @@ -706,16 +709,16 @@ export const matchNotification = functions.firestore const account = accountDoc.data(); - recipients[profileId] = { - name:account?.name, - email:account?.email - } + recipients[profileId] = { + name: account?.name, + email: account?.email, + }; let userIds = []; userIds.push(profileId); const weeklyNewsLetter = await firestore - .collection('weekly-newsletters') + .collection("weekly-newsletters") .add({ city: city?.username, createdAt: Date.now(), @@ -727,16 +730,17 @@ export const matchNotification = functions.firestore const email: any = { from: `WeDance `, recipients, - subject: 'Weekly Newsletter', + subject: "Weekly Newsletter", content: html, id: weeklyNewsLetter.id, - type: 'City', + type: "City", }; return await sendEmail(email); } } + return null; -}); + }); // export const taskRunner = functions diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 20727d27..49739531 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -2,9 +2,8 @@ const { createSSRApp } = require("vue"); const { renderToString } = require("vue/server-renderer"); import mjml2html = require("mjml"); import * as fs from "fs"; -import * as moment from "moment"; +import * as moment from "moment"; import { firestore } from "../firebase"; - export async function renderEmail(type: string, data: any, customUtms = {}) { const template = fs.readFileSync(`./templates/${type}.mjml`, "utf8"); @@ -63,10 +62,10 @@ export async function getWeeklyData(city: string) { for (const doc of profileDocs) { profile = { id: doc.id, ...doc.data() }; } - + let usernames = Object.keys(profile.watch?.list); - for(let username of usernames) { + for (let username of usernames) { const eventDocs = ( await firestore .collection("posts") @@ -76,16 +75,16 @@ export async function getWeeklyData(city: string) { .get() ).docs; - for (const doc of eventDocs) { - const event = { - id: doc.id, - ...doc.data(), - } as any; - - data.push(event); - } + for (const doc of eventDocs) { + const event = { + id: doc.id, + ...doc.data(), + } as any; + + data.push(event); + } } - + const events: any = { intro: "Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.", @@ -98,20 +97,20 @@ export async function getWeeklyData(city: string) { city: `https://wedance.vip/${city}`, }, days: data.map((event) => ({ - day: moment(event.startDate).format("dddd"), - date: moment(event.startDate).format("D MMM") , + day: moment(event.startDate).format("dddd"), + date: moment(event.startDate).format("D MMM"), events: [ { title: event.name, organizer: event.org.name, venue: event.venue?.name, format: event.eventType, - time: moment(event.startDate).format("hh:mm") , + time: moment(event.startDate).format("hh:mm"), link: event.link, cover: event.cover, styles: Object.keys(event.styles), - } - ] + }, + ], })), }; diff --git a/services/firebase/src/lib/sendEmail.ts b/services/firebase/src/lib/sendEmail.ts index 1949693a..616728d0 100644 --- a/services/firebase/src/lib/sendEmail.ts +++ b/services/firebase/src/lib/sendEmail.ts @@ -1,45 +1,45 @@ -import * as mailgun from 'mailgun-js' -import { config } from 'firebase-functions' -import * as MarkdownIt from 'markdown-it' +import * as mailgun from "mailgun-js"; +import { config } from "firebase-functions"; +import * as MarkdownIt from "markdown-it"; const md = new MarkdownIt({ html: true, linkify: true, typographer: true, -}) +}); const getHtml = (content: any) => { - return md.render(content) -} + return md.render(content); +}; export default async (data: any) => { - const mailgunConfig = config().mailgun + const mailgunConfig = config().mailgun; if (!mailgunConfig) { - throw new Error('Mailgun is not configured') + throw new Error("Mailgun is not configured"); } const mg = mailgun({ apiKey: mailgunConfig.key, domain: mailgunConfig.domain, host: mailgunConfig.host, - }) + }); - const jobs: Promise[] = [] + const jobs: Promise[] = []; - const uids = Object.keys(data.recipients) + const uids = Object.keys(data.recipients); uids.forEach((uid) => { const job = mg.messages().send({ from: data.from, to: `${data.recipients[uid].name} <${data.recipients[uid].email}>`, subject: data.subject, html: getHtml(data.content), - 'v:uid': uid, - 'v:campaignId': data.id, - 'v:type': data.type, - }) - jobs.push(job) - }) - - return await Promise.all(jobs) -} + "v:uid": uid, + "v:campaignId": data.id, + "v:type": data.type, + }); + jobs.push(job); + }); + + return await Promise.all(jobs); +}; \ No newline at end of file From 103be6db2070fe438bc37df157ac0b5d8bc6040c Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Fri, 10 Mar 2023 19:25:44 +0600 Subject: [PATCH 11/20] add weekly.html in .gitignore --- services/firebase/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/services/firebase/.gitignore b/services/firebase/.gitignore index ad64b03b..80586eba 100644 --- a/services/firebase/.gitignore +++ b/services/firebase/.gitignore @@ -8,4 +8,5 @@ typings/ node_modules/ var/ -serviceAccountKey.json \ No newline at end of file +serviceAccountKey.json +emails/ \ No newline at end of file From a519056f70c40bedc43cc1bc9884ddaabac245bf Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Tue, 28 Mar 2023 14:17:28 +0600 Subject: [PATCH 12/20] fix some issues. --- services/firebase/.gitignore | 1 - services/firebase/emails/weekly.html | 30 +--------- services/firebase/package.json | 2 +- services/firebase/src/lib/digest.ts | 55 +++++++++---------- services/firebase/src/lib/sendEmail.ts | 2 +- .../firebase/{ => src}/templates/weekly.mjml | 0 6 files changed, 29 insertions(+), 61 deletions(-) rename services/firebase/{ => src}/templates/weekly.mjml (100%) diff --git a/services/firebase/.gitignore b/services/firebase/.gitignore index 80586eba..e3e9410d 100644 --- a/services/firebase/.gitignore +++ b/services/firebase/.gitignore @@ -9,4 +9,3 @@ node_modules/ var/ serviceAccountKey.json -emails/ \ No newline at end of file diff --git a/services/firebase/emails/weekly.html b/services/firebase/emails/weekly.html index ef799f76..b5005fa4 100644 --- a/services/firebase/emails/weekly.html +++ b/services/firebase/emails/weekly.html @@ -251,35 +251,7 @@ - - - - - - -
Friday • 10 Mar
02:00
Alex Razbakov • Munich
Festival • AfroHouse
- - - - - - - - - -
Monday • 13 Mar
02:30
Aina • Munich
Party • Balboa • Afrobeats • AfroHouse
- - - - + ", today) - .where("startDate", "<", sevenDaysFromNow) - .where("username", "==", username) - .get() - ).docs; + const eventDocs = ( + await firestore + .collection("posts") + .where("startDate", ">", today) + .where("startDate", "<", sevenDaysFromNow) + .where("place", "==", cityProfile.place) + .get() + ).docs; - for (const doc of eventDocs) { - const event = { - id: doc.id, - ...doc.data(), - } as any; + for (const doc of eventDocs) { + const event = { + id: doc.id, + ...doc.data(), + } as any; - data.push(event); - } + data.push(event); } const events: any = { @@ -90,9 +87,9 @@ export async function getWeeklyData(city: string) { "Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.", title: `${city} Dance Calendar`, links: { - telegram: profile.telegram, - instagram: profile.instagram, - facebook: profile.facebook, + telegram: cityProfile.telegram, + instagram: cityProfile.instagram, + facebook: cityProfile.facebook, addEvent: "https://wedance.vip/events/-/edit", city: `https://wedance.vip/${city}`, }, @@ -115,4 +112,4 @@ export async function getWeeklyData(city: string) { }; return events; -} \ No newline at end of file +} diff --git a/services/firebase/src/lib/sendEmail.ts b/services/firebase/src/lib/sendEmail.ts index 616728d0..4e221b15 100644 --- a/services/firebase/src/lib/sendEmail.ts +++ b/services/firebase/src/lib/sendEmail.ts @@ -42,4 +42,4 @@ export default async (data: any) => { }); return await Promise.all(jobs); -}; \ No newline at end of file +}; diff --git a/services/firebase/templates/weekly.mjml b/services/firebase/src/templates/weekly.mjml similarity index 100% rename from services/firebase/templates/weekly.mjml rename to services/firebase/src/templates/weekly.mjml From 9d523beb93f8c1edbd8ee006c79f6d8c988e8891 Mon Sep 17 00:00:00 2001 From: Aleksey Razbakov Date: Tue, 28 Mar 2023 12:48:50 +0200 Subject: [PATCH 13/20] improve weekly newsletter --- services/firebase/src/cli.ts | 8 +- services/firebase/src/index.ts | 91 +-------------- services/firebase/src/lib/digest.ts | 170 +++++++++++++++++++++------- 3 files changed, 135 insertions(+), 134 deletions(-) diff --git a/services/firebase/src/cli.ts b/services/firebase/src/cli.ts index 57ab5868..5e70c59d 100644 --- a/services/firebase/src/cli.ts +++ b/services/firebase/src/cli.ts @@ -15,8 +15,8 @@ const { hideBin } = require('yargs/helpers') import { firestore } from './firebase' import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' -import { getWeeklyData, renderEmail } from "./lib/digest"; -import * as fs from "fs"; +import { getWeeklyData, renderEmail } from './lib/digest' +import * as fs from 'fs' yargs(hideBin(process.argv)) .command( @@ -180,9 +180,7 @@ yargs(hideBin(process.argv)) 'Generate Weekly Newsletter', () => undefined, async (argv: any) => { - const data = await getWeeklyData('Munich'); - const html = await renderEmail('weekly', data) - fs.writeFileSync("./emails/weekly.html", html); + await scheduleWeeklyEmails() } ) .command( diff --git a/services/firebase/src/index.ts b/services/firebase/src/index.ts index 30b05ce0..1b496fb5 100644 --- a/services/firebase/src/index.ts +++ b/services/firebase/src/index.ts @@ -19,7 +19,7 @@ import { wrap } from './sentry' import { announceEvent } from './lib/telegram' import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' -import { renderEmail, getWeeklyData } from './lib/digest' +import { renderEmail, getWeeklyData, scheduleWeeklyEmails } from './lib/digest' require('dotenv').config() @@ -114,7 +114,7 @@ app.post('/track/:action', async (req, res) => { }) }) -app.get('/share/*', async (req:any, res:any) => { +app.get('/share/*', async (req: any, res: any) => { const path = req.params[0] const timezone = req.query.timezone as string @@ -657,91 +657,12 @@ export const matchNotification = functions.firestore await sendEmail(data) }) - export const scheduleEmail = functions.pubsub - .schedule("every monday 18:00") - .timeZone("Europe/Berlin") + .schedule('every monday 18:00') + .timeZone('Europe/Berlin') .onRun(async (context) => { - const cityDocs = ( - await firestore.collection("profiles").where("type", "==", "City").get() - ).docs; - - const cities: any = []; - - for (let doc of cityDocs) { - cities.push({ id: doc.id, ...doc.data() }); - } - - let data; - let recipients: any = {}; - - for (let city of cities) { - if (!city.watch?.list) { - continue; - } - data = await getWeeklyData(city.username); - const html = await renderEmail("weekly", data); - - const subscribers = Object.keys(city.watch?.list); - - for (let subscriber of subscribers) { - const profilesOfSubscriber = ( - await firestore - .collection("profiles") - .where("username", "==", subscriber) - .get() - ).docs; - - if (profilesOfSubscriber.length !== 1) { - continue; - } - - const profileId = profilesOfSubscriber[0].id; - - const accountDoc = await firestore - .collection("accounts") - .doc(profileId) - .get(); - - if (!accountDoc.exists) { - continue; - } - - const account = accountDoc.data(); - - recipients[profileId] = { - name: account?.name, - email: account?.email, - }; - - let userIds = []; - userIds.push(profileId); - - const weeklyNewsLetter = await firestore - .collection("weekly-newsletters") - .add({ - city: city?.username, - createdAt: Date.now(), - sentAt: Date.now(), - scheduledAt: Date.now(), - userIds: [...userIds], - }); - - const email: any = { - from: `WeDance `, - recipients, - subject: "Weekly Newsletter", - content: html, - id: weeklyNewsLetter.id, - type: "City", - }; - return await sendEmail(email); - } - } - - return null; - }); - + await scheduleWeeklyEmails() + }) // export const taskRunner = functions // .runWith({ memory: '2GB' }) diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index f11a299e..8bfe442e 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -1,115 +1,197 @@ -const { createSSRApp } = require("vue"); -const { renderToString } = require("vue/server-renderer"); -import mjml2html = require("mjml"); -import * as fs from "fs"; -import * as moment from "moment"; -import { firestore } from "../firebase"; +const { createSSRApp } = require('vue') +const { renderToString } = require('vue/server-renderer') +import mjml2html = require('mjml') +import * as fs from 'fs' +import * as moment from 'moment' +import { firestore } from '../firebase' export async function renderEmail(type: string, data: any, customUtms = {}) { - const template = fs.readFileSync(`./src/templates/${type}.mjml`, "utf8"); + const template = fs.readFileSync(`./src/templates/${type}.mjml`, 'utf8') const defaultUtms = { campaign: type, - medium: "email", - source: "newsletter", - }; + medium: 'email', + source: 'newsletter', + } const utm = { ...defaultUtms, ...customUtms, - }; + } const app = createSSRApp({ data: () => { - return data; + return data }, template, methods: { - link(url: string, utmContent = "") { + link(url: string, utmContent = '') { return ( url + - "?utm_campaign=" + + '?utm_campaign=' + utm.campaign + - "&utm_medium=" + + '&utm_medium=' + utm.medium + - "&utm_source=" + + '&utm_source=' + utm.source + - "&utm_content=" + + '&utm_content=' + utmContent - ); + ) }, }, - }); + }) app.config.compilerOptions.isCustomElement = (tag: any) => - tag.startsWith("mj"); + tag.startsWith('mj') - return mjml2html(await renderToString(app)).html; + return mjml2html(await renderToString(app)).html } export async function getWeeklyData(city: string) { - const time = new Date(); - const today = time.toISOString().slice(0, 10); - time.setDate(time.getDate() + 7); - const sevenDaysFromNow = time.toISOString().slice(0, 10); + const time = new Date() + const today = time.toISOString().slice(0, 10) + time.setDate(time.getDate() + 7) + const sevenDaysFromNow = time.toISOString().slice(0, 10) - const data = []; - let cityProfile: any = {}; + const data = [] + let cityProfile: any = {} const profileDocs = ( - await firestore.collection("profiles").where("username", "==", city).get() - ).docs; + await firestore + .collection('profiles') + .where('username', '==', city) + .get() + ).docs for (const doc of profileDocs) { - cityProfile = { id: doc.id, ...doc.data() }; + cityProfile = { id: doc.id, ...doc.data() } } const eventDocs = ( await firestore - .collection("posts") - .where("startDate", ">", today) - .where("startDate", "<", sevenDaysFromNow) - .where("place", "==", cityProfile.place) + .collection('posts') + .where('startDate', '>', today) + .where('startDate', '<', sevenDaysFromNow) + .where('place', '==', cityProfile.place) .get() - ).docs; + ).docs for (const doc of eventDocs) { const event = { id: doc.id, ...doc.data(), - } as any; + } as any - data.push(event); + data.push(event) } const events: any = { intro: - "Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.", + 'Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.', title: `${city} Dance Calendar`, links: { telegram: cityProfile.telegram, instagram: cityProfile.instagram, facebook: cityProfile.facebook, - addEvent: "https://wedance.vip/events/-/edit", + addEvent: 'https://wedance.vip/events/-/edit', city: `https://wedance.vip/${city}`, }, days: data.map((event) => ({ - day: moment(event.startDate).format("dddd"), - date: moment(event.startDate).format("D MMM"), + day: moment(event.startDate).format('dddd'), + date: moment(event.startDate).format('D MMM'), events: [ { title: event.name, organizer: event.org.name, venue: event.venue?.name, format: event.eventType, - time: moment(event.startDate).format("hh:mm"), + time: moment(event.startDate).format('hh:mm'), link: event.link, cover: event.cover, styles: Object.keys(event.styles), }, ], })), - }; + } + + return events +} + +export async function getSubscribers(cityProfile: any) { + const recipients = {} as any + + const subscribers = Object.keys(cityProfile.watch?.list) + + for (let subscriber of subscribers) { + const profilesOfSubscriber = ( + await firestore + .collection('profiles') + .where('username', '==', subscriber) + .get() + ).docs + + if (profilesOfSubscriber.length !== 1) { + continue + } + + const profileId = profilesOfSubscriber[0].id + + const accountDoc = await firestore + .collection('accounts') + .doc(profileId) + .get() + + if (!accountDoc.exists) { + continue + } + + const account = accountDoc.data() + + recipients[profileId] = { + name: account?.name, + email: account?.email, + } + } + + return recipients +} + +function getNextMonday() { + // todo: use library to get a next monday date + return Date.now() +} + +export async function scheduleWeeklyEmails() { + const cityDocs = ( + await firestore + .collection('profiles') + .where('type', '==', 'City') + .get() + ).docs + + const nextMonday = getNextMonday() + + for (let cityDoc of cityDocs) { + const cityProfile = cityDoc.data() + + if (!cityProfile.watch?.list) { + continue + } + + const weeklyEmailDetails = await getWeeklyData(cityProfile.username) + const html = await renderEmail('weekly', weeklyEmailDetails) + const recipients = await getSubscribers(cityProfile) + + await firestore.collection('emails').add({ + status: 'scheduled', + scheduledAt: nextMonday, + createdAt: Date.now(), + from: `WeDance `, + subject: 'Weekly Newsletter', + recipients, + content: html, + }) + } - return events; + return null } From 62269c6371d063374e63621a29f2f163306ac325 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Thu, 30 Mar 2023 23:59:08 +0600 Subject: [PATCH 14/20] clean up code. --- services/firebase/src/cli.ts | 4 +- services/firebase/src/index.ts | 2 +- services/firebase/src/lib/digest.ts | 112 ++++++++++++------------- services/firebase/src/lib/sendEmail.ts | 14 ++-- 4 files changed, 66 insertions(+), 66 deletions(-) diff --git a/services/firebase/src/cli.ts b/services/firebase/src/cli.ts index 5e70c59d..52e1bdcb 100644 --- a/services/firebase/src/cli.ts +++ b/services/firebase/src/cli.ts @@ -15,8 +15,8 @@ const { hideBin } = require('yargs/helpers') import { firestore } from './firebase' import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' -import { getWeeklyData, renderEmail } from './lib/digest' -import * as fs from 'fs' +import { scheduleWeeklyEmails } from './lib/digest' + yargs(hideBin(process.argv)) .command( diff --git a/services/firebase/src/index.ts b/services/firebase/src/index.ts index 1b496fb5..7a8ec495 100644 --- a/services/firebase/src/index.ts +++ b/services/firebase/src/index.ts @@ -19,7 +19,7 @@ import { wrap } from './sentry' import { announceEvent } from './lib/telegram' import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' -import { renderEmail, getWeeklyData, scheduleWeeklyEmails } from './lib/digest' +import { scheduleWeeklyEmails } from './lib/digest' require('dotenv').config() diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 8bfe442e..1528e13a 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -1,27 +1,27 @@ -const { createSSRApp } = require('vue') -const { renderToString } = require('vue/server-renderer') -import mjml2html = require('mjml') -import * as fs from 'fs' -import * as moment from 'moment' -import { firestore } from '../firebase' +const { createSSRApp } = require('vue'); +const { renderToString } = require('vue/server-renderer'); +import mjml2html = require('mjml'); +import * as fs from 'fs'; +import * as moment from 'moment'; +import { firestore } from '../firebase'; export async function renderEmail(type: string, data: any, customUtms = {}) { - const template = fs.readFileSync(`./src/templates/${type}.mjml`, 'utf8') + const template = fs.readFileSync(`./src/templates/${type}.mjml`, 'utf8'); const defaultUtms = { campaign: type, medium: 'email', source: 'newsletter', - } + }; const utm = { ...defaultUtms, ...customUtms, - } + }; const app = createSSRApp({ data: () => { - return data + return data; }, template, methods: { @@ -36,35 +36,32 @@ export async function renderEmail(type: string, data: any, customUtms = {}) { utm.source + '&utm_content=' + utmContent - ) + ); }, }, - }) + }); app.config.compilerOptions.isCustomElement = (tag: any) => - tag.startsWith('mj') + tag.startsWith('mj'); - return mjml2html(await renderToString(app)).html + return mjml2html(await renderToString(app)).html; } export async function getWeeklyData(city: string) { - const time = new Date() - const today = time.toISOString().slice(0, 10) - time.setDate(time.getDate() + 7) - const sevenDaysFromNow = time.toISOString().slice(0, 10) + const time = new Date(); + const today = time.toISOString().slice(0, 10); + time.setDate(time.getDate() + 7); + const sevenDaysFromNow = time.toISOString().slice(0, 10); - const data = [] - let cityProfile: any = {} + const data = []; + let cityProfile: any = {}; const profileDocs = ( - await firestore - .collection('profiles') - .where('username', '==', city) - .get() - ).docs + await firestore.collection('profiles').where('username', '==', city).get() + ).docs; for (const doc of profileDocs) { - cityProfile = { id: doc.id, ...doc.data() } + cityProfile = { id: doc.id, ...doc.data() }; } const eventDocs = ( @@ -74,15 +71,15 @@ export async function getWeeklyData(city: string) { .where('startDate', '<', sevenDaysFromNow) .where('place', '==', cityProfile.place) .get() - ).docs + ).docs; for (const doc of eventDocs) { const event = { id: doc.id, ...doc.data(), - } as any + } as any; - data.push(event) + data.push(event); } const events: any = { @@ -112,15 +109,15 @@ export async function getWeeklyData(city: string) { }, ], })), - } + }; - return events + return events; } export async function getSubscribers(cityProfile: any) { - const recipients = {} as any + const recipients = {} as any; - const subscribers = Object.keys(cityProfile.watch?.list) + const subscribers = Object.keys(cityProfile.watch?.list); for (let subscriber of subscribers) { const profilesOfSubscriber = ( @@ -128,59 +125,62 @@ export async function getSubscribers(cityProfile: any) { .collection('profiles') .where('username', '==', subscriber) .get() - ).docs + ).docs; if (profilesOfSubscriber.length !== 1) { - continue + continue; } - const profileId = profilesOfSubscriber[0].id + const profileId = profilesOfSubscriber[0].id; const accountDoc = await firestore .collection('accounts') .doc(profileId) - .get() + .get(); if (!accountDoc.exists) { - continue + continue; } - const account = accountDoc.data() + const account = accountDoc.data(); recipients[profileId] = { name: account?.name, email: account?.email, - } + }; } - return recipients + return recipients; } function getNextMonday() { - // todo: use library to get a next monday date - return Date.now() + const nextMonday = moment() + .add(1, 'weeks') + .day('Monday') + .hour(18) + .minute(0) + .second(0); + const formattedDate = nextMonday.format('MMMM DD, YYYY hh:mm A'); + return formattedDate; } export async function scheduleWeeklyEmails() { const cityDocs = ( - await firestore - .collection('profiles') - .where('type', '==', 'City') - .get() - ).docs + await firestore.collection('profiles').where('type', '==', 'City').get() + ).docs; - const nextMonday = getNextMonday() + const nextMonday = getNextMonday(); for (let cityDoc of cityDocs) { - const cityProfile = cityDoc.data() + const cityProfile = cityDoc.data(); if (!cityProfile.watch?.list) { - continue + continue; } - const weeklyEmailDetails = await getWeeklyData(cityProfile.username) - const html = await renderEmail('weekly', weeklyEmailDetails) - const recipients = await getSubscribers(cityProfile) + const weeklyEmailDetails = await getWeeklyData(cityProfile.username); + const html = await renderEmail('weekly', weeklyEmailDetails); + const recipients = await getSubscribers(cityProfile); await firestore.collection('emails').add({ status: 'scheduled', @@ -190,8 +190,8 @@ export async function scheduleWeeklyEmails() { subject: 'Weekly Newsletter', recipients, content: html, - }) + }); } - return null + return null; } diff --git a/services/firebase/src/lib/sendEmail.ts b/services/firebase/src/lib/sendEmail.ts index 4e221b15..4e3b92d3 100644 --- a/services/firebase/src/lib/sendEmail.ts +++ b/services/firebase/src/lib/sendEmail.ts @@ -1,6 +1,6 @@ -import * as mailgun from "mailgun-js"; -import { config } from "firebase-functions"; -import * as MarkdownIt from "markdown-it"; +import * as mailgun from 'mailgun-js'; +import { config } from 'firebase-functions'; +import * as MarkdownIt from 'markdown-it'; const md = new MarkdownIt({ html: true, @@ -16,7 +16,7 @@ export default async (data: any) => { const mailgunConfig = config().mailgun; if (!mailgunConfig) { - throw new Error("Mailgun is not configured"); + throw new Error('Mailgun is not configured'); } const mg = mailgun({ @@ -34,9 +34,9 @@ export default async (data: any) => { to: `${data.recipients[uid].name} <${data.recipients[uid].email}>`, subject: data.subject, html: getHtml(data.content), - "v:uid": uid, - "v:campaignId": data.id, - "v:type": data.type, + 'v:uid': uid, + 'v:campaignId': data.id, + 'v:type': data.type, }); jobs.push(job); }); From eb5c93f59234cc740f3fa56cd4df873c3319da88 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Fri, 31 Mar 2023 15:13:06 +0600 Subject: [PATCH 15/20] format weekly.mjml --- services/firebase/src/templates/weekly.mjml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/services/firebase/src/templates/weekly.mjml b/services/firebase/src/templates/weekly.mjml index 6297bc89..b0b16cee 100644 --- a/services/firebase/src/templates/weekly.mjml +++ b/services/firebase/src/templates/weekly.mjml @@ -173,4 +173,4 @@ - \ No newline at end of file + From ce4ad942e6bd1c8a86f283ff7f0e8a785d769f24 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Fri, 31 Mar 2023 15:41:38 +0600 Subject: [PATCH 16/20] remove emails folder. --- services/firebase/emails/weekly.html | 545 --------------------------- 1 file changed, 545 deletions(-) delete mode 100644 services/firebase/emails/weekly.html diff --git a/services/firebase/emails/weekly.html b/services/firebase/emails/weekly.html deleted file mode 100644 index b5005fa4..00000000 --- a/services/firebase/emails/weekly.html +++ /dev/null @@ -1,545 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - -
- - - - - -
- - - - - - - -
- - -
- - - - - - - - - -
- - - -
- -
- - -
- -
- - - - - -
- - - - - - - -
- - -
- - - - - - - - - -
- - - - - - - -
- - - - - - - -
- -
- -
- - -
- -
- - - - - -
- - - - - - - -
- - - -
- - - - - - - -
- - -
- - - - - - - -
- - - - - - - - - - - - - - - - - - - - - -
- -
Hope you had a great weekend and are ready with your dancing shoes on for a fantastic week ahead.
- -
- -
Munich Dance Calendar
- -
- - - - - - - -
- - Show More - -
- -
- -
Something is missing? Add event.
- -
- -
- -
- - -
- -
- - - - - -
- - - - - - - -
- - -
- - - - - - - - - - - - - - - - - -
- -
Follow us
- -
- -
We regularly post event announcements and introduce new members on our social media.
- -
- - - - - - - - - - - - -
- - - - - - -
- - - -
-
- - - - - - - - - - -
- - - - - - -
- - - -
-
- - - - - - - - - - -
- - - - - - -
- - - -
-
- - - -
- -
- - -
- -
- - - -
- -
- - - - - -
- - - - - - - -
- - -
- - - - - - - - - -
- -
You are subscribed to Munich Dance Calendar. Visit the page to Unsubscribe.
- -
- -
- - -
- -
- - - - - -
- - - - \ No newline at end of file From 0446dcb3b11af6bf8bcebb98803ea29026100522 Mon Sep 17 00:00:00 2001 From: "Mr. Robot" Date: Tue, 4 Apr 2023 21:53:33 +0600 Subject: [PATCH 17/20] format code. --- services/firebase/package.json | 2 +- services/firebase/src/cli.ts | 20 ++---- services/firebase/src/lib/digest.ts | 98 +++++++++++++------------- services/firebase/src/lib/sendEmail.ts | 32 ++++----- 4 files changed, 73 insertions(+), 79 deletions(-) diff --git a/services/firebase/package.json b/services/firebase/package.json index 8db677a3..461f22b4 100644 --- a/services/firebase/package.json +++ b/services/firebase/package.json @@ -3,7 +3,7 @@ "scripts": { "lint": "tslint --project tsconfig.json", "build": "yarn clean && tsc && yarn copy-files", - "copy-files": "copyfiles -u 1 src/**/*.js templates/**/*.mjml dist/", + "copy-files": "copyfiles -u 1 src/**/*.js src/**/*.mjml dist/", "clean": "rimraf dist/", "test": "npm run build && node dist/test.js", "cli": "npm run build && node dist/cli.js", diff --git a/services/firebase/src/cli.ts b/services/firebase/src/cli.ts index 52e1bdcb..838a896f 100644 --- a/services/firebase/src/cli.ts +++ b/services/firebase/src/cli.ts @@ -17,7 +17,6 @@ import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' import { scheduleWeeklyEmails } from './lib/digest' - yargs(hideBin(process.argv)) .command( 'tg:announce ', @@ -51,10 +50,7 @@ yargs(hideBin(process.argv)) () => undefined, async (argv: any) => { const event = ( - await firestore - .collection('posts') - .doc(argv.eventId) - .get() + await firestore.collection('posts').doc(argv.eventId).get() ).data() as any const result = await announceEventIG(event) @@ -226,8 +222,9 @@ yargs(hideBin(process.argv)) for (const instance of instances) { console.log( - ` <${instance.username}> x ${instance.viewsCount} x ${instance - .star?.count || 0} x ${instance.website}` + ` <${instance.username}> x ${instance.viewsCount} x ${ + instance.star?.count || 0 + } x ${instance.website}` ) // if (!instance.website) { @@ -248,12 +245,9 @@ yargs(hideBin(process.argv)) for (const city of missingCityPlace) { console.log(`Updating cityPlaceId for ${city.name}`) - await firestore - .collection('profiles') - .doc(city.id) - .update({ - cityPlaceId: city.place, - }) + await firestore.collection('profiles').doc(city.id).update({ + cityPlaceId: city.place, + }) } } ) diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 1528e13a..2a406d09 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -1,27 +1,27 @@ -const { createSSRApp } = require('vue'); -const { renderToString } = require('vue/server-renderer'); -import mjml2html = require('mjml'); -import * as fs from 'fs'; -import * as moment from 'moment'; -import { firestore } from '../firebase'; +const { createSSRApp } = require('vue') +const { renderToString } = require('vue/server-renderer') +import mjml2html = require('mjml') +import * as fs from 'fs' +import * as moment from 'moment' +import { firestore } from '../firebase' export async function renderEmail(type: string, data: any, customUtms = {}) { - const template = fs.readFileSync(`./src/templates/${type}.mjml`, 'utf8'); + const template = fs.readFileSync(`./src/templates/${type}.mjml`, 'utf8') const defaultUtms = { campaign: type, medium: 'email', source: 'newsletter', - }; + } const utm = { ...defaultUtms, ...customUtms, - }; + } const app = createSSRApp({ data: () => { - return data; + return data }, template, methods: { @@ -36,32 +36,32 @@ export async function renderEmail(type: string, data: any, customUtms = {}) { utm.source + '&utm_content=' + utmContent - ); + ) }, }, - }); + }) app.config.compilerOptions.isCustomElement = (tag: any) => - tag.startsWith('mj'); + tag.startsWith('mj') - return mjml2html(await renderToString(app)).html; + return mjml2html(await renderToString(app)).html } export async function getWeeklyData(city: string) { - const time = new Date(); - const today = time.toISOString().slice(0, 10); - time.setDate(time.getDate() + 7); - const sevenDaysFromNow = time.toISOString().slice(0, 10); + const time = new Date() + const today = time.toISOString().slice(0, 10) + time.setDate(time.getDate() + 7) + const sevenDaysFromNow = time.toISOString().slice(0, 10) - const data = []; - let cityProfile: any = {}; + const data = [] + let cityProfile: any = {} const profileDocs = ( await firestore.collection('profiles').where('username', '==', city).get() - ).docs; + ).docs for (const doc of profileDocs) { - cityProfile = { id: doc.id, ...doc.data() }; + cityProfile = { id: doc.id, ...doc.data() } } const eventDocs = ( @@ -71,15 +71,15 @@ export async function getWeeklyData(city: string) { .where('startDate', '<', sevenDaysFromNow) .where('place', '==', cityProfile.place) .get() - ).docs; + ).docs for (const doc of eventDocs) { const event = { id: doc.id, ...doc.data(), - } as any; + } as any - data.push(event); + data.push(event) } const events: any = { @@ -109,15 +109,15 @@ export async function getWeeklyData(city: string) { }, ], })), - }; + } - return events; + return events } export async function getSubscribers(cityProfile: any) { - const recipients = {} as any; + const recipients = {} as any - const subscribers = Object.keys(cityProfile.watch?.list); + const subscribers = Object.keys(cityProfile.watch?.list) for (let subscriber of subscribers) { const profilesOfSubscriber = ( @@ -125,32 +125,32 @@ export async function getSubscribers(cityProfile: any) { .collection('profiles') .where('username', '==', subscriber) .get() - ).docs; + ).docs if (profilesOfSubscriber.length !== 1) { - continue; + continue } - const profileId = profilesOfSubscriber[0].id; + const profileId = profilesOfSubscriber[0].id const accountDoc = await firestore .collection('accounts') .doc(profileId) - .get(); + .get() if (!accountDoc.exists) { - continue; + continue } - const account = accountDoc.data(); + const account = accountDoc.data() recipients[profileId] = { name: account?.name, email: account?.email, - }; + } } - return recipients; + return recipients } function getNextMonday() { @@ -159,28 +159,28 @@ function getNextMonday() { .day('Monday') .hour(18) .minute(0) - .second(0); - const formattedDate = nextMonday.format('MMMM DD, YYYY hh:mm A'); - return formattedDate; + .second(0) + const formattedDate = nextMonday.format('MMMM DD, YYYY hh:mm A') + return formattedDate } export async function scheduleWeeklyEmails() { const cityDocs = ( await firestore.collection('profiles').where('type', '==', 'City').get() - ).docs; + ).docs - const nextMonday = getNextMonday(); + const nextMonday = getNextMonday() for (let cityDoc of cityDocs) { - const cityProfile = cityDoc.data(); + const cityProfile = cityDoc.data() if (!cityProfile.watch?.list) { - continue; + continue } - const weeklyEmailDetails = await getWeeklyData(cityProfile.username); - const html = await renderEmail('weekly', weeklyEmailDetails); - const recipients = await getSubscribers(cityProfile); + const weeklyEmailDetails = await getWeeklyData(cityProfile.username) + const html = await renderEmail('weekly', weeklyEmailDetails) + const recipients = await getSubscribers(cityProfile) await firestore.collection('emails').add({ status: 'scheduled', @@ -190,8 +190,8 @@ export async function scheduleWeeklyEmails() { subject: 'Weekly Newsletter', recipients, content: html, - }); + }) } - return null; + return null } diff --git a/services/firebase/src/lib/sendEmail.ts b/services/firebase/src/lib/sendEmail.ts index 4e3b92d3..1949693a 100644 --- a/services/firebase/src/lib/sendEmail.ts +++ b/services/firebase/src/lib/sendEmail.ts @@ -1,33 +1,33 @@ -import * as mailgun from 'mailgun-js'; -import { config } from 'firebase-functions'; -import * as MarkdownIt from 'markdown-it'; +import * as mailgun from 'mailgun-js' +import { config } from 'firebase-functions' +import * as MarkdownIt from 'markdown-it' const md = new MarkdownIt({ html: true, linkify: true, typographer: true, -}); +}) const getHtml = (content: any) => { - return md.render(content); -}; + return md.render(content) +} export default async (data: any) => { - const mailgunConfig = config().mailgun; + const mailgunConfig = config().mailgun if (!mailgunConfig) { - throw new Error('Mailgun is not configured'); + throw new Error('Mailgun is not configured') } const mg = mailgun({ apiKey: mailgunConfig.key, domain: mailgunConfig.domain, host: mailgunConfig.host, - }); + }) - const jobs: Promise[] = []; + const jobs: Promise[] = [] - const uids = Object.keys(data.recipients); + const uids = Object.keys(data.recipients) uids.forEach((uid) => { const job = mg.messages().send({ from: data.from, @@ -37,9 +37,9 @@ export default async (data: any) => { 'v:uid': uid, 'v:campaignId': data.id, 'v:type': data.type, - }); - jobs.push(job); - }); + }) + jobs.push(job) + }) - return await Promise.all(jobs); -}; + return await Promise.all(jobs) +} From 44aef5a8d7abf05827e85cfec02b0d58793ac3b1 Mon Sep 17 00:00:00 2001 From: Aleksey Razbakov Date: Tue, 9 May 2023 20:18:59 +0200 Subject: [PATCH 18/20] fix newsletter --- components/TRecipients.vue | 2 + services/firebase/package.json | 1 + services/firebase/src/cli.ts | 23 +++--- services/firebase/src/index.ts | 13 ++-- services/firebase/src/lib/digest.ts | 99 +++++++++++++++----------- services/firebase/src/lib/sendEmail.ts | 15 ++-- services/firebase/yarn.lock | 7 ++ 7 files changed, 93 insertions(+), 67 deletions(-) diff --git a/components/TRecipients.vue b/components/TRecipients.vue index a04e584c..d7728448 100644 --- a/components/TRecipients.vue +++ b/components/TRecipients.vue @@ -55,6 +55,7 @@ export default { unsubscribedAt: 'bg-orange-500', }, folderNames: { + scheduled: 'Scheduled', deliveredAt: 'Unread', openedAt: 'Opened', clickedAt: 'Clicked', @@ -78,6 +79,7 @@ export default { const recipients = this.itemsList return { + scheduled: recipients, failedAt: recipients.filter( (item) => item.failedAt && !item.deliveredAt ), diff --git a/services/firebase/package.json b/services/firebase/package.json index 461f22b4..04732ac7 100644 --- a/services/firebase/package.json +++ b/services/firebase/package.json @@ -40,6 +40,7 @@ "markdown-it": "^10.0.0", "mjml": "^4.13.0", "moment": "^2.29.4", + "moment-timezone": "^0.5.43", "node-fetch": "^3.2.10", "png-to-jpeg": "^1.0.1", "puppeteer": "^7.0.0", diff --git a/services/firebase/src/cli.ts b/services/firebase/src/cli.ts index 838a896f..5fc78fd8 100644 --- a/services/firebase/src/cli.ts +++ b/services/firebase/src/cli.ts @@ -15,7 +15,7 @@ const { hideBin } = require('yargs/helpers') import { firestore } from './firebase' import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' -import { scheduleWeeklyEmails } from './lib/digest' +import { scheduleWeeklyNewsletter } from './lib/digest' yargs(hideBin(process.argv)) .command( @@ -50,7 +50,10 @@ yargs(hideBin(process.argv)) () => undefined, async (argv: any) => { const event = ( - await firestore.collection('posts').doc(argv.eventId).get() + await firestore + .collection('posts') + .doc(argv.eventId) + .get() ).data() as any const result = await announceEventIG(event) @@ -176,7 +179,7 @@ yargs(hideBin(process.argv)) 'Generate Weekly Newsletter', () => undefined, async (argv: any) => { - await scheduleWeeklyEmails() + await scheduleWeeklyNewsletter() } ) .command( @@ -222,9 +225,8 @@ yargs(hideBin(process.argv)) for (const instance of instances) { console.log( - ` <${instance.username}> x ${instance.viewsCount} x ${ - instance.star?.count || 0 - } x ${instance.website}` + ` <${instance.username}> x ${instance.viewsCount} x ${instance + .star?.count || 0} x ${instance.website}` ) // if (!instance.website) { @@ -245,9 +247,12 @@ yargs(hideBin(process.argv)) for (const city of missingCityPlace) { console.log(`Updating cityPlaceId for ${city.name}`) - await firestore.collection('profiles').doc(city.id).update({ - cityPlaceId: city.place, - }) + await firestore + .collection('profiles') + .doc(city.id) + .update({ + cityPlaceId: city.place, + }) } } ) diff --git a/services/firebase/src/index.ts b/services/firebase/src/index.ts index 7a8ec495..67afe6b6 100644 --- a/services/firebase/src/index.ts +++ b/services/firebase/src/index.ts @@ -19,7 +19,6 @@ import { wrap } from './sentry' import { announceEvent } from './lib/telegram' import { announceEventIG } from './lib/instagram' import { getInstagramWebProfileInfo } from './lib/browser' -import { scheduleWeeklyEmails } from './lib/digest' require('dotenv').config() @@ -657,12 +656,12 @@ export const matchNotification = functions.firestore await sendEmail(data) }) -export const scheduleEmail = functions.pubsub - .schedule('every monday 18:00') - .timeZone('Europe/Berlin') - .onRun(async (context) => { - await scheduleWeeklyEmails() - }) +// export const scheduleEmail = functions.pubsub +// .schedule('every monday 18:00') +// .timeZone('Europe/Berlin') +// .onRun(async (context) => { +// await scheduleWeeklyEmails() +// }) // export const taskRunner = functions // .runWith({ memory: '2GB' }) diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 2a406d09..75f6670d 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -4,6 +4,7 @@ import mjml2html = require('mjml') import * as fs from 'fs' import * as moment from 'moment' import { firestore } from '../firebase' +import sendEmail from './sendEmail' export async function renderEmail(type: string, data: any, customUtms = {}) { const template = fs.readFileSync(`./src/templates/${type}.mjml`, 'utf8') @@ -53,11 +54,13 @@ export async function getWeeklyData(city: string) { time.setDate(time.getDate() + 7) const sevenDaysFromNow = time.toISOString().slice(0, 10) - const data = [] let cityProfile: any = {} const profileDocs = ( - await firestore.collection('profiles').where('username', '==', city).get() + await firestore + .collection('profiles') + .where('username', '==', city) + .get() ).docs for (const doc of profileDocs) { @@ -73,13 +76,34 @@ export async function getWeeklyData(city: string) { .get() ).docs + const days: any = {} + for (const doc of eventDocs) { const event = { id: doc.id, ...doc.data(), } as any - data.push(event) + const group = moment(event.startDate).format('YYYY-MM-DD') + + if (!days[group]) { + days[group] = { + day: moment(event.startDate).format('dddd'), + date: moment(event.startDate).format('D MMM'), + events: [], + } + } + + days[group].events.push({ + title: event.name, + organizer: event.org.name, + venue: event.venue?.name, + format: event.eventType, + time: moment(event.startDate).format('HH:mm'), + link: `https://wedance.vip/events/${event.id}`, + cover: event.cover, + styles: Object.keys(event.styles), + }) } const events: any = { @@ -93,22 +117,7 @@ export async function getWeeklyData(city: string) { addEvent: 'https://wedance.vip/events/-/edit', city: `https://wedance.vip/${city}`, }, - days: data.map((event) => ({ - day: moment(event.startDate).format('dddd'), - date: moment(event.startDate).format('D MMM'), - events: [ - { - title: event.name, - organizer: event.org.name, - venue: event.venue?.name, - format: event.eventType, - time: moment(event.startDate).format('hh:mm'), - link: event.link, - cover: event.cover, - styles: Object.keys(event.styles), - }, - ], - })), + days, } return events @@ -117,7 +126,7 @@ export async function getWeeklyData(city: string) { export async function getSubscribers(cityProfile: any) { const recipients = {} as any - const subscribers = Object.keys(cityProfile.watch?.list) + const subscribers = cityProfile.watch?.usernames || [] for (let subscriber of subscribers) { const profilesOfSubscriber = ( @@ -128,6 +137,7 @@ export async function getSubscribers(cityProfile: any) { ).docs if (profilesOfSubscriber.length !== 1) { + // console.log(`Subscriber ${subscriber} not found`) continue } @@ -139,6 +149,7 @@ export async function getSubscribers(cityProfile: any) { .get() if (!accountDoc.exists) { + // console.log(`Account for ${subscriber} not found`) continue } @@ -153,28 +164,18 @@ export async function getSubscribers(cityProfile: any) { return recipients } -function getNextMonday() { - const nextMonday = moment() - .add(1, 'weeks') - .day('Monday') - .hour(18) - .minute(0) - .second(0) - const formattedDate = nextMonday.format('MMMM DD, YYYY hh:mm A') - return formattedDate -} - -export async function scheduleWeeklyEmails() { +export async function scheduleWeeklyNewsletter() { const cityDocs = ( - await firestore.collection('profiles').where('type', '==', 'City').get() + await firestore + .collection('profiles') + .where('username', '==', 'Munich') + .get() ).docs - const nextMonday = getNextMonday() - for (let cityDoc of cityDocs) { const cityProfile = cityDoc.data() - if (!cityProfile.watch?.list) { + if (!cityProfile.watch?.usernames) { continue } @@ -182,15 +183,33 @@ export async function scheduleWeeklyEmails() { const html = await renderEmail('weekly', weeklyEmailDetails) const recipients = await getSubscribers(cityProfile) - await firestore.collection('emails').add({ + const emailDoc: any = { status: 'scheduled', - scheduledAt: nextMonday, + scheduledAt: moment() + .hour(18) + .minute(0) + .second(0), createdAt: Date.now(), from: `WeDance `, subject: 'Weekly Newsletter', recipients, - content: html, - }) + html, + } + + const emailRef = await firestore.collection('emails').add(emailDoc) + + emailDoc.id = emailRef.id + + await sendEmail(emailDoc) + + await firestore + .collection('emails') + .doc(emailDoc.id) + .update({ + status: 'sent', + processedAt: Date.now(), + error: '', + }) } return null diff --git a/services/firebase/src/lib/sendEmail.ts b/services/firebase/src/lib/sendEmail.ts index 1949693a..de1e0bb5 100644 --- a/services/firebase/src/lib/sendEmail.ts +++ b/services/firebase/src/lib/sendEmail.ts @@ -1,5 +1,4 @@ import * as mailgun from 'mailgun-js' -import { config } from 'firebase-functions' import * as MarkdownIt from 'markdown-it' const md = new MarkdownIt({ @@ -13,16 +12,10 @@ const getHtml = (content: any) => { } export default async (data: any) => { - const mailgunConfig = config().mailgun - - if (!mailgunConfig) { - throw new Error('Mailgun is not configured') - } - const mg = mailgun({ - apiKey: mailgunConfig.key, - domain: mailgunConfig.domain, - host: mailgunConfig.host, + apiKey: String(process.env.MAILGUN_KEY), + domain: String(process.env.MAILGUN_DOMAIN), + host: String(process.env.MAILGUN_HOST), }) const jobs: Promise[] = [] @@ -33,7 +26,7 @@ export default async (data: any) => { from: data.from, to: `${data.recipients[uid].name} <${data.recipients[uid].email}>`, subject: data.subject, - html: getHtml(data.content), + html: data.html || getHtml(data.content), 'v:uid': uid, 'v:campaignId': data.id, 'v:type': data.type, diff --git a/services/firebase/yarn.lock b/services/firebase/yarn.lock index ee68e75b..b4558516 100644 --- a/services/firebase/yarn.lock +++ b/services/firebase/yarn.lock @@ -3499,6 +3499,13 @@ module-alias@^2.2.2: resolved "https://registry.yarnpkg.com/module-alias/-/module-alias-2.2.2.tgz#151cdcecc24e25739ff0aa6e51e1c5716974c0e0" integrity sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q== +moment-timezone@^0.5.43: + version "0.5.43" + resolved "https://registry.yarnpkg.com/moment-timezone/-/moment-timezone-0.5.43.tgz#3dd7f3d0c67f78c23cd1906b9b2137a09b3c4790" + integrity sha512-72j3aNyuIsDxdF1i7CEgV2FfxM1r6aaqJyLB2vwb33mXYyoyLly+F1zbWqhA3/bVIoJ4szlUoMbUnVdid32NUQ== + dependencies: + moment "^2.29.4" + moment@^2.29.4: version "2.29.4" resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" From 59f029556ca392e655a6d1134f76b852a10a966d Mon Sep 17 00:00:00 2001 From: Aleksey Razbakov Date: Mon, 13 Nov 2023 17:37:37 +0100 Subject: [PATCH 19/20] fix emails --- services/firebase/src/lib/digest.ts | 37 ++++++++++++++++------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index 75f6670d..bfec5f92 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -5,6 +5,7 @@ import * as fs from 'fs' import * as moment from 'moment' import { firestore } from '../firebase' import sendEmail from './sendEmail' +import { orderBy } from 'lodash' export async function renderEmail(type: string, data: any, customUtms = {}) { const template = fs.readFileSync(`./src/templates/${type}.mjml`, 'utf8') @@ -49,11 +50,6 @@ export async function renderEmail(type: string, data: any, customUtms = {}) { } export async function getWeeklyData(city: string) { - const time = new Date() - const today = time.toISOString().slice(0, 10) - time.setDate(time.getDate() + 7) - const sevenDaysFromNow = time.toISOString().slice(0, 10) - let cityProfile: any = {} const profileDocs = ( @@ -67,23 +63,30 @@ export async function getWeeklyData(city: string) { cityProfile = { id: doc.id, ...doc.data() } } - const eventDocs = ( + const today = moment(new Date()) + const sevenDaysFromNow = moment(today).add(7, 'days') + + let eventDocs = ( await firestore .collection('posts') - .where('startDate', '>', today) - .where('startDate', '<', sevenDaysFromNow) + .where('startDate', '>', today.unix()) .where('place', '==', cityProfile.place) .get() - ).docs + ).docs.map((doc: any) => ({ id: doc.id, ...doc.data() })) + + eventDocs = orderBy(eventDocs, ['startDate'], ['asc']) + eventDocs = eventDocs.filter((event: any) => + ['Party', 'Festival', 'Show', 'Concert'].includes(event.eventType) + ) + eventDocs = eventDocs.filter( + (event: any) => + moment(event.startDate).unix() >= today.unix() && + moment(event.startDate).unix() <= sevenDaysFromNow.unix() + ) const days: any = {} - for (const doc of eventDocs) { - const event = { - id: doc.id, - ...doc.data(), - } as any - + for (const event of eventDocs) { const group = moment(event.startDate).format('YYYY-MM-DD') if (!days[group]) { @@ -126,7 +129,8 @@ export async function getWeeklyData(city: string) { export async function getSubscribers(cityProfile: any) { const recipients = {} as any - const subscribers = cityProfile.watch?.usernames || [] + // const subscribers = cityProfile.watch?.usernames || [] + const subscribers = ['alexrazbakov'] for (let subscriber of subscribers) { const profilesOfSubscriber = ( @@ -180,6 +184,7 @@ export async function scheduleWeeklyNewsletter() { } const weeklyEmailDetails = await getWeeklyData(cityProfile.username) + const html = await renderEmail('weekly', weeklyEmailDetails) const recipients = await getSubscribers(cityProfile) From 96d4b7549046981df85d341311aa3d350ce27071 Mon Sep 17 00:00:00 2001 From: Aleksey Razbakov Date: Mon, 13 Nov 2023 17:41:04 +0100 Subject: [PATCH 20/20] fix newsletter --- services/firebase/src/lib/digest.ts | 3 +-- services/firebase/src/templates/weekly.mjml | 8 ++++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/services/firebase/src/lib/digest.ts b/services/firebase/src/lib/digest.ts index bfec5f92..a056389a 100644 --- a/services/firebase/src/lib/digest.ts +++ b/services/firebase/src/lib/digest.ts @@ -129,8 +129,7 @@ export async function getWeeklyData(city: string) { export async function getSubscribers(cityProfile: any) { const recipients = {} as any - // const subscribers = cityProfile.watch?.usernames || [] - const subscribers = ['alexrazbakov'] + const subscribers = cityProfile.watch?.usernames || [] for (let subscriber of subscribers) { const profilesOfSubscriber = ( diff --git a/services/firebase/src/templates/weekly.mjml b/services/firebase/src/templates/weekly.mjml index b0b16cee..15930830 100644 --- a/services/firebase/src/templates/weekly.mjml +++ b/services/firebase/src/templates/weekly.mjml @@ -19,7 +19,7 @@ @@ -141,16 +141,16 @@ mode="horizontal" >