Няма описание

Bounce.php 5.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. <?php
  2. namespace MailPoet\Cron\Workers;
  3. if (!defined('ABSPATH')) exit;
  4. use MailPoet\Entities\NewsletterEntity;
  5. use MailPoet\Entities\ScheduledTaskEntity;
  6. use MailPoet\Entities\StatisticsBounceEntity;
  7. use MailPoet\Entities\SubscriberEntity;
  8. use MailPoet\Mailer\Mailer;
  9. use MailPoet\Models\ScheduledTask;
  10. use MailPoet\Models\ScheduledTaskSubscriber;
  11. use MailPoet\Newsletter\Sending\SendingQueuesRepository;
  12. use MailPoet\Services\Bridge;
  13. use MailPoet\Services\Bridge\API;
  14. use MailPoet\Settings\SettingsController;
  15. use MailPoet\Statistics\StatisticsBouncesRepository;
  16. use MailPoet\Subscribers\SubscribersRepository;
  17. use MailPoet\Tasks\Bounce as BounceTask;
  18. use MailPoet\Tasks\Subscribers as TaskSubscribers;
  19. use MailPoet\Tasks\Subscribers\BatchIterator;
  20. use MailPoetVendor\Carbon\Carbon;
  21. class Bounce extends SimpleWorker {
  22. const TASK_TYPE = 'bounce';
  23. const BATCH_SIZE = 100;
  24. const BOUNCED_HARD = 'hard';
  25. const BOUNCED_SOFT = 'soft';
  26. const NOT_BOUNCED = null;
  27. public $api;
  28. /** @var SettingsController */
  29. private $settings;
  30. /** @var Bridge */
  31. private $bridge;
  32. /** @var SubscribersRepository */
  33. private $subscribersRepository;
  34. /** @var SendingQueuesRepository */
  35. private $sendingQueuesRepository;
  36. /** @var StatisticsBouncesRepository */
  37. private $statisticsBouncesRepository;
  38. public function __construct(
  39. SettingsController $settings,
  40. SubscribersRepository $subscribersRepository,
  41. SendingQueuesRepository $sendingQueuesRepository,
  42. StatisticsBouncesRepository $statisticsBouncesRepository,
  43. Bridge $bridge
  44. ) {
  45. $this->settings = $settings;
  46. $this->bridge = $bridge;
  47. parent::__construct();
  48. $this->subscribersRepository = $subscribersRepository;
  49. $this->sendingQueuesRepository = $sendingQueuesRepository;
  50. $this->statisticsBouncesRepository = $statisticsBouncesRepository;
  51. }
  52. public function init() {
  53. if (!$this->api) {
  54. $this->api = new API($this->settings->get(Mailer::MAILER_CONFIG_SETTING_NAME)['mailpoet_api_key']);
  55. }
  56. }
  57. public function checkProcessingRequirements() {
  58. return $this->bridge->isMailpoetSendingServiceEnabled();
  59. }
  60. public function prepareTaskStrategy(ScheduledTaskEntity $task, $timer) {
  61. BounceTask::prepareSubscribers($task);
  62. if (!ScheduledTaskSubscriber::getUnprocessedCount($task->getId())) {
  63. ScheduledTaskSubscriber::where('task_id', $task->getId())->deleteMany();
  64. return false;
  65. }
  66. return true;
  67. }
  68. public function processTaskStrategy(ScheduledTask $task, $timer) {
  69. $subscriberBatches = new BatchIterator($task->id, self::BATCH_SIZE);
  70. if (count($subscriberBatches) === 0) {
  71. ScheduledTaskSubscriber::where('task_id', $task->id)->deleteMany();
  72. return true; // mark completed
  73. }
  74. $taskSubscribers = new TaskSubscribers($task);
  75. foreach ($subscriberBatches as $subscribersToProcessIds) {
  76. // abort if execution limit is reached
  77. $this->cronHelper->enforceExecutionLimit($timer);
  78. $subscriberEmails = $this->subscribersRepository->getUndeletedSubscribersEmailsByIds($subscribersToProcessIds);
  79. $subscriberEmails = array_column($subscriberEmails, 'email');
  80. $this->processEmails($task, $subscriberEmails);
  81. $taskSubscribers->updateProcessedSubscribers($subscribersToProcessIds);
  82. }
  83. return true;
  84. }
  85. public function processEmails($task, array $subscriberEmails) {
  86. $checkedEmails = $this->api->checkBounces($subscriberEmails);
  87. $this->processApiResponse($task, (array)$checkedEmails);
  88. }
  89. public function processApiResponse($task, array $checkedEmails) {
  90. $previousTask = $this->findPreviousTask($task);
  91. foreach ($checkedEmails as $email) {
  92. if (!isset($email['address'], $email['bounce'])) {
  93. continue;
  94. }
  95. if ($email['bounce'] === self::BOUNCED_HARD) {
  96. $subscriber = $this->subscribersRepository->findOneBy(['email' => $email['address']]);
  97. if (!$subscriber instanceof SubscriberEntity) continue;
  98. $subscriber->setStatus(SubscriberEntity::STATUS_BOUNCED);
  99. $this->saveBouncedStatistics($subscriber, $task, $previousTask);
  100. }
  101. }
  102. $this->subscribersRepository->flush();
  103. }
  104. public function getNextRunDate() {
  105. $date = Carbon::createFromTimestamp($this->wp->currentTime('timestamp'));
  106. return $date->startOfDay()
  107. ->addDay()
  108. ->addHours(rand(0, 5))
  109. ->addMinutes(rand(0, 59))
  110. ->addSeconds(rand(0, 59));
  111. }
  112. private function findPreviousTask(ScheduledTask $task): ?ScheduledTaskEntity {
  113. $taskEntity = $this->scheduledTasksRepository->findOneById($task->id);
  114. if (!$taskEntity instanceof ScheduledTaskEntity) return null;
  115. return $this->scheduledTasksRepository->findPreviousTask($taskEntity);
  116. }
  117. private function saveBouncedStatistics(SubscriberEntity $subscriber, ScheduledTask $task, ?ScheduledTaskEntity $previousTask): void {
  118. $taskEntity = $this->scheduledTasksRepository->findOneById($task->id);
  119. if (!$taskEntity instanceof ScheduledTaskEntity) return;
  120. $dateFrom = null;
  121. if ($previousTask instanceof ScheduledTaskEntity) {
  122. $dateFrom = $previousTask->getScheduledAt();
  123. }
  124. $queues = $this->sendingQueuesRepository->findAllForSubscriberSentBetween($subscriber, $taskEntity->getScheduledAt(), $dateFrom);
  125. foreach ($queues as $queue) {
  126. $newsletter = $queue->getNewsletter();
  127. if ($newsletter instanceof NewsletterEntity) {
  128. $statistics = new StatisticsBounceEntity($newsletter, $queue, $subscriber);
  129. $this->statisticsBouncesRepository->persist($statistics);
  130. }
  131. }
  132. }
  133. }