Send emails for new comments on markets you commented on or created.

This commit is contained in:
James Grugett 2022-02-22 19:35:25 -06:00
parent a6657a28fd
commit 7522b6af3c
8 changed files with 126 additions and 29 deletions

View File

@ -28,6 +28,7 @@ export type PrivateUser = {
email?: string
unsubscribedFromResolutionEmails?: boolean
unsubscribedFromCommentEmails?: boolean
initialDeviceToken?: string
initialIpAddress?: string
}

View File

@ -1,11 +1,12 @@
import _ = require('lodash')
import { getProbability } from '../../common/calculate'
import { Comment } from '../../common/comment'
import { Contract } from '../../common/contract'
import { CREATOR_FEE } from '../../common/fees'
import { PrivateUser, User } from '../../common/user'
import { formatMoney, formatPercent } from '../../common/util/format'
import { sendTemplateEmail, sendTextEmail } from './send-email'
import { getPrivateUser, getUser } from './utils'
import { getPrivateUser, getUser, isProd } from './utils'
type market_resolved_template = {
userId: string
@ -138,3 +139,48 @@ export const sendMarketCloseEmail = async (
}
)
}
export const sendNewCommentEmail = async (
userId: string,
commentCreator: User,
comment: Comment,
contract: Contract
) => {
const privateUser = await getPrivateUser(userId)
if (
!privateUser ||
privateUser.unsubscribedFromCommentEmails ||
!privateUser.email
)
return
const user = await getUser(userId)
if (!user) return
const { question, creatorUsername, slug } = contract
const marketUrl = `https://manifold.markets/${creatorUsername}/${slug}`
const unsubscribeUrl = `https://us-central1-${
isProd ? 'mantic-markets' : 'dev-mantic-markets'
}.cloudfunctions.net/unsubscribe?id=${userId}&type=market-comment`
const { name: commentorName, avatarUrl: commentorAvatarUrl } = commentCreator
const { text } = comment
const subject = `Comment on ${question}`
const from = `${commentorName} <info@manifold.markets>`
await sendTemplateEmail(
privateUser.email,
subject,
'market-comment',
{
commentorName,
commentorAvatarUrl: commentorAvatarUrl ?? '',
comment: text,
marketUrl,
unsubscribeUrl,
},
{ from }
)
}

View File

@ -12,6 +12,7 @@ export * from './create-contract'
export * from './create-user'
export * from './create-fold'
export * from './create-answer'
export * from './on-create-comment'
export * from './on-fold-follow'
export * from './on-fold-delete'
export * from './unsubscribe'

View File

@ -0,0 +1,40 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getContract, getUser, getValues } from './utils'
import { Comment } from '../../common/comment'
import { sendNewCommentEmail } from './emails'
const firestore = admin.firestore()
export const onCreateComment = functions.firestore
.document('contracts/{contractId}/comments/{commentId}')
.onCreate(async (change, context) => {
const { contractId } = context.params as {
contractId: string
}
const contract = await getContract(contractId)
if (!contract) return
const comment = change.data() as Comment
const commentCreator = await getUser(comment.userId)
if (!commentCreator) return
const comments = await getValues<Comment>(
firestore.collection('contracts').doc(contractId).collection('comments')
)
const recipientUserIds = _.uniq([
contract.creatorId,
...comments.map((comment) => comment.userId),
]).filter((id) => id !== comment.userId)
await Promise.all(
recipientUserIds.map((userId) =>
sendNewCommentEmail(userId, commentCreator, comment, contract)
)
)
})

View File

@ -24,10 +24,11 @@ export const sendTemplateEmail = (
to: string,
subject: string,
templateId: string,
templateData: Record<string, string>
templateData: Record<string, string>,
options?: { from: string }
) => {
const data = {
from: 'Manifold Markets <info@manifold.markets>',
from: options?.from ?? 'Manifold Markets <info@manifold.markets>',
to,
subject,
template: templateId,

View File

@ -2,7 +2,7 @@ import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import Stripe from 'stripe'
import { payUser } from './utils'
import { isProd, payUser } from './utils'
export type StripeTransaction = {
userId: string
@ -18,20 +18,19 @@ const stripe = new Stripe(functions.config().stripe.apikey, {
})
// manage at https://dashboard.stripe.com/test/products?active=true
const manticDollarStripePrice =
admin.instanceId().app.options.projectId === 'mantic-markets'
? {
500: 'price_1KFQXcGdoFKoCJW770gTNBrm',
1000: 'price_1KFQp1GdoFKoCJW7Iu0dsF65',
2500: 'price_1KFQqNGdoFKoCJW7SDvrSaEB',
10000: 'price_1KFQraGdoFKoCJW77I4XCwM3',
}
: {
500: 'price_1K8W10GdoFKoCJW7KWORLec1',
1000: 'price_1K8bC1GdoFKoCJW76k3g5MJk',
2500: 'price_1K8bDSGdoFKoCJW7avAwpV0e',
10000: 'price_1K8bEiGdoFKoCJW7Us4UkRHE',
}
const manticDollarStripePrice = isProd
? {
500: 'price_1KFQXcGdoFKoCJW770gTNBrm',
1000: 'price_1KFQp1GdoFKoCJW7Iu0dsF65',
2500: 'price_1KFQqNGdoFKoCJW7SDvrSaEB',
10000: 'price_1KFQraGdoFKoCJW77I4XCwM3',
}
: {
500: 'price_1K8W10GdoFKoCJW7KWORLec1',
1000: 'price_1K8bC1GdoFKoCJW76k3g5MJk',
2500: 'price_1K8bDSGdoFKoCJW7avAwpV0e',
10000: 'price_1K8bEiGdoFKoCJW7Us4UkRHE',
}
export const createCheckoutSession = functions
.runWith({ minInstances: 1 })

View File

@ -1,30 +1,36 @@
import * as functions from 'firebase-functions'
import * as admin from 'firebase-admin'
import * as _ from 'lodash'
import { getPrivateUser } from './utils'
import { getUser } from './utils'
import { PrivateUser } from '../../common/user'
export const unsubscribe = functions
.runWith({ minInstances: 1 })
.https.onRequest(async (req, res) => {
let id = req.query.id as string
if (!id) return
const { id, type } = req.query as { id: string; type: string }
if (!id || !type) return
let privateUser = await getPrivateUser(id)
const user = await getUser(id)
if (privateUser) {
let { username } = privateUser
if (user) {
const { name } = user
const update: Partial<PrivateUser> = {
unsubscribedFromResolutionEmails: true,
unsubscribedFromResolutionEmails: type === 'market-resolve',
unsubscribedFromCommentEmails: type === 'market-comment',
}
await firestore.collection('private-users').doc(id).update(update)
res.send(
username +
', you have been unsubscribed from market resolution emails on Manifold Markets.'
)
if (type === 'market-resolve')
res.send(
`${name}, you have been unsubscribed from market resolution emails on Manifold Markets.`
)
else if (type === 'market-comment')
res.send(
`${name}, you have been unsubscribed from market comment emails on Manifold Markets.`
)
else res.send(`${name}, you have been unsubscribed.`)
} else {
res.send('This user is not currently subscribed or does not exist.')
}

View File

@ -3,6 +3,9 @@ import * as admin from 'firebase-admin'
import { Contract } from '../../common/contract'
import { PrivateUser, User } from '../../common/user'
export const isProd =
admin.instanceId().app.options.projectId === 'mantic-markets'
export const getValue = async <T>(collection: string, doc: string) => {
const snap = await admin.firestore().collection(collection).doc(doc).get()