Skip to content

Easier to send broadcast emails - #275

Merged
gregv merged 4 commits into
mainfrom
develop
Aug 30, 2026
Merged

Easier to send broadcast emails#275
gregv merged 4 commits into
mainfrom
develop

Conversation

@gregv

@gregv gregv commented Aug 30, 2026

Copy link
Copy Markdown
Contributor

No description provided.

gregv and others added 4 commits August 29, 2026 21:29
…ls, contact pruning

- New api/broadcasts/ blueprint + services/broadcasts_service.py (all routes
  volunteer.admin-gated), porting scripts/sync_resend_audience.py loaders:
  profiles, volunteers (type/event/selected), leads, Slack members, and
  contact_submissions filtered by inquiryType (+ receiveUpdates opt-in)
- Segment sync runs as a daemon thread with redis status/lock (idempotent,
  create-only — never re-subscribes unsubscribed contacts); preview endpoint
  returns per-source counts + a contact-quota guardrail
  (RESEND_MARKETING_CONTACT_LIMIT, default 1000 = current free tier)
- Broadcast create/send with server-enforced unsubscribe footer and a
  from-address allowlist (RESEND_BROADCAST_FROM / _FROM_DOMAINS, defaulting
  to notify.ohack.dev — notifs.ohack.org verification is partially failed)
- POST /admin/broadcasts/batch-send: pre-personalized messages via
  resend.Batch.send in chunks of 100 (transactional quota), replacing the
  frontend's one-request-per-recipient loop; QR messages rejected per-recipient
- Contact management for quota reclaim: full inventory endpoint + background
  prune job (modes: unsubscribed / emails / all) deleting GLOBAL contacts
- userlist() in common/utils/slack.py is now redis-cached (10 min) above the
  rate limiter so varying active_days filters reuse one crawl;
  clear_slack_cache() clears the new prefix
- Segment/contact/broadcast ops require RESEND_API_KEY (no send-only-key
  fallback); everything is ENVIRONMENT=test-gated and Slack-audited
- Tests: api/broadcasts/tests/test_broadcasts_service.py (36 cases)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Resend broadcasts: segment sync, one-call mass email, batch sends, contact pruning
Every judge application with anything typed in "Additional availability
details" fired an ERROR ("CRITICAL: All patterns failed to match slot",
Sentry 80f5bb179a23474d843391277adf62ba) plus two WARNINGs. The .ics
calendar-attachment parser only understands the machine-generated slot
format the mentor/volunteer forms emit; the judge form's availability is
deliberately free text, so it failed all three patterns on every submit.
The request itself always succeeded — this was pure log noise.

- Early guard: availability with no "Weekday, Mon D" structured prefix
  logs INFO and returns [] (free text is expected input, not an error)
- Genuine structured-parse failures now log one WARNING per slot
  (was ERROR with a char-code dump); per-pattern cascade logs are DEBUG
- Call-site "no calendar attachments generated" downgraded to INFO
- Tests: free-text skip (the exact Sentry string) + structured
  two-slot regression

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…sentry-noise

Stop Sentry noise: judge free-text availability isn't a parse error
if not raw:
return None
e = str(raw).strip().lower()
return e if EMAIL_RE.match(e) else None
@gregv
gregv merged commit a316f9d into main Aug 30, 2026
9 of 10 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants