Skip to content

Send the weekly AutoJack newsletter (off until enabled) - #15

Open
jack-arturo wants to merge 1 commit into
mainfrom
conventional-commit-prefix/weekly-newsletter-32b8
Open

jack-arturo wants to merge 1 commit into
mainfrom
conventional-commit-prefix/weekly-newsletter-32b8

Conversation

@jack-arturo

Copy link
Copy Markdown
Member

What was already there

The weekly issue never went out because nothing in this repo, or in the signup worker, collects posts and sends them.

Signup lives in the theme, the list does not.

  • The form is inc/newsletter.php plus NewsletterSignup in assets/js/theme.js. It POSTs { email, company } to https://news.autojack.ai/subscribe. Aside, footer, and the mid-post ajn-box all use that endpoint. The old Fluent Snippet is gone; the theme owns the markup.
  • The endpoint is the Cloudflare Worker autojack-newsletter on the Very Good Plugins account (script eacb2b8feee74f8b870b4be12d594f2b, last deployed 2026-08-20). That source is not in this repo. It only implements POST /subscribe, GET /confirm, and GET /unsubscribe.
  • It is not WP Fusion, not a CRM plugin, and not a WordPress table or option. Contacts are a Resend audience (NEWSLETTER_AUDIENCE_ID on the worker).
  • Transport is the Resend API (https://api.resend.com), from AutoJack <newsletter@news.autojack.ai>. Not wp_mail, and not an SMTP plugin.
  • Signup upserts the contact with unsubscribed: true (pending) and sends the confirm mail. Confirm flips unsubscribed to false and sends the welcome HTML from the worker’s emailShell (paper #f3efe6, hot #e3ff04, Georgia masthead, AutoJack portrait, Berlin footer). Unsubscribe flips the flag back to true. There is no separate “confirmed” column: confirmed means unsubscribed === false. Pending and unsubscribed addresses are true.

What this adds

The digest is a theme module, next to the form, because the posts live in WordPress. inc/newsletter-digest.php is pure (window, plan, HMAC tokens, HTML/text). inc/newsletter-weekly.php wires WP-Cron or Action Scheduler, Resend, Settings → Newsletter, and wp minimalcode newsletter.

The worker stays the system of record for signup, confirm, and the welcome mail. Weekly mail uses the same audience and the same HMAC unsubscribe tokens (NEWSLETTER_CONFIRM_SECRET). The visible link and List-Unsubscribe point at /newsletter/unsubscribe/ on the site, which PATCHes the Resend contact and accepts POST for one-click (RFC 8058). The worker only implements GET, so the weekly issue cannot one-click through it.

Each issue is one email per confirmed address (no shared To/Cc), with title, excerpt, and link per post, plus the welcome header and footer. Plain text is sent alongside HTML. A week with no posts is skipped and recorded. The same week cannot be claimed twice. Sends run in batches of 10, half a second apart, then the next batch 45 seconds later. One recipient’s error is logged and the rest continue. HTTP 429 pauses the batch. A partial run is marked sent so the successes are not mailed again; if everyone failed, the claim is released so Send now can retry. The log stores date, post ids, recipient count, and failures (failure addresses stay in an admin-only option).

Automatic sending ships off. Merging this does not email anyone. Enabling the toggle arms the next slot; it does not send the current week. Send now is a separate button (and wp minimalcode newsletter send asks unless --yes). Test sends go to one address, are prefixed [test], and do not claim the week. CI / GITHUB_ACTIONS refuses every Resend call unless MINIMALCODE_NEWSLETTER_ALLOW_SEND is set. Tests never load the sender.

Schedule and how to turn it on

Default slot is Monday 08:00 in the site timezone (Settings → General). Weekday and time are editable. The window is the previous complete Monday 00:00 through Sunday 23:59:59, so a Monday morning send covers the week that just ended.

Scheduling uses wp_schedule_single_event on minimalcode_newsletter_weekly, or Action Scheduler’s as_schedule_single_action when that plugin is loaded — not both. After each run the next slot is queued. If the slot was missed while the feature was already on, it is caught up for 18 hours. A slot from before the toggle was enabled is not caught up.

WP-Cron only runs when something hits WordPress. On a quiet Monday the issue waits. On the server, every five minutes:

*/5 * * * * curl -s https://drunk.support/wp-cron.php?doing_wp_cron >/dev/null 2>&1

or wp cron event run --due-now.

To enable, copy the worker’s env into wp-config.php (do not commit them):

define( 'MINIMALCODE_RESEND_API_KEY', '...' );
define( 'MINIMALCODE_NEWSLETTER_AUDIENCE_ID', '...' );       // NEWSLETTER_AUDIENCE_ID
define( 'MINIMALCODE_NEWSLETTER_CONFIRM_SECRET', '...' );    // NEWSLETTER_CONFIRM_SECRET, 16+ chars
define( 'MINIMALCODE_NEWSLETTER_FROM', 'AutoJack <newsletter@news.autojack.ai>' ); // optional
define( 'MINIMALCODE_NEWSLETTER_SEGMENT_ID', '...' );        // optional, only if the audience id was migrated

The secret must be the worker’s NEWSLETTER_CONFIRM_SECRET so tokens match. Then Settings → Newsletter, check Email confirmed subscribers every week, and save. Preview is on that screen (wp minimalcode newsletter preview prints the same HTML). The list is only queried at send time, and only that audience or an explicit segment — never every contact on the Resend account.

Set the site timezone to Europe/Berlin if it is not already; 08:00 is the site clock, not a hardcoded Berlin clock.

Preview

Rendered by minimalcode_newsletter_render_html() for the week of 2 Nov 2026 (Mon–Sun before this Monday): the four posts that were actually on the log. Unsubscribe target is the preview token, so it does not change anyone’s subscription. Nothing was sent.

Weekly issue for 2 Nov 2026

Subject: This week on drunk.support — 4 new posts.

Tests

tests/newsletter-weekly.php covers the default-off flag, the Mon–Sun window, the Monday 08:00 slot, the worker HMAC vector, confirmed-only filtering, empty-week skip, duplicate-week skip, batching, CI refusal, List-Unsubscribe, and HTML escaping. No network. .github/workflows/test.yml runs php -l and the existing theme tests on pull requests. The deploy workflow is unchanged and still only runs on main.

To show artifacts inline, enable in settings.

Open in Web Open in Cursor 

Compose the previous Monday–Sunday posts into an issue that matches the
welcome email, and mail confirmed Resend contacts in batches. The weekly
cron stays off until Settings → Newsletter enables it.

Co-authored-by: Jack Arturo <info@verygoodplugins.com>
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Nov 9, 2026 •

Copy link
Copy Markdown
Codex Review Summary: ✅ Completed

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-11-09T21:12:43.696317Z 47d9dbb PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 47d9dbb58b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread inc/newsletter-weekly.php
Comment on lines +740 to +741
foreach ( $slice as $index => $email ) {
$result = minimalcode_newsletter_deliver_one( $state, $email );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor unsubscribes before delivering queued recipients

When a subscriber unsubscribes after the recipient list is captured but before their batch runs, the unsubscribe handler updates Resend, but this loop still sends to the saved address without checking its current status. With batches spread across cron requests, that interval can be substantial. A stubbed execution of the unsubscribe update followed by batch processing confirmed that the code still posts an email to the unsubscribed address. Recheck subscription status before delivery or maintain a suppression mechanism that queued batches consult.

Useful? React with 👍 / 👎.

Comment thread inc/newsletter-weekly.php
Comment on lines +939 to +940
update_option( 'minimalcode_newsletter_active', $state, false );
minimalcode_newsletter_process_batch( $plan['week_id'] );

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Prevent a new issue from overwriting an unfinished send

If an issue is still queued when another week's send starts—for example, a manual Sunday-night send followed by the Monday automatic slot—the per-week claim allows both runs, but this unconditional update replaces the only active state with the newer issue. Remaining callbacks for the old week then return because their week ID no longer matches, leaving its remaining subscribers unsent and its claim permanently marked sending. Refuse or defer a new issue while another week is active, or store active state separately for each week.

Useful? React with 👍 / 👎.

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