Each organizer is different, so the sender identity of ticket emails is
now per event rather than per instance:
- `extra.organizer_name` → From display name "Organizer via <site title>"
(site title alone when unset). The From address stays the instance
mailbox — that is what DKIM signs — so this costs nothing in mail auth.
- `extra.reply_to_email` → Reply-To; blank falls back to the email on the
LNbits account that owns the event wallet; no header when neither
exists. When a reply-to exists the body says "Questions? Reply to this
email and it reaches <organizer>", and "Organizer: <name>" is listed
with the ticket details.
Both fields sit next to the existing per-event subject/body in the admin
dialog. The organizer's own wording (in whatever language) remains
`notification_body`.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo
The v1.6.1-aio.9 mail still scored 8.4/10 on mail-tester: the remaining
deduction was HTML_IMAGE_ONLY (1.8) — an HTML part whose only content of
note is a remote <img>. Remote images are also blocked by default in most
clients until the reader opts in, and a bare QR saved from that mail says
nothing about what it opens.
- New `qr.py` module (QR + logo helpers moved out of views_api) with
`render_ticket_card`: site title, event name, when/where, the branded
QR, name on ticket, ticket id and the door instruction, laid out with
the bundled DejaVu Sans; `format_event_when` gives "Fri 19 Feb 2027,
16:00 - 20:00"; filenames are `ticket-<event-slug>-<id8>.png`.
- `GET /events/api/v1/ticket-card/{ticket_id}` serves the same PNG
(anonymous, like the QR endpoint); the email's "Ticket image" link
now points there.
- The ticket email becomes multipart/mixed: text + HTML alternatives
(URLs as links, no <img>) plus the card as a PNG attachment, which
clients show inline at the end of the message and which works offline
at the door.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYwoAkZZmXMMmaBp4WGUBo