Send emails for new comments on markets you commented on or created.
This commit is contained in:
parent
a6657a28fd
commit
7522b6af3c
|
@ -28,6 +28,7 @@ export type PrivateUser = {
|
||||||
|
|
||||||
email?: string
|
email?: string
|
||||||
unsubscribedFromResolutionEmails?: boolean
|
unsubscribedFromResolutionEmails?: boolean
|
||||||
|
unsubscribedFromCommentEmails?: boolean
|
||||||
initialDeviceToken?: string
|
initialDeviceToken?: string
|
||||||
initialIpAddress?: string
|
initialIpAddress?: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
import _ = require('lodash')
|
import _ = require('lodash')
|
||||||
import { getProbability } from '../../common/calculate'
|
import { getProbability } from '../../common/calculate'
|
||||||
|
import { Comment } from '../../common/comment'
|
||||||
import { Contract } from '../../common/contract'
|
import { Contract } from '../../common/contract'
|
||||||
import { CREATOR_FEE } from '../../common/fees'
|
import { CREATOR_FEE } from '../../common/fees'
|
||||||
import { PrivateUser, User } from '../../common/user'
|
import { PrivateUser, User } from '../../common/user'
|
||||||
import { formatMoney, formatPercent } from '../../common/util/format'
|
import { formatMoney, formatPercent } from '../../common/util/format'
|
||||||
import { sendTemplateEmail, sendTextEmail } from './send-email'
|
import { sendTemplateEmail, sendTextEmail } from './send-email'
|
||||||
import { getPrivateUser, getUser } from './utils'
|
import { getPrivateUser, getUser, isProd } from './utils'
|
||||||
|
|
||||||
type market_resolved_template = {
|
type market_resolved_template = {
|
||||||
userId: string
|
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 }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ export * from './create-contract'
|
||||||
export * from './create-user'
|
export * from './create-user'
|
||||||
export * from './create-fold'
|
export * from './create-fold'
|
||||||
export * from './create-answer'
|
export * from './create-answer'
|
||||||
|
export * from './on-create-comment'
|
||||||
export * from './on-fold-follow'
|
export * from './on-fold-follow'
|
||||||
export * from './on-fold-delete'
|
export * from './on-fold-delete'
|
||||||
export * from './unsubscribe'
|
export * from './unsubscribe'
|
||||||
|
|
40
functions/src/on-create-comment.ts
Normal file
40
functions/src/on-create-comment.ts
Normal 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)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
})
|
|
@ -24,10 +24,11 @@ export const sendTemplateEmail = (
|
||||||
to: string,
|
to: string,
|
||||||
subject: string,
|
subject: string,
|
||||||
templateId: string,
|
templateId: string,
|
||||||
templateData: Record<string, string>
|
templateData: Record<string, string>,
|
||||||
|
options?: { from: string }
|
||||||
) => {
|
) => {
|
||||||
const data = {
|
const data = {
|
||||||
from: 'Manifold Markets <info@manifold.markets>',
|
from: options?.from ?? 'Manifold Markets <info@manifold.markets>',
|
||||||
to,
|
to,
|
||||||
subject,
|
subject,
|
||||||
template: templateId,
|
template: templateId,
|
||||||
|
|
|
@ -2,7 +2,7 @@ import * as functions from 'firebase-functions'
|
||||||
import * as admin from 'firebase-admin'
|
import * as admin from 'firebase-admin'
|
||||||
import Stripe from 'stripe'
|
import Stripe from 'stripe'
|
||||||
|
|
||||||
import { payUser } from './utils'
|
import { isProd, payUser } from './utils'
|
||||||
|
|
||||||
export type StripeTransaction = {
|
export type StripeTransaction = {
|
||||||
userId: string
|
userId: string
|
||||||
|
@ -18,8 +18,7 @@ const stripe = new Stripe(functions.config().stripe.apikey, {
|
||||||
})
|
})
|
||||||
|
|
||||||
// manage at https://dashboard.stripe.com/test/products?active=true
|
// manage at https://dashboard.stripe.com/test/products?active=true
|
||||||
const manticDollarStripePrice =
|
const manticDollarStripePrice = isProd
|
||||||
admin.instanceId().app.options.projectId === 'mantic-markets'
|
|
||||||
? {
|
? {
|
||||||
500: 'price_1KFQXcGdoFKoCJW770gTNBrm',
|
500: 'price_1KFQXcGdoFKoCJW770gTNBrm',
|
||||||
1000: 'price_1KFQp1GdoFKoCJW7Iu0dsF65',
|
1000: 'price_1KFQp1GdoFKoCJW7Iu0dsF65',
|
||||||
|
|
|
@ -1,30 +1,36 @@
|
||||||
import * as functions from 'firebase-functions'
|
import * as functions from 'firebase-functions'
|
||||||
import * as admin from 'firebase-admin'
|
import * as admin from 'firebase-admin'
|
||||||
import * as _ from 'lodash'
|
import * as _ from 'lodash'
|
||||||
import { getPrivateUser } from './utils'
|
import { getUser } from './utils'
|
||||||
import { PrivateUser } from '../../common/user'
|
import { PrivateUser } from '../../common/user'
|
||||||
|
|
||||||
export const unsubscribe = functions
|
export const unsubscribe = functions
|
||||||
.runWith({ minInstances: 1 })
|
.runWith({ minInstances: 1 })
|
||||||
.https.onRequest(async (req, res) => {
|
.https.onRequest(async (req, res) => {
|
||||||
let id = req.query.id as string
|
const { id, type } = req.query as { id: string; type: string }
|
||||||
if (!id) return
|
if (!id || !type) return
|
||||||
|
|
||||||
let privateUser = await getPrivateUser(id)
|
const user = await getUser(id)
|
||||||
|
|
||||||
if (privateUser) {
|
if (user) {
|
||||||
let { username } = privateUser
|
const { name } = user
|
||||||
|
|
||||||
const update: Partial<PrivateUser> = {
|
const update: Partial<PrivateUser> = {
|
||||||
unsubscribedFromResolutionEmails: true,
|
unsubscribedFromResolutionEmails: type === 'market-resolve',
|
||||||
|
unsubscribedFromCommentEmails: type === 'market-comment',
|
||||||
}
|
}
|
||||||
|
|
||||||
await firestore.collection('private-users').doc(id).update(update)
|
await firestore.collection('private-users').doc(id).update(update)
|
||||||
|
|
||||||
|
if (type === 'market-resolve')
|
||||||
res.send(
|
res.send(
|
||||||
username +
|
`${name}, you have been unsubscribed from market resolution emails on Manifold Markets.`
|
||||||
', 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 {
|
} else {
|
||||||
res.send('This user is not currently subscribed or does not exist.')
|
res.send('This user is not currently subscribed or does not exist.')
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,9 @@ import * as admin from 'firebase-admin'
|
||||||
import { Contract } from '../../common/contract'
|
import { Contract } from '../../common/contract'
|
||||||
import { PrivateUser, User } from '../../common/user'
|
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) => {
|
export const getValue = async <T>(collection: string, doc: string) => {
|
||||||
const snap = await admin.firestore().collection(collection).doc(doc).get()
|
const snap = await admin.firestore().collection(collection).doc(doc).get()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user