public function insertBroadcast() { if ($broadcasts = MArBroadcast::where('status', '=', 'queued')->where('send_at', '<=', Carbon::now())->get()) { /** @var MArBroadcast $broadcast */ foreach ($broadcasts as $broadcast) { $broadcast->status = 'processing'; $broadcast->save(); $status = 'fail'; if ($campaign = MArCampaign::where('ar_campaign_id', '=', $broadcast->ar_campaign_id)->where('enabled', '=', 'true')->first()) { MArQueue::unguard(); /** @var Carbon $last */ $last = null; $mails = MArMessage::where('ar_campaign_id', '=', $campaign->ar_campaign_id)->orderBy('sequence', 'asc')->get(); $user_ids = $this->listManager->getTargetUserIds($campaign->ar_list_id); if (!empty($user_ids)) { $max_mail_time = ($broadcast->mailing_time ?: 1) * 60 * 60; $mails_per_sec = min(1, $max_mail_time / count($user_ids)); foreach ($mails as $mail) { $time_offset = 0; $wait = !empty($last) ? $last->addHours($mail->wait_hrs ?: 24)->getTimestamp() : time(); foreach ($user_ids as $user_id) { try { $send = Carbon::createFromTimestamp($wait + floor($time_offset)); if (MArQueue::create(['user_id' => $user_id, 'mail_id' => $mail->mail_id, 'send_at' => $send, 'status' => 'pending'])) { echo "Mail #{$mail->mail_id} for #{$user_id} sent ", $send->diffForHumans() . "\n"; $time_offset += $mails_per_sec; } } catch (\Exception $e) { //probably duplicate insert (because laravel doesn't have INSERT IGNORE) } } $last = $send ?? Carbon::now(); } } $status = 'sent'; } $broadcast->status = $status; $broadcast->save(); } } }
public function queueMails(string $now = '') { MArSummary::unguard(); MArQueue::unguard(); $campaigns = MArCampaign::where('enabled', '=', 'true')->where('type', '=', 'autoresponder')->orderBy('priority', 'desc')->get(); $now_time = !empty($now) ? Carbon::parse($now) : Carbon::now(); /** @var CollectionEx $pending_ids */ $yesterday = Carbon::now()->subHours(12); $excluded_ids = []; foreach (['pass', 'pending'] as $status) { //if combined into 1 query or status <> 'fail' then mysql does not use index :/ this way is 100x faster $pending_ids = MArQueue::select('user_id')->distinct()->where('send_at', '>', $yesterday)->where('status', '=', $status)->get(); $excluded_ids = array_unique(array_merge($excluded_ids, $pending_ids->pluck('user_id')->toArray())); //exclude ids which got mail less than 12 hours ago (probably from a broadcast) } foreach ($campaigns as $campaign) { /** @var Collection $messages */ $ar_camp_id = $campaign->ar_campaign_id; //$exclusive = $messages = MArMessage::where('ar_campaign_id', '=', $ar_camp_id)->orderBy('sequence', 'asc')->get(); $mail_ids = $messages->pluck('mail_id'); $target_user_ids = $this->listManager->getTargetUserIds($campaign->ar_list_id); if (!empty($target_user_ids) && !empty($excluded_ids)) { $target_user_ids = array_diff($target_user_ids, $excluded_ids); } if (!empty($target_user_ids)) { /** @var Collection $completed - get user_ids of all users on the last mail of this Ar */ $six_hrs = Carbon::parse((string) $now_time)->subHours(6); $completed = MArSummary::where('ar_campaign_id', '=', $ar_camp_id)->where('last_mail_id', $mail_ids->last())->where('last_send_date', '<', $six_hrs)->select('user_id')->get(); if ($completed_user_ids = $completed->pluck('user_id')->all()) { $target_user_ids = array_diff($target_user_ids, $completed_user_ids); } /** @var Collection $summaries - contains details of last mail sent by user_id */ $summaries = MArSummary::where('ar_campaign_id', '=', $ar_camp_id)->whereIn('user_id', $target_user_ids)->get(); foreach ($target_user_ids as $user_id) { $mail_to_send = 0; if ($summary = $summaries->where('user_id', $user_id)->first()) { if (($current_mail_index = array_search($summary->last_mail_id, $mail_ids->all())) !== false) { $message = $messages->get($current_mail_index + 1); $last_sent = Carbon::parse($summary->last_send_date); if ($now_time > $last_sent) { $time_diff = $now_time->diffInHours($last_sent); $wait_hrs = $message->sequence > 0 ? $message->wait_hrs : 0; if ($time_diff > $wait_hrs) { $mail_to_send = $message->mail_id; } } } } else { $summary = new MArSummary(['user_id' => $user_id, 'ar_campaign_id' => $ar_camp_id]); $mail_to_send = $mail_ids->first(); } if ($campaign->priority > 0) { //if the Ar has priority > 0, temporarily bar this user from getting emails from other ars until this Ar is complete $excluded_ids[] = $user_id; } if (!empty($mail_to_send)) { $schedule = json_decode($campaign->schedule_json, true); if (!empty($schedule)) { $user = User::find($user_id); $timezone = !empty($user->tz_offset) ? timezone_name_from_abbr("", $user->tz_offset * -60, 0) : 'America/Chicago'; $send_time = $this->getQueueDate($schedule, $timezone); } else { $send_time = $now_time; } try { if (MArQueue::create(['send_at' => $send_time, 'user_id' => $user_id, 'mail_id' => $mail_to_send, 'status' => 'pending'])) { printf("Inserted mail %d into mail queue for user %d to send at: %s\n", $mail_to_send, $user_id, $send_time); $insertions[] = ['mail_id' => $mail_to_send, 'user_id' => $user_id]; $summary->last_send_date = $send_time; $summary->last_mail_id = $mail_to_send; $summary->save(); } } catch (\Exception $e) { echo ''; } } } } } return $insertions ?? []; }