Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions services/firebase/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
91 changes: 6 additions & 85 deletions services/firebase/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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 <noreply@wedance.vip>`,
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' })
Expand Down
170 changes: 126 additions & 44 deletions services/firebase/src/lib/digest.ts
Original file line number Diff line number Diff line change
@@ -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 <noreply@wedance.vip>`,
subject: 'Weekly Newsletter',
recipients,
content: html,
})
}

return events;
return null
}