Let users email personal reports on demand
This commit is contained in:
@@ -82,23 +82,27 @@ def preferences(user: dict) -> dict:
|
||||
return {"state": state, "email": account.get("email"), "can_subscribe": ready and linked and bool(email),
|
||||
"detail": detail if not ready else "Save a valid email address in your profile." if not email else
|
||||
"Your Jellyfin account needs a saved identity link." if not linked else "Your monthly story, in your inbox.",
|
||||
"automatic_monthly": bool(sub["automatic_monthly"]) if sub else False,
|
||||
"can_send": ready and state == "enabled", "deliveries": store.personal_history(account["id"]),
|
||||
"schedule_enabled": config["enabled"], "next_send_at": config["next_send_at"],
|
||||
"day": config["day"], "hour": config["hour"], "timezone": "UTC",
|
||||
"resend_after": (sub["requested_at"] + 300) if sub else None}
|
||||
|
||||
|
||||
async def subscribe(user: dict) -> dict:
|
||||
async def subscribe(user: dict, automatic_monthly: bool | None = None) -> dict:
|
||||
account = current_account(user)
|
||||
preference = preferences(user)
|
||||
automatic = preference['automatic_monthly'] if automatic_monthly is None else automatic_monthly
|
||||
if preference["state"] == "enabled":
|
||||
return preference
|
||||
store.set_automatic(account['id'], automatic)
|
||||
return preferences(user)
|
||||
if not preference["can_subscribe"]:
|
||||
raise RecapError(preference["detail"])
|
||||
config = store.settings()
|
||||
runtime = get_runtime_settings()
|
||||
try:
|
||||
token = store.request_confirmation(account, source_key(runtime.jellyfin_base_url),
|
||||
linked_user_id(account["username"], runtime.jellyfin_base_url), time.time())
|
||||
linked_user_id(account["username"], runtime.jellyfin_base_url), time.time(), automatic)
|
||||
except ValueError as exc:
|
||||
raise RecapError(str(exc), 429) from exc
|
||||
url = f"{config['public_url']}/email-recaps#" + urlencode({"action": "confirm", "token": token})
|
||||
@@ -108,7 +112,7 @@ async def subscribe(user: dict) -> dict:
|
||||
mail.message_id(uuid.uuid4().hex, config["public_url"]))
|
||||
except mail.DeliveryError as exc:
|
||||
raise RecapError("Could not confirm delivery of the verification email. Check your inbox; you can request another in five minutes.", 502) from exc
|
||||
return {**preferences(user), "message": "Check your inbox and confirm within 24 hours to turn on monthly recaps."}
|
||||
return {**preferences(user), "message": "Check your inbox and confirm within 24 hours to enable personal report emails."}
|
||||
|
||||
|
||||
def token_action(token: str, action: str, *, apply: bool = False) -> dict:
|
||||
@@ -180,7 +184,7 @@ def eligible_delivery(delivery: dict) -> tuple[dict, dict]:
|
||||
if (not ready or not sub or sub["state"] != "enabled" or sub["version"] != delivery["subscription_version"]
|
||||
or sub["email"] != delivery["email"] or not binding_matches(sub, account)
|
||||
or config["public_url"] != delivery["public_url"]
|
||||
or (delivery["kind"] == "scheduled" and not config["enabled"])):
|
||||
or (delivery["kind"] == "scheduled" and (not config["enabled"] or not sub["automatic_monthly"]))):
|
||||
raise mail.DeliveryCancelled()
|
||||
return account, sub
|
||||
|
||||
@@ -190,10 +194,10 @@ async def process_delivery(delivery: dict) -> None:
|
||||
try:
|
||||
account, sub = eligible_delivery(delivery)
|
||||
report = await asyncio.wait_for(get_monthly_report(account, delivery["month"]), timeout=180)
|
||||
if report["state"] != "ready" or report["is_partial"]:
|
||||
if report["state"] != "ready" or (report["is_partial"] and delivery["kind"] != "on_demand"):
|
||||
raise mail.DeliveryError("failed", "A complete personal report is not available.")
|
||||
unsubscribe = f"{delivery['public_url']}/email-recaps#" + urlencode({"action": "unsubscribe", "token": sub["unsubscribe_token"]})
|
||||
rendered = mail.render_recap(report, account["username"], delivery["public_url"], unsubscribe, test=delivery["kind"] == "test")
|
||||
rendered = mail.render_recap(report, account["username"], delivery["public_url"], unsubscribe, test=delivery["kind"] == "test", requested=delivery["kind"] == "on_demand")
|
||||
|
||||
def before_data():
|
||||
eligible_delivery(delivery)
|
||||
@@ -241,3 +245,22 @@ async def run_email_recap_loop() -> None:
|
||||
except Exception as exc:
|
||||
logger.error("email recap worker failed type=%s", type(exc).__name__)
|
||||
await asyncio.sleep(30)
|
||||
|
||||
|
||||
def queue_personal(user: dict, month: str | None, request_id: str) -> dict:
|
||||
account = current_account(user)
|
||||
ready, detail = delivery_ready()
|
||||
if not ready:
|
||||
raise RecapError(detail)
|
||||
sub = active_subscription(account)
|
||||
if not sub or sub['state'] != 'enabled':
|
||||
raise RecapError('Confirm your profile email in email preferences before emailing a report.')
|
||||
try:
|
||||
selected = month_periods(month, datetime.now(timezone.utc))['month']
|
||||
except ValueError as exc:
|
||||
raise RecapError(str(exc), 422) from exc
|
||||
try:
|
||||
delivery_id = store.enqueue_test(sub, selected, request_id, store.settings()['public_url'], time.time(), 'on_demand')
|
||||
except ValueError as exc:
|
||||
raise RecapError(str(exc), 429) from exc
|
||||
return {'id': delivery_id, 'message': 'Your report is queued for your confirmed profile email. Delivery status appears below.'}
|
||||
|
||||
Reference in New Issue
Block a user