This commit is contained in:
		
						commit
						d3c7f77076
					
				|  | @ -178,31 +178,44 @@ export const getNotificationDestinationsForUser = ( | ||||||
|   reason: notification_reason_types | notification_preference |   reason: notification_reason_types | notification_preference | ||||||
| ) => { | ) => { | ||||||
|   const notificationSettings = privateUser.notificationPreferences |   const notificationSettings = privateUser.notificationPreferences | ||||||
|   let destinations |  | ||||||
|   let subscriptionType: notification_preference | undefined |  | ||||||
|   if (Object.keys(notificationSettings).includes(reason)) { |  | ||||||
|     subscriptionType = reason as notification_preference |  | ||||||
|     destinations = notificationSettings[subscriptionType] |  | ||||||
|   } else { |  | ||||||
|     const key = reason as notification_reason_types |  | ||||||
|     subscriptionType = notificationReasonToSubscriptionType[key] |  | ||||||
|     destinations = subscriptionType |  | ||||||
|       ? notificationSettings[subscriptionType] |  | ||||||
|       : [] |  | ||||||
|   } |  | ||||||
|   const optOutOfAllSettings = notificationSettings['opt_out_all'] |  | ||||||
|   // Your market closure notifications are high priority, opt-out doesn't affect their delivery
 |  | ||||||
|   const optedOutOfEmail = |  | ||||||
|     optOutOfAllSettings.includes('email') && |  | ||||||
|     subscriptionType !== 'your_contract_closed' |  | ||||||
|   const optedOutOfBrowser = |  | ||||||
|     optOutOfAllSettings.includes('browser') && |  | ||||||
|     subscriptionType !== 'your_contract_closed' |  | ||||||
|   const unsubscribeEndpoint = getFunctionUrl('unsubscribe') |   const unsubscribeEndpoint = getFunctionUrl('unsubscribe') | ||||||
|   return { |   try { | ||||||
|     sendToEmail: destinations.includes('email') && !optedOutOfEmail, |     let destinations | ||||||
|     sendToBrowser: destinations.includes('browser') && !optedOutOfBrowser, |     let subscriptionType: notification_preference | undefined | ||||||
|     unsubscribeUrl: `${unsubscribeEndpoint}?id=${privateUser.id}&type=${subscriptionType}`, |     if (Object.keys(notificationSettings).includes(reason)) { | ||||||
|     urlToManageThisNotification: `${DOMAIN}/notifications?tab=settings§ion=${subscriptionType}`, |       subscriptionType = reason as notification_preference | ||||||
|  |       destinations = notificationSettings[subscriptionType] | ||||||
|  |     } else { | ||||||
|  |       const key = reason as notification_reason_types | ||||||
|  |       subscriptionType = notificationReasonToSubscriptionType[key] | ||||||
|  |       destinations = subscriptionType | ||||||
|  |         ? notificationSettings[subscriptionType] | ||||||
|  |         : [] | ||||||
|  |     } | ||||||
|  |     const optOutOfAllSettings = notificationSettings['opt_out_all'] | ||||||
|  |     // Your market closure notifications are high priority, opt-out doesn't affect their delivery
 | ||||||
|  |     const optedOutOfEmail = | ||||||
|  |       optOutOfAllSettings.includes('email') && | ||||||
|  |       subscriptionType !== 'your_contract_closed' | ||||||
|  |     const optedOutOfBrowser = | ||||||
|  |       optOutOfAllSettings.includes('browser') && | ||||||
|  |       subscriptionType !== 'your_contract_closed' | ||||||
|  |     return { | ||||||
|  |       sendToEmail: destinations.includes('email') && !optedOutOfEmail, | ||||||
|  |       sendToBrowser: destinations.includes('browser') && !optedOutOfBrowser, | ||||||
|  |       unsubscribeUrl: `${unsubscribeEndpoint}?id=${privateUser.id}&type=${subscriptionType}`, | ||||||
|  |       urlToManageThisNotification: `${DOMAIN}/notifications?tab=settings§ion=${subscriptionType}`, | ||||||
|  |     } | ||||||
|  |   } catch (e) { | ||||||
|  |     // Fail safely
 | ||||||
|  |     console.log( | ||||||
|  |       `couldn't get notification destinations for type ${reason} for user ${privateUser.id}` | ||||||
|  |     ) | ||||||
|  |     return { | ||||||
|  |       sendToEmail: false, | ||||||
|  |       sendToBrowser: false, | ||||||
|  |       unsubscribeUrl: '', | ||||||
|  |       urlToManageThisNotification: '', | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -12,7 +12,7 @@ import { getValueFromBucket } from '../../common/calculate-dpm' | ||||||
| import { formatNumericProbability } from '../../common/pseudo-numeric' | import { formatNumericProbability } from '../../common/pseudo-numeric' | ||||||
| 
 | 
 | ||||||
| import { sendTemplateEmail, sendTextEmail } from './send-email' | import { sendTemplateEmail, sendTextEmail } from './send-email' | ||||||
| import { contractUrl, getUser } from './utils' | import { contractUrl, getUser, log } from './utils' | ||||||
| import { buildCardUrl, getOpenGraphProps } from '../../common/contract-details' | import { buildCardUrl, getOpenGraphProps } from '../../common/contract-details' | ||||||
| import { notification_reason_types } from '../../common/notification' | import { notification_reason_types } from '../../common/notification' | ||||||
| import { Dictionary } from 'lodash' | import { Dictionary } from 'lodash' | ||||||
|  | @ -212,20 +212,16 @@ export const sendOneWeekBonusEmail = async ( | ||||||
|   user: User, |   user: User, | ||||||
|   privateUser: PrivateUser |   privateUser: PrivateUser | ||||||
| ) => { | ) => { | ||||||
|   if ( |   if (!privateUser || !privateUser.email) return | ||||||
|     !privateUser || |  | ||||||
|     !privateUser.email || |  | ||||||
|     !privateUser.notificationPreferences.onboarding_flow.includes('email') |  | ||||||
|   ) |  | ||||||
|     return |  | ||||||
| 
 | 
 | ||||||
|   const { name } = user |   const { name } = user | ||||||
|   const firstName = name.split(' ')[0] |   const firstName = name.split(' ')[0] | ||||||
| 
 | 
 | ||||||
|   const { unsubscribeUrl } = getNotificationDestinationsForUser( |   const { unsubscribeUrl, sendToEmail } = getNotificationDestinationsForUser( | ||||||
|     privateUser, |     privateUser, | ||||||
|     'onboarding_flow' |     'onboarding_flow' | ||||||
|   ) |   ) | ||||||
|  |   if (!sendToEmail) return | ||||||
| 
 | 
 | ||||||
|   return await sendTemplateEmail( |   return await sendTemplateEmail( | ||||||
|     privateUser.email, |     privateUser.email, | ||||||
|  | @ -247,19 +243,15 @@ export const sendCreatorGuideEmail = async ( | ||||||
|   privateUser: PrivateUser, |   privateUser: PrivateUser, | ||||||
|   sendTime: string |   sendTime: string | ||||||
| ) => { | ) => { | ||||||
|   if ( |   if (!privateUser || !privateUser.email) return | ||||||
|     !privateUser || |  | ||||||
|     !privateUser.email || |  | ||||||
|     !privateUser.notificationPreferences.onboarding_flow.includes('email') |  | ||||||
|   ) |  | ||||||
|     return |  | ||||||
| 
 | 
 | ||||||
|   const { name } = user |   const { name } = user | ||||||
|   const firstName = name.split(' ')[0] |   const firstName = name.split(' ')[0] | ||||||
|   const { unsubscribeUrl } = getNotificationDestinationsForUser( |   const { unsubscribeUrl, sendToEmail } = getNotificationDestinationsForUser( | ||||||
|     privateUser, |     privateUser, | ||||||
|     'onboarding_flow' |     'onboarding_flow' | ||||||
|   ) |   ) | ||||||
|  |   if (!sendToEmail) return | ||||||
|   return await sendTemplateEmail( |   return await sendTemplateEmail( | ||||||
|     privateUser.email, |     privateUser.email, | ||||||
|     'Create your own prediction market', |     'Create your own prediction market', | ||||||
|  | @ -279,22 +271,16 @@ export const sendThankYouEmail = async ( | ||||||
|   user: User, |   user: User, | ||||||
|   privateUser: PrivateUser |   privateUser: PrivateUser | ||||||
| ) => { | ) => { | ||||||
|   if ( |   if (!privateUser || !privateUser.email) return | ||||||
|     !privateUser || |  | ||||||
|     !privateUser.email || |  | ||||||
|     !privateUser.notificationPreferences.thank_you_for_purchases.includes( |  | ||||||
|       'email' |  | ||||||
|     ) |  | ||||||
|   ) |  | ||||||
|     return |  | ||||||
| 
 | 
 | ||||||
|   const { name } = user |   const { name } = user | ||||||
|   const firstName = name.split(' ')[0] |   const firstName = name.split(' ')[0] | ||||||
|   const { unsubscribeUrl } = getNotificationDestinationsForUser( |   const { unsubscribeUrl, sendToEmail } = getNotificationDestinationsForUser( | ||||||
|     privateUser, |     privateUser, | ||||||
|     'thank_you_for_purchases' |     'thank_you_for_purchases' | ||||||
|   ) |   ) | ||||||
| 
 | 
 | ||||||
|  |   if (!sendToEmail) return | ||||||
|   return await sendTemplateEmail( |   return await sendTemplateEmail( | ||||||
|     privateUser.email, |     privateUser.email, | ||||||
|     'Thanks for your Manifold purchase', |     'Thanks for your Manifold purchase', | ||||||
|  | @ -466,17 +452,13 @@ export const sendInterestingMarketsEmail = async ( | ||||||
|   contractsToSend: Contract[], |   contractsToSend: Contract[], | ||||||
|   deliveryTime?: string |   deliveryTime?: string | ||||||
| ) => { | ) => { | ||||||
|   if ( |   if (!privateUser || !privateUser.email) return | ||||||
|     !privateUser || |  | ||||||
|     !privateUser.email || |  | ||||||
|     !privateUser.notificationPreferences.trending_markets.includes('email') |  | ||||||
|   ) |  | ||||||
|     return |  | ||||||
| 
 | 
 | ||||||
|   const { unsubscribeUrl } = getNotificationDestinationsForUser( |   const { unsubscribeUrl, sendToEmail } = getNotificationDestinationsForUser( | ||||||
|     privateUser, |     privateUser, | ||||||
|     'trending_markets' |     'trending_markets' | ||||||
|   ) |   ) | ||||||
|  |   if (!sendToEmail) return | ||||||
| 
 | 
 | ||||||
|   const { name } = user |   const { name } = user | ||||||
|   const firstName = name.split(' ')[0] |   const firstName = name.split(' ')[0] | ||||||
|  | @ -620,18 +602,15 @@ export const sendWeeklyPortfolioUpdateEmail = async ( | ||||||
|   investments: PerContractInvestmentsData[], |   investments: PerContractInvestmentsData[], | ||||||
|   overallPerformance: OverallPerformanceData |   overallPerformance: OverallPerformanceData | ||||||
| ) => { | ) => { | ||||||
|   if ( |   if (!privateUser || !privateUser.email) return | ||||||
|     !privateUser || |  | ||||||
|     !privateUser.email || |  | ||||||
|     !privateUser.notificationPreferences.profit_loss_updates.includes('email') |  | ||||||
|   ) |  | ||||||
|     return |  | ||||||
| 
 | 
 | ||||||
|   const { unsubscribeUrl } = getNotificationDestinationsForUser( |   const { unsubscribeUrl, sendToEmail } = getNotificationDestinationsForUser( | ||||||
|     privateUser, |     privateUser, | ||||||
|     'profit_loss_updates' |     'profit_loss_updates' | ||||||
|   ) |   ) | ||||||
| 
 | 
 | ||||||
|  |   if (!sendToEmail) return | ||||||
|  | 
 | ||||||
|   const { name } = user |   const { name } = user | ||||||
|   const firstName = name.split(' ')[0] |   const firstName = name.split(' ')[0] | ||||||
|   const templateData: Record<string, string> = { |   const templateData: Record<string, string> = { | ||||||
|  | @ -656,4 +635,5 @@ export const sendWeeklyPortfolioUpdateEmail = async ( | ||||||
|       : 'portfolio-update', |       : 'portfolio-update', | ||||||
|     templateData |     templateData | ||||||
|   ) |   ) | ||||||
|  |   log('Sent portfolio update email to', privateUser.email) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,25 +1,33 @@ | ||||||
| import * as admin from 'firebase-admin' | import * as admin from 'firebase-admin' | ||||||
| 
 | 
 | ||||||
| import { initAdmin } from './script-init' | import { initAdmin } from './script-init' | ||||||
| import { getAllPrivateUsers } from 'functions/src/utils' | import { filterDefined } from 'common/lib/util/array' | ||||||
|  | import { getPrivateUser } from '../utils' | ||||||
| initAdmin() | initAdmin() | ||||||
| 
 | 
 | ||||||
| const firestore = admin.firestore() | const firestore = admin.firestore() | ||||||
| 
 | 
 | ||||||
| async function main() { | async function main() { | ||||||
|   const privateUsers = await getAllPrivateUsers() |   // const privateUsers = await getAllPrivateUsers()
 | ||||||
|  |   const privateUsers = filterDefined([ | ||||||
|  |     await getPrivateUser('ddSo9ALC15N9FAZdKdA2qE3iIvH3'), | ||||||
|  |   ]) | ||||||
|   await Promise.all( |   await Promise.all( | ||||||
|     privateUsers.map((privateUser) => { |     privateUsers.map((privateUser) => { | ||||||
|       if (!privateUser.id) return Promise.resolve() |       if (!privateUser.id) return Promise.resolve() | ||||||
|       return firestore |       if (privateUser.notificationPreferences.opt_out_all === undefined) { | ||||||
|         .collection('private-users') |         console.log('updating opt out all', privateUser.id) | ||||||
|         .doc(privateUser.id) |         return firestore | ||||||
|         .update({ |           .collection('private-users') | ||||||
|           notificationPreferences: { |           .doc(privateUser.id) | ||||||
|             ...privateUser.notificationPreferences, |           .update({ | ||||||
|             opt_out_all: [], |             notificationPreferences: { | ||||||
|           }, |               ...privateUser.notificationPreferences, | ||||||
|         }) |               opt_out_all: [], | ||||||
|  |             }, | ||||||
|  |           }) | ||||||
|  |       } | ||||||
|  |       return | ||||||
|     }) |     }) | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -112,13 +112,12 @@ export async function sendPortfolioUpdateEmailsToAllUsers() { | ||||||
|       ) |       ) | ||||||
|     ) |     ) | ||||||
|   ) |   ) | ||||||
|   log('Found', contractsUsersBetOn.length, 'contracts') |  | ||||||
|   let count = 0 |  | ||||||
|   await Promise.all( |   await Promise.all( | ||||||
|     privateUsersToSendEmailsTo.map(async (privateUser) => { |     privateUsersToSendEmailsTo.map(async (privateUser) => { | ||||||
|       const user = await getUser(privateUser.id) |       const user = await getUser(privateUser.id) | ||||||
|       // Don't send to a user unless they're over 5 days old
 |       // Don't send to a user unless they're over 5 days old
 | ||||||
|       if (!user || user.createdTime > Date.now() - 5 * DAY_MS) return |       if (!user || user.createdTime > Date.now() - 5 * DAY_MS) | ||||||
|  |         return await setEmailFlagAsSent(privateUser.id) | ||||||
|       const userBets = usersBets[privateUser.id] as Bet[] |       const userBets = usersBets[privateUser.id] as Bet[] | ||||||
|       const contractsUserBetOn = contractsUsersBetOn.filter((contract) => |       const contractsUserBetOn = contractsUsersBetOn.filter((contract) => | ||||||
|         userBets.some((bet) => bet.contractId === contract.id) |         userBets.some((bet) => bet.contractId === contract.id) | ||||||
|  | @ -219,13 +218,6 @@ export async function sendPortfolioUpdateEmailsToAllUsers() { | ||||||
|         (differences) => Math.abs(differences.profit) |         (differences) => Math.abs(differences.profit) | ||||||
|       ).reverse() |       ).reverse() | ||||||
| 
 | 
 | ||||||
|       log( |  | ||||||
|         'Found', |  | ||||||
|         investmentValueDifferences.length, |  | ||||||
|         'investment differences for user', |  | ||||||
|         privateUser.id |  | ||||||
|       ) |  | ||||||
| 
 |  | ||||||
|       const [winningInvestments, losingInvestments] = partition( |       const [winningInvestments, losingInvestments] = partition( | ||||||
|         investmentValueDifferences.filter( |         investmentValueDifferences.filter( | ||||||
|           (diff) => diff.pastValue > 0.01 && Math.abs(diff.profit) > 1 |           (diff) => diff.pastValue > 0.01 && Math.abs(diff.profit) > 1 | ||||||
|  | @ -245,29 +237,28 @@ export async function sendPortfolioUpdateEmailsToAllUsers() { | ||||||
|         usersToContractsCreated[privateUser.id].length === 0 |         usersToContractsCreated[privateUser.id].length === 0 | ||||||
|       ) { |       ) { | ||||||
|         log( |         log( | ||||||
|           'No bets in last week, no market movers, no markets created. Not sending an email.' |           `No bets in last week, no market movers, no markets created. Not sending an email to ${privateUser.email} .` | ||||||
|         ) |         ) | ||||||
|         await firestore.collection('private-users').doc(privateUser.id).update({ |         return await setEmailFlagAsSent(privateUser.id) | ||||||
|           weeklyPortfolioUpdateEmailSent: true, |  | ||||||
|         }) |  | ||||||
|         return |  | ||||||
|       } |       } | ||||||
|  |       // Set the flag beforehand just to be safe
 | ||||||
|  |       await setEmailFlagAsSent(privateUser.id) | ||||||
|       await sendWeeklyPortfolioUpdateEmail( |       await sendWeeklyPortfolioUpdateEmail( | ||||||
|         user, |         user, | ||||||
|         privateUser, |         privateUser, | ||||||
|         topInvestments.concat(worstInvestments) as PerContractInvestmentsData[], |         topInvestments.concat(worstInvestments) as PerContractInvestmentsData[], | ||||||
|         performanceData |         performanceData | ||||||
|       ) |       ) | ||||||
|       await firestore.collection('private-users').doc(privateUser.id).update({ |  | ||||||
|         weeklyPortfolioUpdateEmailSent: true, |  | ||||||
|       }) |  | ||||||
|       log('Sent weekly portfolio update email to', privateUser.email) |  | ||||||
|       count++ |  | ||||||
|       log('sent out emails to users:', count) |  | ||||||
|     }) |     }) | ||||||
|   ) |   ) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | async function setEmailFlagAsSent(privateUserId: string) { | ||||||
|  |   await firestore.collection('private-users').doc(privateUserId).update({ | ||||||
|  |     weeklyPortfolioUpdateEmailSent: true, | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| export type PerContractInvestmentsData = { | export type PerContractInvestmentsData = { | ||||||
|   questionTitle: string |   questionTitle: string | ||||||
|   questionUrl: string |   questionUrl: string | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user