From dfe834f70228047a5f73619ba1a54db69292a717 Mon Sep 17 00:00:00 2001 From: Giulio Rossi Date: Mon, 2 Dec 2019 16:50:46 -0800 Subject: [PATCH] Email confirmation and backfill --- functions/package-lock.json | 65 +++++++++++++++++++++ functions/package.json | 5 +- functions/src/index.ts | 111 +++++++++++++++++++++++++++++++++++- package-lock.json | 3 - 4 files changed, 179 insertions(+), 5 deletions(-) delete mode 100644 package-lock.json diff --git a/functions/package-lock.json b/functions/package-lock.json index 00b330d..e539b37 100644 --- a/functions/package-lock.json +++ b/functions/package-lock.json @@ -352,6 +352,21 @@ "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=", "optional": true }, + "@slack/types": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@slack/types/-/types-1.3.0.tgz", + "integrity": "sha512-3AjHsDJjJKT3q0hQzFHQN7piYIh99LuN7Po56W/R6P/uscqZqwS5xm1U1cTYGIzk8fmsuW7TvWVg0W85hKY/MQ==" + }, + "@slack/webhook": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@slack/webhook/-/webhook-5.0.2.tgz", + "integrity": "sha512-Ns35+KC//EWu+g+Bzp5m18V7qsCBdqAdvV4FJi2ng8+jto4rH2Bv2vUMmMioRn9LIiZ38LPsX/InrFjaO5XwGA==", + "requires": { + "@slack/types": "^1.1.0", + "@types/node": ">=8.9.0", + "axios": "^0.18.0" + } + }, "@types/body-parser": { "version": "1.17.0", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.0.tgz", @@ -439,6 +454,14 @@ "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.50.tgz", "integrity": "sha512-+ZbcUwJdaBgOZpwXeT0v+gHC/jQbEfzoc9s4d0rN0JIKeQbuTrT+A2n1aQY6LpZjrLXJT7avVUqiCecCJeeZxA==" }, + "@types/nodemailer": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/@types/nodemailer/-/nodemailer-6.2.2.tgz", + "integrity": "sha512-vDbSSe3+bBXYgibKs8duOrH7bhAv1hMHl3Vtdr3KmXZWLfi5WtIg3X6D/+K4SMK1RbNlm5QZBQCOldST/sVjjg==", + "requires": { + "@types/node": "*" + } + }, "@types/range-parser": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz", @@ -596,6 +619,15 @@ "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" }, + "axios": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.18.1.tgz", + "integrity": "sha512-0BfJq4NSfQXd+SkFdrvFbG7addhYSBA2mQwISr46pD6E5iqkWg02RAs8vyTT/j0RTnoYmeXauBuSv1qKwR179g==", + "requires": { + "follow-redirects": "1.5.10", + "is-buffer": "^2.0.2" + } + }, "balanced-match": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", @@ -1241,6 +1273,29 @@ "lodash": "^4.17.5" } }, + "follow-redirects": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.5.10.tgz", + "integrity": "sha512-0V5l4Cizzvqt5D44aTXbFZz+FtyXV1vrDN6qrelxtfYQKW0KO0W2T/hkE8xvGa/540LkZlkaUjO4ailYTFtHVQ==", + "requires": { + "debug": "=3.1.0" + }, + "dependencies": { + "debug": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", + "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", + "requires": { + "ms": "2.0.0" + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + } + } + }, "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", @@ -1659,6 +1714,11 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.0.tgz", "integrity": "sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==" }, + "is-buffer": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.4.tgz", + "integrity": "sha512-Kq1rokWXOPXWuaMAqZiJW4XxsmD9zGx9q4aePabbn3qCRGedtH7Cm+zV8WETitMfu1wdh+Rvd6w5egwSngUX2A==" + }, "is-obj": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz", @@ -2000,6 +2060,11 @@ "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-0.7.4.tgz", "integrity": "sha512-8Df0906+tq/omxuCZD6PqhPaQDYuyJ1d+VITgxoIA8zvQd1ru+nMJcDChHH324MWitIgbVkAkQoGEEVJNpn/PA==" }, + "nodemailer": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/nodemailer/-/nodemailer-6.3.1.tgz", + "integrity": "sha512-j0BsSyaMlyadEDEypK/F+xlne2K5m6wzPYMXS/yxKI0s7jmT1kBx6GEKRVbZmyYfKOsjkeC/TiMVDJBI/w5gMQ==" + }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", diff --git a/functions/package.json b/functions/package.json index 68da006..fb7c549 100644 --- a/functions/package.json +++ b/functions/package.json @@ -14,12 +14,15 @@ }, "main": "lib/index.js", "dependencies": { + "@slack/webhook": "^5.0.2", "@types/json2csv": "^4.5.0", + "@types/nodemailer": "^6.2.2", "cors": "^2.8.5", "firebase-admin": "^8.2.0", "firebase-functions": "^3.1.0", "json2csv": "^4.5.2", - "mailchimp-api-v3": "^1.13.0" + "mailchimp-api-v3": "^1.13.0", + "nodemailer": "^6.3.1" }, "devDependencies": { "firebase-functions-test": "^0.1.6", diff --git a/functions/src/index.ts b/functions/src/index.ts index 59de980..2b782bb 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -1,16 +1,125 @@ import * as functions from 'firebase-functions'; import * as admin from 'firebase-admin' import * as corsModule from "cors"; +import * as nodemailer from 'nodemailer' +import Mail = require('nodemailer/lib/mailer'); const cors = corsModule({origin: true}) -//import * as Parser from 'json2csv' +import * as Parser from 'json2csv' admin.initializeApp() const Mailchimp = require('mailchimp-api-v3'); const API_KEY: string = functions.config().mailchimp.key const adminPass = functions.config().cms.pass +const applicantUpdateUrl = functions.config().slack.applicant const mailchimp = new Mailchimp(API_KEY) // // Start writing Firebase Functions // // https://firebase.google.com/docs/functions/typescript // +const backFill = async () => { + const db = admin.firestore() + const hackers = await db.collection('hacker_info_2020').get() + await Promise.all(hackers.docs.map(doc => { + return doc.ref.update({ + id: doc.data().email + }) + })) +} + +const numberTracker = async () => { + const db = admin.firestore() + const length = (await db.collection('hacker_email_2020').get()).size + const dataCollection = db.collection('application_data').doc('nwHacks') + let dataDoc = await dataCollection.get() + if (!(dataDoc.exists)){ + console.log('creating data doc') + await dataCollection.create({ + size: 0, + lastSize: 0 + }) + } + await dataCollection.update({ + size: length + }) + dataDoc = await dataCollection.get() + const data = dataDoc.data() + if (!data){ return } + console.log(`last milestone was: ${data.lastSize} current size is: ${length}`) + if (length % 50 === 0){ + console.log('Logging new milestone...') + await dataCollection.update({ + lastSize: length, + [`milestones.${length}`]: admin.firestore.FieldValue.serverTimestamp() + }) + console.log('Messaging slack!') + const { IncomingWebhook } = require('@slack/webhook'); + const url = applicantUpdateUrl; + const webhook = new IncomingWebhook(url); + await webhook.send({ + text: `🎉🎉 There are now ${length} applicants for nwHacks!!! 🎉🎉`, + }); + } +} +const Email = async (email: String) => { + console.log(`Attempting to email ${email}`) + const transporter = nodemailer.createTransport({ + host: 'smtp.zoho.com', + port: 465, + secure: true, // use SSL + auth: { + user: 'logistics@nwplus.io', + pass: adminPass + } + }); + const mailOptions = { + from: 'logistics@nwplus.io', + to: email, + subject: 'Thank you for registering for nwHacks 2020!', + html: + 'nwHacks confirmation', + attachments: [{ + filename: 'Email_confirmation_banner.png', + path: './Email_confirmation_banner.png', + cid: 'email_confirm' //same cid value as in the html img src + }] + }; + return new Promise((resolve, reject) => { + transporter.sendMail(mailOptions as Mail.Options, function(error, info){ + if (error) { + console.log(error); + reject() + } else { + console.log(`Email sent to: ${email} with response ${info.response}`); + resolve() + } + }); + }) +} + +export const emailConfirmation = functions.firestore.document('hacker_info_2020/{hackerID}').onWrite(async (change, context) => { + // delete mail doc if document is deleted. + const db = admin.firestore() + if (!change.after.exists && change.before.exists) { + const oldData = change.before.data() + if (oldData){ + console.log(`Deleting applicant: ${oldData.email}`) + return db.collection('hacker_email_2020').doc(oldData.email).delete() + } + } + if (change.before.exists) { + console.log('Applicant already tracked/emailed.') + return + } + const data = change.after.data() + if ( data === undefined) return + await Email(data.email) + await numberTracker() + await change.after.ref.update({ + id: data.email, + timestamp: admin.firestore.FieldValue.serverTimestamp() + }) + console.log('Applicant setup!') + return true; +}) + export const subscribeToMailingList = functions.https.onRequest(async (request, response) => { return cors(request, response, async () => { if (request.body.email_address === '') { diff --git a/package-lock.json b/package-lock.json deleted file mode 100644 index 48e341a..0000000 --- a/package-lock.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "lockfileVersion": 1 -}