diff --git a/common/contract.ts b/common/contract.ts index ed642cfa..f5733059 100644 --- a/common/contract.ts +++ b/common/contract.ts @@ -28,6 +28,7 @@ export type Contract = { resolutionTime?: number // When the contract creator resolved the market resolution?: outcome // Chosen by creator; must be one of outcomes resolutionProbability?: number + closeEmailsSent?: number volume24Hours: number volume7Days: number diff --git a/functions/src/emails.ts b/functions/src/emails.ts index 15f1e8b9..11560522 100644 --- a/functions/src/emails.ts +++ b/functions/src/emails.ts @@ -87,3 +87,26 @@ Best, Austin from Manifold` ) } + +export const sendMarketCloseEmail = async ( + user: User, + privateUser: PrivateUser, + contract: Contract +) => { + const firstName = user.name.split(' ')[0] + const url = `https://manifold.markets/${user.username}/${contract.slug}` + + await sendTextEmail( + privateUser.email || '', + 'Your market has closed', + `Hi ${firstName}, + +Trading for one of your markets has ended. Please resolve it as soon as possible. + +Question: ${contract.question} +Url: ${url} + +Best, +Austin from Manifold` + ) +} diff --git a/functions/src/index.ts b/functions/src/index.ts index f46e72a8..202136c9 100644 --- a/functions/src/index.ts +++ b/functions/src/index.ts @@ -18,3 +18,4 @@ export * from './update-contract-metrics' export * from './update-user-metrics' export * from './backup-db' export * from './change-user-info' +export * from './market-close-emails' diff --git a/functions/src/market-close-emails.ts b/functions/src/market-close-emails.ts new file mode 100644 index 00000000..84efa190 --- /dev/null +++ b/functions/src/market-close-emails.ts @@ -0,0 +1,59 @@ +import * as functions from 'firebase-functions' +import * as admin from 'firebase-admin' + +import { Contract } from '../../common/contract' +import { getPrivateUser, getUserByUsername } from './utils' +import { sendMarketCloseEmail } from './emails' + +export const marketCloseEmails = functions.pubsub + .schedule('every 2 minutes') + .onRun(async () => { + await sendMarketCloseEmails() + }) + +const firestore = admin.firestore() + +async function sendMarketCloseEmails() { + const contracts = await firestore.runTransaction(async (transaction) => { + const snap = await transaction.get( + firestore.collection('contracts').where('isResolved', '!=', true) + ) + + return snap.docs + .map((doc) => { + const contract = doc.data() as Contract + + if ( + contract.resolution || + (contract.closeEmailsSent ?? 0) >= 1 || + (contract.closeTime ?? 0) > Date.now() + ) + return undefined + + transaction.update(doc.ref, { + closeEmailsSent: contract.closeEmailsSent ?? 0 + 1, + }) + + return contract + }) + .filter((x) => !!x) as Contract[] + }) + + for (let contract of contracts) { + console.log( + 'sending close email for', + contract.slug, + 'closed', + contract.closeTime + ) + + const user = await getUserByUsername(contract.creatorUsername) + if (!user) continue + + const privateUser = await getPrivateUser(user.id) + console.log('private user', privateUser) + if (!privateUser) continue + + await sendMarketCloseEmail(user, privateUser, contract) + } +}