email.ts 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import 'reflect-metadata'
  2. import 'newrelic'
  3. import { Stream } from 'stream'
  4. import { Logger } from 'winston'
  5. import * as dayjs from 'dayjs'
  6. import * as utc from 'dayjs/plugin/utc'
  7. import { UserRepositoryInterface } from '../src/Domain/User/UserRepositoryInterface'
  8. import { ContainerConfigLoader } from '../src/Bootstrap/Container'
  9. import TYPES from '../src/Bootstrap/Types'
  10. import { Env } from '../src/Bootstrap/Env'
  11. import { SettingServiceInterface } from '../src/Domain/Setting/SettingServiceInterface'
  12. import { DomainEventFactoryInterface } from '../src/Domain/Event/DomainEventFactoryInterface'
  13. import { UserSubscriptionRepositoryInterface } from '../src/Domain/Subscription/UserSubscriptionRepositoryInterface'
  14. import { DomainEventPublisherInterface } from '@standardnotes/domain-events'
  15. import { MuteMarketingEmailsOption, SettingName } from '@standardnotes/settings'
  16. import { EmailMessageIdentifier } from '@standardnotes/common'
  17. import { User } from '../src/Domain/User/User'
  18. import { EncryptionVersion } from '../src/Domain/Encryption/EncryptionVersion'
  19. import { TimerInterface } from '@standardnotes/time'
  20. const inputArgs = process.argv.slice(2)
  21. const emailMessageIdentifier = inputArgs[0]
  22. const sendEmailCampaign = async (
  23. userRepository: UserRepositoryInterface,
  24. settingService: SettingServiceInterface,
  25. userSubscriptionRepository: UserSubscriptionRepositoryInterface,
  26. timer: TimerInterface,
  27. domainEventFactory: DomainEventFactoryInterface,
  28. domainEventPublisher: DomainEventPublisherInterface,
  29. ): Promise<void> => {
  30. const stream = await userRepository.streamAll()
  31. return new Promise((resolve, reject) => {
  32. stream
  33. .pipe(
  34. new Stream.Transform({
  35. objectMode: true,
  36. transform: async (rawUserData, _encoding, callback) => {
  37. let emailsMutedSetting = await settingService.findSettingWithDecryptedValue({
  38. userUuid: rawUserData.user_uuid,
  39. settingName: SettingName.MuteMarketingEmails,
  40. })
  41. if (emailsMutedSetting === null) {
  42. const user = (await userRepository.findOneByUuid(rawUserData.user_uuid)) as User
  43. const { setting } = await settingService.createOrReplace({
  44. user,
  45. props: {
  46. name: SettingName.MuteMarketingEmails,
  47. unencryptedValue: MuteMarketingEmailsOption.NotMuted,
  48. serverEncryptionVersion: EncryptionVersion.Default,
  49. sensitive: false,
  50. },
  51. })
  52. emailsMutedSetting = setting
  53. }
  54. if (emailsMutedSetting.value === MuteMarketingEmailsOption.Muted) {
  55. callback()
  56. return
  57. }
  58. let activeSubscription = false
  59. let subscriptionPlanName = null
  60. const userSubscription = await userSubscriptionRepository.findOneByUserUuid(rawUserData.user_uuid)
  61. if (userSubscription !== null) {
  62. activeSubscription =
  63. !userSubscription.cancelled && userSubscription.endsAt > timer.getTimestampInMicroseconds()
  64. subscriptionPlanName = userSubscription.planName
  65. }
  66. await domainEventPublisher.publish(
  67. domainEventFactory.createEmailMessageRequestedEvent({
  68. userEmail: rawUserData.user_email,
  69. messageIdentifier: emailMessageIdentifier as EmailMessageIdentifier,
  70. context: {
  71. activeSubscription,
  72. subscriptionPlanName,
  73. muteEmailsSettingUuid: emailsMutedSetting.uuid,
  74. },
  75. }),
  76. )
  77. callback()
  78. },
  79. }),
  80. )
  81. .on('finish', resolve)
  82. .on('error', reject)
  83. })
  84. }
  85. const container = new ContainerConfigLoader()
  86. void container.load().then((container) => {
  87. dayjs.extend(utc)
  88. const env: Env = new Env()
  89. env.load()
  90. const logger: Logger = container.get(TYPES.Logger)
  91. logger.info(`Starting email campaign for email ${emailMessageIdentifier} ...`)
  92. if (!emailMessageIdentifier) {
  93. logger.error('No email message identifier passed as argument. Skipped sending.')
  94. process.exit(1)
  95. }
  96. const userRepository: UserRepositoryInterface = container.get(TYPES.UserRepository)
  97. const settingService: SettingServiceInterface = container.get(TYPES.SettingService)
  98. const userSubscriptionRepository: UserSubscriptionRepositoryInterface = container.get(
  99. TYPES.UserSubscriptionRepository,
  100. )
  101. const timer: TimerInterface = container.get(TYPES.Timer)
  102. const domainEventFactory: DomainEventFactoryInterface = container.get(TYPES.DomainEventFactory)
  103. const domainEventPublisher: DomainEventPublisherInterface = container.get(TYPES.DomainEventPublisher)
  104. Promise.resolve(
  105. sendEmailCampaign(
  106. userRepository,
  107. settingService,
  108. userSubscriptionRepository,
  109. timer,
  110. domainEventFactory,
  111. domainEventPublisher,
  112. ),
  113. )
  114. .then(() => {
  115. logger.info(`${emailMessageIdentifier} email campaign complete.`)
  116. process.exit(0)
  117. })
  118. .catch((error) => {
  119. logger.error(`Could not finish ${emailMessageIdentifier} email campaign: ${error.message}`)
  120. process.exit(1)
  121. })
  122. })