Skip to content

Let requesters reply to messages by e-mail #1861

Open

Description

@KOliver94

Requesters get an e-mail for every new message from the studio (email_user_new_comment in backend/video_requests/emails.py), but to answer they have to log in. Replying to that e-mail should add their answer to the request's message thread. Only for requesters; the crew keeps using the admin.

We have no mail server, but the domain's mail goes through Cloudflare Email Routing, which can hand incoming mail to a Worker's email() handler (docs). Cloudflare already sees this mail, so no new third party gets personal data.

Flow

  1. E-mails to the requester set Reply-To to a per-request address, e.g. valasz-<request id>-<mac>@…, instead of DEFAULT_REPLY_EMAIL.
  2. An Email Routing rule sends these addresses to the Worker: either the catch-all (the Worker forwards everything else to where the catch-all goes today) or a separate subdomain, so the existing rules stay untouched.
  3. The Worker only matches the address pattern and POSTs the raw MIME to a backend endpoint with a shared secret. On a 4xx answer it calls message.setReject(reason), so the sender gets a bounce with our text.
  4. The backend checks the MAC, loads the request, checks that the sender is the requester, parses the MIME with the standard email module, cuts the quoted history and creates Comment(internal=False, author=requester). Then it notifies the crew the same way CommentCreateUpdateSerializer does.

Parsing stays in the backend so the logic lives in one place and pytest covers it.

Address token

  • The token must be unguessable, not look random. <id>-<HMAC(secret, id) truncated to ~20 hex chars> needs no extra column and is an O(1) lookup; request IDs are already public in URLs. A salted hash without the ID would force the backend to hash every request or store the token.
  • Lowercase hex or base32 only: some mail systems lowercase addresses, so base64 and Django signing (case-sensitive, : separator) would break.

Gotchas

  • Spoofed From. Email Routing rejects mail that fails both SPF and DKIM and enforces the sender's DMARC policy (postmaster), but common providers like gmail.com publish p=none, so a forged header From can still get through. The token is the real secret; the sender check is a second factor. Worst case is a fake message in one thread that the crew sees. Check with a real mail whether the Worker gets an Authentication-Results header.
  • Compare the header From, not the envelope sender: forwarding and university relays often rewrite the envelope to a bounce address.
  • Quoted history is the hardest part. Replies contain the whole previous e-mail, so cut at Gmail and Outlook separators in Hungarian and English (… ezt írta:, On … wrote:, Feladó:/From: blocks, > lines). email-reply-parser only knows English. The alternative is a "write above this line" marker in our e-mails: reliable, but ugly. Collect real samples for tests.
  • Auto-replies: drop mail with Auto-Submitted other than no, Precedence: bulk or auto_reply, or an empty envelope sender, or out-of-office replies become messages.
  • HTML-only mail needs an HTML-to-text fallback. Attachments: ignore, or bounce with a pointer to the site.
  • Retries: the docs do not say whether an exception in the Worker is a temporary failure or a bounce. If the sender's server retries, the comment can be created twice; deduplicate on Message-ID if needed.
  • Reply-all: the request confirmation CCs DEFAULT_REPLY_EMAIL. If it also gets the token address, a reply-all reaches both.
  • New deployable: a Worker project, a wrangler deploy step and the shared secret on both sides.

Open decisions

  • Can unregistered requesters (is_active=False, who cannot log in) reply by e-mail? It would be their only way to answer, so probably yes.
  • Which e-mails get the token address: only new messages, or also the request confirmation and the video published e-mail?
  • Banned requesters: bounce or drop silently?
  • The address prefix (valasz-, uzenet-, …) shows in the requester's To field.

Nice to have, low priority.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions