array( $this, 'sanitize_options' ), ) ); add_settings_section( 'robotstxt_smtp_newsletter_main', __( 'SMTP (by ROBOTSTXT) Integration', 'robotstxt-smtp-newsletter' ), array( $this, 'render_section' ), 'robotstxt_smtp_newsletter' ); add_settings_field( 'enabled', __( 'Enable Integration', 'robotstxt-smtp-newsletter' ), array( $this, 'render_enabled_field' ), 'robotstxt_smtp_newsletter', 'robotstxt_smtp_newsletter_main' ); add_settings_field( 'batch_size', __( 'Batch Size', 'robotstxt-smtp-newsletter' ), array( $this, 'render_batch_size_field' ), 'robotstxt_smtp_newsletter', 'robotstxt_smtp_newsletter_main' ); add_settings_field( 'max_per_connection', __( 'Max Emails Per Connection', 'robotstxt-smtp-newsletter' ), array( $this, 'render_max_per_connection_field' ), 'robotstxt_smtp_newsletter', 'robotstxt_smtp_newsletter_main' ); } /** * Add settings page to admin menu. * * @return void */ public function add_settings_page() { add_submenu_page( 'newsletter_main_index', __( 'SMTP (by ROBOTSTXT)', 'robotstxt-smtp-newsletter' ), __( 'SMTP (by ROBOTSTXT)', 'robotstxt-smtp-newsletter' ), 'manage_options', 'robotstxt-smtp-newsletter', array( $this, 'render_page' ) ); } /** * Render settings page. * * @return void */ public function render_page() { if ( ! current_user_can( 'manage_options' ) ) { wp_die( esc_html__( 'You do not have sufficient permissions to access this page.', 'robotstxt-smtp-newsletter' ) ); } ?>

'; esc_html_e( 'Configure how Newsletter uses SMTP (by ROBOTSTXT) for email delivery.', 'robotstxt-smtp-newsletter' ); echo '

'; // Show current SMTP configuration. if ( ! class_exists( '\Robotstxt_SMTP\Plugin' ) ) { echo '

'; esc_html_e( 'ROBOTSTXT SMTP plugin is not active. Please activate it first.', 'robotstxt-smtp-newsletter' ); echo '

'; return; } $plugin = \Robotstxt_SMTP_Newsletter\Plugin::get_instance(); $settings = $plugin->get_smtp_settings(); echo '

'; echo '' . esc_html__( 'Current SMTP Configuration:', 'robotstxt-smtp-newsletter' ) . '
'; echo '

'; echo '

'; // Show optimal batch size recommendations. $this->render_recommendations(); } /** * Render settings recommendations. * * @return void */ private function render_recommendations() { $optimal = $this->calculate_optimal_settings(); $current_options = \Robotstxt_SMTP_Newsletter\Plugin::get_instance()->get_settings(); $current_batch = absint( $current_options['batch_size'] ?? 10 ); $current_max_conn = absint( $current_options['max_per_connection'] ?? 50 ); // Determine batch size status. $batch_status = 'optimal'; if ( $current_batch < $optimal['batch_size'] * 0.5 ) { $batch_status = 'too_low'; } elseif ( $current_batch > $optimal['batch_size'] * 1.5 ) { $batch_status = 'too_high'; } elseif ( abs( $current_batch - $optimal['batch_size'] ) > 5 ) { $batch_status = 'suboptimal'; } // Determine max_per_connection status (only for SMTP). $max_conn_status = 'optimal'; if ( ! $optimal['is_ses'] && $optimal['max_per_connection'] > 0 ) { if ( $current_max_conn < $optimal['max_per_connection'] * 0.5 ) { $max_conn_status = 'too_low'; } elseif ( $current_max_conn > $optimal['max_per_connection'] * 1.5 ) { $max_conn_status = 'too_high'; } elseif ( abs( $current_max_conn - $optimal['max_per_connection'] ) > 20 ) { $max_conn_status = 'suboptimal'; } } // Overall status. $overall_status = 'optimal'; if ( 'too_low' === $batch_status || 'too_high' === $batch_status || 'too_low' === $max_conn_status || 'too_high' === $max_conn_status ) { $overall_status = 'warning'; } elseif ( 'suboptimal' === $batch_status || 'suboptimal' === $max_conn_status ) { $overall_status = 'info'; } // Status styling. $notice_class = 'notice-info'; $status_icon = 'ⓘ'; // Info icon. if ( 'optimal' === $overall_status ) { $notice_class = 'notice-success'; $status_icon = '✔'; // Checkmark. } elseif ( 'warning' === $overall_status ) { $notice_class = 'notice-warning'; $status_icon = '⚠'; // Warning icon. } echo '

'; echo '' . $status_icon . ' ' . esc_html__( 'Settings Recommendations', 'robotstxt-smtp-newsletter' ) . '
'; echo '

'; // Show recommendations. echo '

'; // Batch size recommendation. if ( 'optimal' === $batch_status ) { echo '✔ ' . sprintf( /* translators: %d: batch size */ esc_html__( 'Batch size: Your current setting (%d) is optimal!', 'robotstxt-smtp-newsletter' ), $current_batch ); } else { echo '► ' . sprintf( /* translators: %d: recommended batch size */ esc_html__( 'Recommended batch size: %d emails/batch', 'robotstxt-smtp-newsletter' ), $optimal['batch_size'] ); if ( 'too_low' === $batch_status ) { echo '
   ' . sprintf( /* translators: %d: current batch size */ esc_html__( 'Current (%d) is significantly lower than optimal. Increase it to send faster.', 'robotstxt-smtp-newsletter' ), $current_batch ); } elseif ( 'too_high' === $batch_status ) { echo '
   ' . sprintf( /* translators: %d: current batch size */ esc_html__( 'Current (%d) is too high and may trigger rate limits. Reduce it to avoid errors.', 'robotstxt-smtp-newsletter' ), $current_batch ); } elseif ( 'suboptimal' === $batch_status ) { echo '
   ' . sprintf( /* translators: 1: current batch size, 2: recommended batch size */ esc_html__( 'Current (%1$d) could be optimized. Consider changing to %2$d.', 'robotstxt-smtp-newsletter' ), $current_batch, $optimal['batch_size'] ); } } // Max per connection recommendation (only for SMTP). if ( ! $optimal['is_ses'] && $optimal['max_per_connection'] > 0 ) { echo '
'; if ( 'optimal' === $max_conn_status ) { echo '✔ ' . sprintf( /* translators: %d: max per connection */ esc_html__( 'Max per connection: Your current setting (%d) is optimal!', 'robotstxt-smtp-newsletter' ), $current_max_conn ); } else { echo '► ' . sprintf( /* translators: %d: recommended max per connection */ esc_html__( 'Recommended max per connection: %d emails', 'robotstxt-smtp-newsletter' ), $optimal['max_per_connection'] ); if ( 'too_low' === $max_conn_status ) { echo '
   ' . sprintf( /* translators: %d: current max per connection */ esc_html__( 'Current (%d) is too low. Increase to reduce connection overhead.', 'robotstxt-smtp-newsletter' ), $current_max_conn ); } elseif ( 'too_high' === $max_conn_status ) { echo '
   ' . sprintf( /* translators: %d: current max per connection */ esc_html__( 'Current (%d) is too high and may cause SMTP timeouts. Reduce it.', 'robotstxt-smtp-newsletter' ), $current_max_conn ); } elseif ( 'suboptimal' === $max_conn_status ) { echo '
   ' . sprintf( /* translators: 1: current max per connection, 2: recommended max per connection */ esc_html__( 'Current (%1$d) could be optimized. Consider changing to %2$d.', 'robotstxt-smtp-newsletter' ), $current_max_conn, $optimal['max_per_connection'] ); } } } echo '

'; echo '

'; } /** * Render enabled field. * * @return void */ public function render_enabled_field() { $options = get_option( self::OPTION_NAME, array() ); $enabled = ! empty( $options['enabled'] ); ?>



Array with 'batch_size', 'max_per_connection', 'reasoning', and 'status'. */ private function calculate_optimal_settings() { $plugin = \Robotstxt_SMTP_Newsletter\Plugin::get_instance(); $settings = $plugin->get_smtp_settings(); // Get Newsletter cron interval (default 300 seconds). $cron_interval = defined( 'NEWSLETTER_CRON_INTERVAL' ) ? NEWSLETTER_CRON_INTERVAL : 300; // Get rate limits. $rate_per_second = absint( $settings['rate_limit_per_second'] ?? 0 ); $rate_per_hour = absint( $settings['rate_limit_per_hour'] ?? 0 ); $rate_per_day = absint( $settings['rate_limit_per_day'] ?? 0 ); // Check if Amazon SES is active. $is_ses = \Robotstxt_SMTP\Plugin::is_amazon_ses_integration_active(); $optimal_batch = 10; // Default fallback. $optimal_max_conn = 50; // Default fallback. $reasoning = array(); $limiting_factor = ''; // If no rate limits are set. if ( 0 === $rate_per_second && 0 === $rate_per_hour && 0 === $rate_per_day ) { $optimal_batch = $is_ses ? 50 : 20; $optimal_max_conn = $is_ses ? 0 : 100; // SES doesn't need max per connection. $reasoning[] = __( 'No rate limits configured.', 'robotstxt-smtp-newsletter' ); $reasoning[] = $is_ses ? __( 'Using default for Amazon SES (50 emails/batch).', 'robotstxt-smtp-newsletter' ) : __( 'Using default for SMTP (20 emails/batch, 100 max per connection).', 'robotstxt-smtp-newsletter' ); $limiting_factor = 'default'; } else { // Calculate based on rate limits. $candidates = array(); // Per-second limit: Calculate how many emails we can send in one cron cycle. if ( $rate_per_second > 0 ) { // Reserve time for connection overhead (10s for SMTP, 2s for SES). $overhead = $is_ses ? 2 : 10; $available_time = max( 1, $cron_interval - $overhead ); $max_emails = floor( $rate_per_second * $available_time ); $candidates['per_second'] = $max_emails; $reasoning[] = sprintf( /* translators: 1: emails per second, 2: cron interval, 3: overhead, 4: calculated max */ __( 'Per-second limit: %1$d emails/sec × (%2$d sec - %3$d sec overhead) = %4$d emails/batch', 'robotstxt-smtp-newsletter' ), $rate_per_second, $cron_interval, $overhead, $max_emails ); } // Per-hour limit: Divide by number of cron executions per hour. if ( $rate_per_hour > 0 ) { $batches_per_hour = floor( 3600 / $cron_interval ); $max_emails = floor( $rate_per_hour / $batches_per_hour ); $candidates['per_hour'] = $max_emails; $reasoning[] = sprintf( /* translators: 1: emails per hour, 2: batches per hour, 3: calculated max */ __( 'Per-hour limit: %1$d emails/hour ÷ %2$d batches/hour = %3$d emails/batch', 'robotstxt-smtp-newsletter' ), $rate_per_hour, $batches_per_hour, $max_emails ); } // Per-day limit: Divide by number of cron executions per day. if ( $rate_per_day > 0 ) { $batches_per_day = floor( 86400 / $cron_interval ); $max_emails = floor( $rate_per_day / $batches_per_day ); $candidates['per_day'] = $max_emails; $reasoning[] = sprintf( /* translators: 1: emails per day, 2: batches per day, 3: calculated max */ __( 'Per-day limit: %1$d emails/day ÷ %2$d batches/day = %3$d emails/batch', 'robotstxt-smtp-newsletter' ), $rate_per_day, $batches_per_day, $max_emails ); } // The optimal batch size is the minimum of all candidates. if ( ! empty( $candidates ) ) { $optimal_batch = min( $candidates ); $limiting_factor = array_search( $optimal_batch, $candidates, true ); // Apply safety margin (90%). $optimal_batch = max( 1, floor( $optimal_batch * 0.9 ) ); $reasoning[] = sprintf( /* translators: 1: limiting factor, 2: final batch size */ __( 'Most restrictive limit: %1$s. Recommended batch size (with 10%% safety margin): %2$d', 'robotstxt-smtp-newsletter' ), $limiting_factor, $optimal_batch ); } // Calculate optimal max_per_connection for SMTP (not needed for SES). if ( ! $is_ses ) { // Max per connection should be 2-5x the batch size, or based on hourly limits. if ( $rate_per_hour > 0 ) { // Conservative: use 10 minutes of the hourly limit. $optimal_max_conn = floor( ( $rate_per_hour / 6 ) * 0.9 ); } else { $optimal_max_conn = min( 500, max( 50, $optimal_batch * 3 ) ); } $reasoning[] = sprintf( /* translators: %d: max per connection */ __( 'Max per connection (SMTP): %d emails before reconnecting to prevent timeouts.', 'robotstxt-smtp-newsletter' ), $optimal_max_conn ); } } return array( 'batch_size' => $optimal_batch, 'max_per_connection' => $optimal_max_conn, 'reasoning' => $reasoning, 'limiting_factor' => $limiting_factor, 'cron_interval' => $cron_interval, 'is_ses' => $is_ses, ); } /** * Sanitize options. * * @param array $options Raw options. * * @return array Sanitized options. */ public function sanitize_options( $options ) { $clean = array(); $clean['enabled'] = ! empty( $options['enabled'] ); // Batch size: Allow up to 10,000 for high-throughput scenarios (SES, high rate limits). $clean['batch_size'] = max( 1, min( 10000, (int) ( $options['batch_size'] ?? 10 ) ) ); // Max per connection: Allow up to 1,000 for providers that support it. $clean['max_per_connection'] = max( 1, min( 1000, (int) ( $options['max_per_connection'] ?? 50 ) ) ); return $clean; } }