$schedules Schedules. * @return array */ public static function schedules( $schedules ) { $schedules = is_array( $schedules ) ? $schedules : array(); $schedules[ self::SCHEDULE ] = array( 'interval' => 10 * MINUTE_IN_SECONDS, // Not translated: cron_schedules can run before translations load. 'display' => 'Every 10 minutes (Vibrate SMS)', ); return $schedules; } /** * Schedule the recurring job if it is missing. */ public static function ensure_scheduled(): void { if ( ! wp_next_scheduled( self::HOOK ) ) { wp_schedule_event( time() + 5 * MINUTE_IN_SECONDS, self::SCHEDULE, self::HOOK ); } } /** * Remove scheduled jobs (deactivation, uninstall). */ public static function unschedule(): void { wp_clear_scheduled_hook( self::HOOK ); wp_unschedule_hook( Messenger::DISPATCH_HOOK ); if ( function_exists( 'as_unschedule_all_actions' ) && did_action( 'action_scheduler_init' ) ) { as_unschedule_all_actions( Messenger::DISPATCH_HOOK ); } } /** * The recurring job. */ public static function run(): void { $started = time(); self::recover(); self::refresh( self::RUNS_PER_TICK, $started ); self::purge(); } /** * Re-dispatch orphaned batches, or give up on ones that are too old. */ public static function recover(): void { $give_up_before = time() - self::GIVE_UP_AFTER; foreach ( Log::stale_sending( self::STALE_SENDING ) as $batch ) { $key = (string) $batch->batch_key; if ( strtotime( $batch->created_at . ' UTC' ) < $give_up_before ) { Log::update_batch( $key, 'sending', array( 'status' => 'failed', 'error_code' => 'dispatch_interrupted', 'error_message' => __( 'Sending was interrupted and could not be confirmed. Check the sent messages in the Vibrate app before sending again.', 'vibrate-sms' ), ) ); continue; } // Same Idempotency-Key: if Vibrate already took it, it replays. Log::update_batch( $key, 'sending', array( 'status' => 'pending' ) ); Messenger::dispatch( $key ); } foreach ( Log::stale_pending( self::STALE_PENDING ) as $key ) { $rows = Log::batch( $key ); if ( $rows && strtotime( $rows[0]->created_at . ' UTC' ) < $give_up_before ) { Log::update_batch( $key, 'pending', array( 'status' => 'failed', 'error_code' => 'expired', 'error_message' => __( 'Not sent: the background queue did not run in time. Check that WP-Cron or Action Scheduler is working on this site.', 'vibrate-sms' ), ) ); continue; } Messenger::dispatch( $key ); } } /** * Pull delivery status for runs that are still open. * * @param int $limit Runs to check. * @param int $started Start time, for the time budget. * @param int $settle Skip runs checked less than this many seconds ago. * @return int Runs checked. */ public static function refresh( int $limit, int $started = 0, int $settle = MINUTE_IN_SECONDS ): int { if ( '' === Settings::token() ) { return 0; } $started = $started ? $started : time(); $checked = 0; foreach ( Log::open_runs( $limit, $settle ) as $run_id ) { if ( time() - $started > self::TIME_BUDGET ) { break; } Messenger::refresh_run( $run_id ); ++$checked; } return $checked; } /** * Prune the log once a day. */ public static function purge(): void { $last = (int) get_option( self::PURGE_OPTION, 0 ); if ( time() - $last < DAY_IN_SECONDS ) { return; } update_option( self::PURGE_OPTION, time(), false ); $days = (int) Settings::protection()['log_retention_days']; if ( $days > 0 ) { Log::purge( $days ); } } }