sample-wc-mail-throttle.php 2.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687
  1. <?php
  2. /**
  3. * Plugin Name: WC Completed-Order Email Throttle
  4. * Description: Posílá zákaznické e-maily o dokončení objednávky přes rate-limitovanou frontu (Action Scheduler), aby hromadná změna stavu nezahltila wp_mail / nepřekročila limit hostingu.
  5. * Version: 1.0.0
  6. * Requires Plugins: woocommerce
  7. *
  8. * Umístění: wp-content/mu-plugins/wc-mail-throttle.php (mu-plugin = vždy aktivní)
  9. * nebo jako běžný plugin do wp-content/plugins/.
  10. */
  11. defined( 'ABSPATH' ) || exit;
  12. if ( ! class_exists( 'WC_Mail_Throttle' ) ) :
  13. class WC_Mail_Throttle {
  14. const HOOK = 'wcmt_send_completed_email';
  15. const GROUP = 'wcmt';
  16. const SLOT_OPT = 'wcmt_next_slot';
  17. /**
  18. * Odstup mezi jednotlivými e-maily v sekundách.
  19. * 12 s = max. 5 e-mailů / min. Uprav podle limitu svého hostingu.
  20. */
  21. public static function interval() {
  22. return (int) apply_filters( 'wcmt_interval_seconds', 12 );
  23. }
  24. public static function init() {
  25. // 1) Odpojí standardní (synchronní) odeslání e-mailu o dokončení objednávky.
  26. add_action( 'woocommerce_email', array( __CLASS__, 'detach_default' ), 99 );
  27. // 2) Místo něj zařadí objednávku do fronty se staggered časem.
  28. add_action( 'woocommerce_order_status_completed_notification', array( __CLASS__, 'enqueue' ), 10 );
  29. // 3) Worker fronty – v naplánovaný čas e-mail skutečně odešle.
  30. add_action( self::HOOK, array( __CLASS__, 'process' ) );
  31. }
  32. /** Sundá výchozí trigger z mailer objektu, aby se neposlal hned v rámci requestu. */
  33. public static function detach_default( $wc_emails ) {
  34. if ( isset( $wc_emails->emails['WC_Email_Customer_Completed_Order'] ) ) {
  35. remove_action(
  36. 'woocommerce_order_status_completed_notification',
  37. array( $wc_emails->emails['WC_Email_Customer_Completed_Order'], 'trigger' )
  38. );
  39. }
  40. }
  41. /** Zařadí objednávku do fronty; každý další e-mail dostane pozdější slot. */
  42. public static function enqueue( $order_id ) {
  43. $order_id = (int) $order_id;
  44. // Fallback, kdyby Action Scheduler nebyl k dispozici – pošli hned.
  45. if ( ! function_exists( 'as_schedule_single_action' ) ) {
  46. self::process( $order_id );
  47. return;
  48. }
  49. $now = time();
  50. $next = (int) get_option( self::SLOT_OPT, 0 );
  51. // Slot = buď „teď", nebo navazuje na poslední naplánovaný (self-healing přes max()).
  52. $slot = max( $now, $next );
  53. update_option( self::SLOT_OPT, $slot + self::interval(), false ); // autoload = false
  54. as_schedule_single_action( $slot, self::HOOK, array( $order_id ), self::GROUP );
  55. }
  56. /** Worker: znovu spustí standardní WooCommerce e-mail pro danou objednávku. */
  57. public static function process( $order_id = 0 ) {
  58. $order_id = (int) $order_id;
  59. if ( ! $order_id ) {
  60. return;
  61. }
  62. $emails = WC()->mailer()->get_emails();
  63. if ( isset( $emails['WC_Email_Customer_Completed_Order'] ) ) {
  64. $emails['WC_Email_Customer_Completed_Order']->trigger( $order_id );
  65. }
  66. }
  67. }
  68. add_action( 'init', array( 'WC_Mail_Throttle', 'init' ) );
  69. endif;