cron.py 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. import argparse
  2. import arrow
  3. from app import s3
  4. from app.config import IGNORED_EMAILS, ADMIN_EMAIL
  5. from app.email_utils import send_email, send_trial_end_soon_email, render
  6. from app.extensions import db
  7. from app.log import LOG
  8. from app.models import (
  9. Subscription,
  10. User,
  11. Alias,
  12. EmailLog,
  13. Contact,
  14. CustomDomain,
  15. Client,
  16. ManualSubscription,
  17. RefusedEmail,
  18. )
  19. from server import create_app
  20. def notify_trial_end():
  21. for user in User.query.filter(
  22. User.activated == True, User.trial_end.isnot(None), User.lifetime == False
  23. ).all():
  24. if user.in_trial() and arrow.now().shift(
  25. days=3
  26. ) > user.trial_end >= arrow.now().shift(days=2):
  27. LOG.d("Send trial end email to user %s", user)
  28. send_trial_end_soon_email(user)
  29. def delete_refused_emails():
  30. for refused_email in RefusedEmail.query.filter(RefusedEmail.deleted == False).all():
  31. if arrow.now().shift(days=1) > refused_email.delete_at >= arrow.now():
  32. LOG.d("Delete refused email %s", refused_email)
  33. s3.delete(refused_email.path)
  34. s3.delete(refused_email.full_report_path)
  35. # do not set path and full_report_path to null
  36. # so we can check later that the files are indeed deleted
  37. refused_email.deleted = True
  38. db.session.commit()
  39. LOG.d("Finish delete_refused_emails")
  40. def notify_premium_end():
  41. """sent to user who has canceled their subscription and who has their subscription ending soon"""
  42. for sub in Subscription.query.filter(Subscription.cancelled == True).all():
  43. if (
  44. arrow.now().shift(days=3).date()
  45. > sub.next_bill_date
  46. >= arrow.now().shift(days=2).date()
  47. ):
  48. user = sub.user
  49. LOG.d(f"Send subscription ending soon email to user {user}")
  50. send_email(
  51. user.email,
  52. f"Your subscription will end soon {user.name}",
  53. render("transactional/subscription-end.txt", user=user),
  54. render("transactional/subscription-end.html", user=user),
  55. )
  56. def notify_manual_sub_end():
  57. for manual_sub in ManualSubscription.query.all():
  58. need_reminder = False
  59. if arrow.now().shift(days=14) > manual_sub.end_at > arrow.now().shift(days=13):
  60. need_reminder = True
  61. elif arrow.now().shift(days=4) > manual_sub.end_at > arrow.now().shift(days=3):
  62. need_reminder = True
  63. if need_reminder:
  64. user = manual_sub.user
  65. LOG.debug("Remind user %s that their manual sub is ending soon", user)
  66. send_email(
  67. user.email,
  68. f"Your trial will end soon {user.name}",
  69. render(
  70. "transactional/manual-subscription-end.txt",
  71. name=user.name,
  72. user=user,
  73. manual_sub=manual_sub,
  74. ),
  75. render(
  76. "transactional/manual-subscription-end.html",
  77. name=user.name,
  78. user=user,
  79. manual_sub=manual_sub,
  80. ),
  81. )
  82. def stats():
  83. """send admin stats everyday"""
  84. if not ADMIN_EMAIL:
  85. # nothing to do
  86. return
  87. # nb user
  88. q = User.query
  89. for ie in IGNORED_EMAILS:
  90. q = q.filter(~User.email.contains(ie))
  91. nb_user = q.count()
  92. LOG.d("total number user %s", nb_user)
  93. # nb gen emails
  94. q = db.session.query(Alias, User).filter(Alias.user_id == User.id)
  95. for ie in IGNORED_EMAILS:
  96. q = q.filter(~User.email.contains(ie))
  97. nb_alias = q.count()
  98. LOG.d("total number alias %s", nb_alias)
  99. # nb mails forwarded
  100. q = db.session.query(EmailLog, Contact, Alias, User).filter(
  101. EmailLog.contact_id == Contact.id,
  102. Contact.alias_id == Alias.id,
  103. Alias.user_id == User.id,
  104. )
  105. for ie in IGNORED_EMAILS:
  106. q = q.filter(~User.email.contains(ie))
  107. nb_forward = nb_block = nb_reply = 0
  108. for fel, _, _, _ in q:
  109. if fel.is_reply:
  110. nb_reply += 1
  111. elif fel.blocked:
  112. nb_block += 1
  113. else:
  114. nb_forward += 1
  115. LOG.d("nb forward %s, nb block %s, nb reply %s", nb_forward, nb_block, nb_reply)
  116. nb_premium = Subscription.query.count()
  117. nb_custom_domain = CustomDomain.query.count()
  118. nb_custom_domain_alias = Alias.query.filter(
  119. Alias.custom_domain_id.isnot(None)
  120. ).count()
  121. nb_disabled_alias = Alias.query.filter(Alias.enabled == False).count()
  122. nb_app = Client.query.count()
  123. today = arrow.now().format()
  124. send_email(
  125. ADMIN_EMAIL,
  126. subject=f"SimpleLogin Stats for {today}, {nb_user} users, {nb_alias} aliases, {nb_forward} forwards",
  127. plaintext="",
  128. html=f"""
  129. Stats for {today} <br>
  130. nb_user: {nb_user} <br>
  131. nb_premium: {nb_premium} <br>
  132. nb_alias: {nb_alias} <br>
  133. nb_disabled_alias: {nb_disabled_alias} <br>
  134. nb_custom_domain: {nb_custom_domain} <br>
  135. nb_custom_domain_alias: {nb_custom_domain_alias} <br>
  136. nb_forward: {nb_forward} <br>
  137. nb_reply: {nb_reply} <br>
  138. nb_block: {nb_block} <br>
  139. nb_app: {nb_app} <br>
  140. """,
  141. )
  142. if __name__ == "__main__":
  143. LOG.d("Start running cronjob")
  144. parser = argparse.ArgumentParser()
  145. parser.add_argument(
  146. "-j",
  147. "--job",
  148. help="Choose a cron job to run",
  149. type=str,
  150. choices=[
  151. "stats",
  152. "notify_trial_end",
  153. "notify_manual_subscription_end",
  154. "notify_premium_end",
  155. "delete_refused_emails",
  156. ],
  157. )
  158. args = parser.parse_args()
  159. app = create_app()
  160. with app.app_context():
  161. if args.job == "stats":
  162. LOG.d("Compute Stats")
  163. stats()
  164. elif args.job == "notify_trial_end":
  165. LOG.d("Notify users with trial ending soon")
  166. notify_trial_end()
  167. elif args.job == "notify_manual_subscription_end":
  168. LOG.d("Notify users with manual subscription ending soon")
  169. notify_manual_sub_end()
  170. elif args.job == "notify_premium_end":
  171. LOG.d("Notify users with premium ending soon")
  172. notify_premium_end()
  173. elif args.job == "delete_refused_emails":
  174. LOG.d("Deleted refused emails")
  175. delete_refused_emails()