Files
RamVignesh B 6cf24731ce Feature/saajan persona (#3)
* feat: add template based email content (html + plaintext fallback)

* feat: init saajan component

* feat: add aesthetic noise background and implement Saajan component in register and login

* feat: add post seal modal for vault

* refactor: add proper props interfaces

* refactor: expose props on ui components

* feat: add ssajan in lots of flows

* fix: remove render test with no value and add aria helper for btn identification

* refactor: update email notification to account for proper arguments

* refactor:  refactor E2E auth helper and mail parsing logic

---------

Co-authored-by: ramvignesh-b <ramvignesh-b@github.com>
2026-04-28 20:51:23 +05:30

75 lines
2.4 KiB
Python

from datetime import UTC, datetime
import structlog
from apscheduler.schedulers.background import BackgroundScheduler
from django.core.mail import send_mail
from django.template.loader import render_to_string
from config import settings
from config.settings import FRONTEND_URLS
from letters.models import Letter
logger = structlog.get_logger(__name__)
def get_vault_letters_to_notify():
"""
Identifies the vault letters that have been recently unlocked and not notified
"""
return Letter.objects.filter(unlock_at__lt=datetime.now(UTC), notified_at=None)
def notify_unlocked_letter(letter):
"""
Notifies the author of the letter via email and if successful, updates the notified_at field for the letter.
"""
author = letter.user.get_username()
try:
letter_link = f"{FRONTEND_URLS[0]}/read/{letter.public_id}"
subject = "A letter. Written for this exact moment."
context = {
"pen_name": letter.user.first_name,
"cta": {"title": "View what you wrote", "link": letter_link},
"footnote": True,
}
plaint_content = render_to_string("email/vault_unlock.txt", context=context)
html_content = render_to_string("email/vault_unlock.html", context=context)
send_mail(
subject=subject,
message=plaint_content,
from_email=settings.FROM_EMAIL,
recipient_list=[author],
fail_silently=False,
html_message=html_content,
)
letter.notified_at = datetime.now(UTC)
letter.save()
logger.info(f"Successfully notified {author} of unlocked letter")
except Exception:
logger.exception(f"Failed to notify {author} of unlocked letter")
def vault_unlock_notification_polling_scheduler():
"""
Orchestrates the vault polling logic.
"""
letters_to_notify = get_vault_letters_to_notify()
for letter in letters_to_notify:
notify_unlocked_letter(letter)
def start_scheduler():
"""
Starts the background scheduler for polling and notifying vault letters.
"""
logger.info("Starting vault polling scheduler...")
scheduler = BackgroundScheduler()
scheduler.add_job(
vault_unlock_notification_polling_scheduler,
trigger="interval",
minutes=1,
id="letter_polling",
replace_existing=True,
)
scheduler.start()