Skip to main content

Show an honest finish date before a large send

Free Intermediate

For anything that enqueues a lot of mail at once: a migration import, a big broadcast, an add-on’s own campaign. Before the send starts, tell the user when it will finish, and why.

Don’t work it out by dividing the total by Emails per hour. That setting is a ceiling, and a new sending address is held below it by the warm-up, so that arithmetic promises a date the queue can’t meet. WarmupRamp::capacity_over_next_days() walks the warm-up forward the way it will actually advance and returns days, finish_day, a per_day breakdown and complete.

  • It’s a best case. WP-Cron only fires when the site gets traffic, so a quiet site falls behind the projection. Always word it as “about”.
  • complete false means the projection ran out of days, not that the send will fail. Say “more than N days” rather than printing finish_day.
  • It counts what the address has already sent today, so asking twice on the same day can give two different answers, both correct.

describe() tells you why it’s slow, using the same reading as Settings → Email, so your advice agrees with Benecaster’s. Use it rather than a DNS lookup of your own. dns_checked false means the lookup didn’t answer, not that SPF and DMARC are missing, and there is no DKIM field because it can’t be checked.

If the send isn’t urgent, JobPaces::set() asks for a gentler pace for that job group (related_id plus email type). A pace can only ever send less: there is no faster pace, and the warm-up stays in charge either way. Benecaster clears the entry when the job completes.

Benecaster has no bounce or complaint feedback, so none of this measures whether mail reached inboxes. Don’t build a screen that implies it does.

Code

<?php
use Benecaster\Email\Warmup\JobPaces;
use Benecaster\Email\Warmup\SendingPace;
use Benecaster\Email\Warmup\WarmupRamp;

// $container is the Benecaster\Container your benecaster_boot callback received.
function my_addon_describe_send( \Benecaster\Container $container, int $count ): string {
    $ramp       = $container->make( WarmupRamp::class );
    $projection = $ramp->capacity_over_next_days( $count );

    if ( ! $projection['complete'] ) {
        /* translators: %d: number of days */
        $when = sprintf( __( 'This will take more than %d days.', 'my-addon' ), $projection['days'] );
    } else {
        $when = sprintf(
            /* translators: 1: number of days, 2: a date */
            __( 'About %1$d days. The last email should go out on %2$s.', 'my-addon' ),
            $projection['days'],
            $projection['finish_day']
        );
    }

    // Agree with Settings -> Email rather than inventing your own advice.
    if ( ! $ramp->describe()['has_service'] ) {
        $when .= ' ' . __( 'Connecting an email sending service will make this much faster.', 'my-addon' );
    }

    return $when;
}

// Not urgent? Ask for half the daily allowance for this job group.
function my_addon_send_gently( \Benecaster\Container $container, int $related_id ): void {
    $container->make( JobPaces::class )->set(
        $related_id,
        'my_addon_campaign',
        SendingPace::Gentle
    );
}

View on GitHub →

Need this built rather than just documented? See our services →