Plugin Directory

Changeset 3418432


Ignore:
Timestamp:
12/12/2025 05:02:23 PM (3 months ago)
Author:
manovermachine
Message:

Release 1.3.2: prevent duplicate scheduled runs and double posts

Location:
weather-write/trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • weather-write/trunk/includes/class-admin.php

    r3393864 r3418432  
    13461346                // Help: placeholders available to the model
    13471347                echo '<p class="description">' . esc_html__( 'Possible fields you can reference in your instructions (the AI will infer or be given these values):', 'weather-write') . '</p>';
     1348                // Use current date for examples to keep them fresh
     1349                $example_date = current_time( 'timestamp' );
     1350                $ex_numeric = wp_date( 'n/j/y', $example_date );
     1351                $ex_long = wp_date( 'F j, Y', $example_date );
     1352                $ex_abbrev = wp_date( 'M j', $example_date );
     1353                $ex_abbrev_dot = wp_date( 'M. j', $example_date );
     1354                $ex_iso = wp_date( 'Y-m-d', $example_date );
    13481355                echo '<ul style="margin:.3em 0 0 1.2em; list-style:disc"'
    1349                     . '<li><code>{date}</code> — ' . esc_html__( 'Numeric date like 8/18/25', 'weather-write') . '</li>'
    1350                     . '<li><code>{date_long}</code> — ' . esc_html__( 'Long date: August 2, 2025', 'weather-write') . '</li>'
    1351                     . '<li><code>{date_abbrev}</code> — ' . esc_html__( 'Abbreviated month: Aug 2', 'weather-write') . '</li>'
    1352                     . '<li><code>{date_abbrev_dot}</code> — ' . esc_html__( 'Abbreviated month with period: Aug. 2', 'weather-write') . '</li>'
    1353                     . '<li><code>{date_iso}</code> — ' . esc_html__( 'ISO style: 2025-08-02', 'weather-write') . '</li>'
     1356                    . '<li><code>{date}</code> — ' . sprintf( esc_html__( 'Numeric date like %s', 'weather-write'), $ex_numeric ) . '</li>'
     1357                    . '<li><code>{date_long}</code> — ' . sprintf( esc_html__( 'Long date: %s', 'weather-write'), $ex_long ) . '</li>'
     1358                    . '<li><code>{date_abbrev}</code> — ' . sprintf( esc_html__( 'Abbreviated month: %s', 'weather-write'), $ex_abbrev ) . '</li>'
     1359                    . '<li><code>{date_abbrev_dot}</code> — ' . sprintf( esc_html__( 'Abbreviated month with period: %s', 'weather-write'), $ex_abbrev_dot ) . '</li>'
     1360                    . '<li><code>{date_iso}</code> — ' . sprintf( esc_html__( 'ISO style: %s', 'weather-write'), $ex_iso ) . '</li>'
    13541361                    . '<li><code>{city}</code> / <code>{location}</code> — ' . esc_html__( 'Location/city name', 'weather-write') . '</li>'
    13551362                    . '<li><code>{condition}</code> — ' . esc_html__( 'Current weather summary', 'weather-write') . '</li>'
  • weather-write/trunk/includes/class-scheduler.php

    r3393864 r3418432  
    175175        $slot_time = $hm ? wp_date( 'Y-m-d H:i:s', strtotime( $hm ), $tz ) : wp_date( 'Y-m-d H:i:s' );
    176176        $location_key = wwrt_compute_location_key();
     177       
     178        // Deduplication guard: prevent multiple successful runs for the same date, time slot, and location.
     179        // This protects against double triggers from internal WP-Cron, external cron, or retries.
     180        $hm_key = ( is_string( $hm ) && preg_match( '/^\d{2}:\d{2}$/', $hm ) ) ? $hm : '';
     181        if ( $hm_key !== '' ) {
     182            // Normalize to the site-local date for this run
     183            $today_local = function_exists( 'wp_date' ) ? wp_date( 'Y-m-d', null, $tz ) : date_i18n( 'Y-m-d' );
     184            $dedupe_key  = 'wwrt_run_' . md5( $today_local . '|' . $hm_key . '|' . $location_key );
     185            if ( get_transient( $dedupe_key ) ) {
     186                // A run for this slot has already been executed recently; skip to avoid duplicate posts and API usage.
     187                wwrt_log_and_notify( 'duplicate_run', 'Duplicate run prevented for this time slot', [
     188                    'slot_time'    => $slot_time,
     189                    'location_key' => $location_key,
     190                    'run_id'       => $run_id,
     191                ], false );
     192                return new WP_Error( 'wwrt_duplicate_run', 'A run for this time slot has already been executed.' );
     193            }
     194            // Mark this slot as in-progress/done for a short window (15 minutes is plenty for a single run).
     195            set_transient( $dedupe_key, 'running', 15 * MINUTE_IN_SECONDS );
     196        }
    177197       
    178198        // CRITICAL: Use try/finally to ALWAYS schedule next run, even on failure
  • weather-write/trunk/readme.txt

    r3393864 r3418432  
    44Requires at least: 6.5
    55Tested up to: 6.8
    6 Stable tag: 1.3.1
     6Stable tag: 1.3.2
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
     
    8787
    8888== Changelog ==
     89
     90= 1.3.2 =
     91- CRITICAL FIX: Added slot-level deduplication to prevent duplicate scheduled runs for the same time and location
     92- RELIABILITY: Protects against double posts and double API usage when multiple triggers fire for the same slot
     93- TECHNICAL: Uses a transient key based on local date, schedule time, and location to short-circuit duplicate runs
    8994
    9095= 1.3.1 =
  • weather-write/trunk/weather-write.php

    r3393864 r3418432  
    33 * Plugin Name: Weather Write
    44 * Description: Generate and publish weather-aware posts with summaries, charts, images, alerts, SEO, and more — fully automated or on-demand.
    5  * Version: 1.3.1
     5 * Version: 1.3.2
    66 * Author: Mike Freeman - WeatherWrite
    77 * Plugin URI: https://www.weatherwrite.com/
Note: See TracChangeset for help on using the changeset viewer.