Initial commit
This commit is contained in:
123
app/Jobs/DeleteExpiredWireChatMessagesJob.php
Normal file
123
app/Jobs/DeleteExpiredWireChatMessagesJob.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Namu\WireChat\Enums\Actions;
|
||||
use Namu\WireChat\Models\Message;
|
||||
|
||||
class DeleteExpiredWireChatMessagesJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->onQueue('low');
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
// Disappearing messages always enabled - this prevents orphaned messages
|
||||
Log::info('WireChat disappearing messages: Starting cleanup');
|
||||
|
||||
$deletedCount = 0;
|
||||
$deletedKeptCount = 0;
|
||||
|
||||
// Get all conversations with disappearing settings
|
||||
$conversations = \Namu\WireChat\Models\Conversation::whereNotNull('disappearing_started_at')
|
||||
->whereNotNull('disappearing_duration')
|
||||
->get();
|
||||
|
||||
Log::info("WireChat disappearing messages: Found {$conversations->count()} conversations with disappearing enabled");
|
||||
|
||||
foreach ($conversations as $conversation) {
|
||||
// Get regular messages (not kept) that should be deleted
|
||||
$messages = $conversation->messages()
|
||||
->withoutGlobalScopes()
|
||||
->where(function ($query) {
|
||||
// Messages that are not kept
|
||||
$query->whereNull('kept_at')
|
||||
// Or messages that are kept but have delete actions or are trashed
|
||||
->orWhere(function ($query) {
|
||||
$query->whereNotNull('kept_at') // Kept messages
|
||||
->where(function ($query) {
|
||||
$query->whereNotNull('deleted_at') // Trashed
|
||||
->orWhereHas('actions', function ($query) {
|
||||
$query->where('type', Actions::DELETE);
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
// Only messages created AFTER disappearing was enabled
|
||||
->where('created_at', '>', $conversation->disappearing_started_at)
|
||||
->get();
|
||||
|
||||
foreach ($messages as $message) {
|
||||
$createdAt = $message->created_at;
|
||||
|
||||
if ($createdAt && $createdAt->isFuture()) {
|
||||
continue; // Skip future messages
|
||||
}
|
||||
|
||||
// Check if message is older than the conversation's duration
|
||||
if ($createdAt && $createdAt->diffInSeconds(now()) > $conversation->disappearing_duration) {
|
||||
try {
|
||||
Log::info("WireChat disappearing messages: Deleting message {$message->id} (age: {$createdAt->diffInSeconds(now())}s, limit: {$conversation->disappearing_duration}s)");
|
||||
$message->forceDelete();
|
||||
$deletedCount++;
|
||||
} catch (\Exception $e) {
|
||||
Log::error("WireChat disappearing messages: Failed to delete message {$message->id}: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get kept messages that should be deleted based on kept_messages_duration
|
||||
$keptMessagesDurationDays = timebank_config('wirechat.disappearing_messages.kept_messages_duration');
|
||||
if ($keptMessagesDurationDays !== null && timebank_config('wirechat.disappearing_messages.allow_users_to_keep', true)) {
|
||||
$keptMessagesDuration = $keptMessagesDurationDays * 86400; // Convert days to seconds
|
||||
$keptMessages = $conversation->messages()
|
||||
->withoutGlobalScopes()
|
||||
->whereNotNull('kept_at')
|
||||
->whereNull('deleted_at')
|
||||
->whereDoesntHave('actions', function ($query) {
|
||||
$query->where('type', Actions::DELETE);
|
||||
})
|
||||
->where('kept_at', '>', $conversation->disappearing_started_at)
|
||||
->get();
|
||||
|
||||
foreach ($keptMessages as $keptMessage) {
|
||||
$keptAt = $keptMessage->kept_at;
|
||||
|
||||
if ($keptAt && $keptAt->isFuture()) {
|
||||
continue; // Skip future kept messages
|
||||
}
|
||||
|
||||
// Check if kept message is older than the kept messages duration
|
||||
if ($keptAt && $keptAt->diffInSeconds(now()) > $keptMessagesDuration) {
|
||||
try {
|
||||
Log::info("WireChat disappearing messages: Deleting kept message {$keptMessage->id} (kept for: {$keptAt->diffInSeconds(now())}s, limit: {$keptMessagesDuration}s)");
|
||||
$keptMessage->forceDelete();
|
||||
$deletedKeptCount++;
|
||||
} catch (\Exception $e) {
|
||||
Log::error("WireChat disappearing messages: Failed to delete kept message {$keptMessage->id}: {$e->getMessage()}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log::info("WireChat disappearing messages: Deleted {$deletedCount} expired messages and {$deletedKeptCount} expired kept messages");
|
||||
}
|
||||
}
|
||||
360
app/Jobs/SendBulkMailJob.php
Normal file
360
app/Jobs/SendBulkMailJob.php
Normal file
@@ -0,0 +1,360 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\NewsletterMail;
|
||||
use App\Models\MailingBounce;
|
||||
use App\Models\Mailing;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendBulkMailJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
protected $mailing;
|
||||
protected $locale;
|
||||
protected $recipients;
|
||||
protected $contentBlocks;
|
||||
|
||||
public $timeout = 300; // 5 minutes timeout
|
||||
|
||||
/**
|
||||
* Get the number of times the job may be attempted.
|
||||
*/
|
||||
public function tries(): int
|
||||
{
|
||||
return timebank_config('mailing.max_retries', 3) + 1; // +1 for initial attempt
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determine if the job should be retried based on time limits.
|
||||
*/
|
||||
public function retryUntil(): \DateTime
|
||||
{
|
||||
$abandonAfterHours = timebank_config('mailing.abandon_after_hours', 72);
|
||||
return now()->addHours($abandonAfterHours);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*/
|
||||
public function __construct(Mailing $mailing, string $locale, array $contentBlocks, $recipients)
|
||||
{
|
||||
$this->mailing = $mailing;
|
||||
$this->locale = $locale;
|
||||
$this->contentBlocks = $contentBlocks;
|
||||
$this->recipients = $recipients;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* This job processes a batch of recipients for a specific locale.
|
||||
* Multiple instances of this job may run in parallel for different batches/locales.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::info("MAILING: Starting bulk mail batch job for Mailing ID: {$this->mailing->id}, Recipients in batch: " . count($this->recipients) . ", Locale: {$this->locale}");
|
||||
|
||||
$sendDelay = timebank_config('mailing.send_delay_seconds', 5);
|
||||
$successCount = 0;
|
||||
$failureCount = 0;
|
||||
$skippedCount = 0;
|
||||
|
||||
foreach ($this->recipients as $recipient) {
|
||||
// Skip if email is already suppressed due to bounces
|
||||
if (MailingBounce::isSuppressed($recipient->email)) {
|
||||
Log::warning("MAILING: Skipping suppressed email: {$recipient->email} (Mailing ID: {$this->mailing->id})");
|
||||
$skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
// Send individual email synchronously - let NewsletterMail generate its own content blocks
|
||||
$newsletterMail = new NewsletterMail($this->mailing, $recipient, null, $this->locale, true);
|
||||
|
||||
// Attempt to send email with comprehensive error handling
|
||||
$this->sendEmailWithErrorHandling($recipient->email, $newsletterMail);
|
||||
|
||||
$successCount++;
|
||||
|
||||
// Add delay between emails to avoid rate limiting
|
||||
if ($sendDelay > 0) {
|
||||
sleep($sendDelay);
|
||||
}
|
||||
|
||||
} catch (\Exception $e) {
|
||||
$failureCount++;
|
||||
Log::error("Failed to send newsletter to {$recipient->email}: " . $e->getMessage());
|
||||
|
||||
// Check if this is a bounce-related error
|
||||
if ($this->isBounceError($e)) {
|
||||
$bounceType = $this->determineBounceType($e);
|
||||
MailingBounce::recordBounce(
|
||||
$recipient->email,
|
||||
$bounceType,
|
||||
$e->getMessage(),
|
||||
$this->mailing->id
|
||||
);
|
||||
Log::warning("Recorded bounce for {$recipient->email}: {$bounceType}");
|
||||
}
|
||||
|
||||
// If it's an SMTP quota/limit error and configured to fail job, trigger retry
|
||||
if ($this->isSMTPQuotaError($e) && timebank_config('mailing.fail_job_on_quota_error', true)) {
|
||||
Log::error("SMTP quota/limit error detected for {$recipient->email}. Failing job for retry with extended delay.");
|
||||
|
||||
// Set longer delay for quota errors
|
||||
$this->retryWithQuotaDelay();
|
||||
throw $e; // This will trigger job retry
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Log summary before updating statistics
|
||||
Log::info("MAILING: Completed bulk mail job for Mailing ID: {$this->mailing->id} - Success: {$successCount}, Failed: {$failureCount}, Skipped: {$skippedCount}");
|
||||
|
||||
// Update mailing statistics
|
||||
$this->updateMailingStats($successCount, $failureCount);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update mailing statistics
|
||||
*/
|
||||
protected function updateMailingStats(int $successCount, int $failureCount): void
|
||||
{
|
||||
$this->mailing->increment('sent_count', $successCount);
|
||||
$this->mailing->increment('failed_count', $failureCount);
|
||||
|
||||
// Check if all jobs are complete by comparing sent + failed with total recipients
|
||||
$totalProcessed = $this->mailing->sent_count + $this->mailing->failed_count;
|
||||
|
||||
if ($totalProcessed >= $this->mailing->recipients_count) {
|
||||
$this->mailing->update([
|
||||
'status' => 'sent',
|
||||
'sent_at' => now()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a job failure.
|
||||
*/
|
||||
public function failed(\Throwable $exception): void
|
||||
{
|
||||
// Check if this is a quota error and provide specific guidance
|
||||
if ($this->isSMTPQuotaError($exception)) {
|
||||
Log::error("Bulk mail job failed due to SMTP quota/limit for mailing {$this->mailing->id}, locale {$this->locale}. This job will be retried automatically when the quota resets.");
|
||||
|
||||
// Don't increment failed count for quota errors as they will be retried
|
||||
return;
|
||||
}
|
||||
|
||||
Log::error("Bulk mail job failed for mailing {$this->mailing->id}, locale {$this->locale}: " . $exception->getMessage());
|
||||
|
||||
// Update failure count for all recipients in this batch (only for non-quota errors)
|
||||
$this->mailing->increment('failed_count', $this->recipients->count());
|
||||
|
||||
// If this was the last job, mark mailing as sent (even with failures)
|
||||
$totalProcessed = $this->mailing->sent_count + $this->mailing->failed_count;
|
||||
if ($totalProcessed >= $this->mailing->recipients_count) {
|
||||
$this->mailing->update([
|
||||
'status' => 'sent',
|
||||
'sent_at' => now()
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Send email with comprehensive error handling
|
||||
*/
|
||||
protected function sendEmailWithErrorHandling($email, $newsletterMail)
|
||||
{
|
||||
try {
|
||||
// Use a more specific approach to detect SMTP errors
|
||||
Mail::to($email)->send($newsletterMail);
|
||||
|
||||
// Additional verification: Check if we can still send (for services that don't throw immediate exceptions)
|
||||
// This is a lightweight check that can help detect quota issues
|
||||
$this->verifyEmailServiceHealth();
|
||||
|
||||
} catch (\Symfony\Component\Mailer\Exception\TransportException $e) {
|
||||
// Specific handling for Symfony mailer transport exceptions
|
||||
Log::error("Symfony Transport Exception for {$email}: " . $e->getMessage());
|
||||
throw $e;
|
||||
} catch (\Swift_TransportException $e) {
|
||||
// Legacy SwiftMailer transport exceptions (if still in use)
|
||||
Log::error("Swift Transport Exception for {$email}: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the error is related to SMTP quota/limits
|
||||
*/
|
||||
protected function isSMTPQuotaError(\Exception $e): bool
|
||||
{
|
||||
$message = strtolower($e->getMessage());
|
||||
|
||||
// Common SMTP quota/limit error patterns
|
||||
$quotaPatterns = [
|
||||
'email limit',
|
||||
'quota exceeded',
|
||||
'rate limit',
|
||||
'too many emails',
|
||||
'limit reached',
|
||||
'billing',
|
||||
'upgrade your plan',
|
||||
'sending limit',
|
||||
'daily limit',
|
||||
'monthly limit'
|
||||
];
|
||||
|
||||
foreach ($quotaPatterns as $pattern) {
|
||||
if (strpos($message, $pattern) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Lightweight check to verify email service is still operational
|
||||
*/
|
||||
protected function verifyEmailServiceHealth()
|
||||
{
|
||||
// This method can be expanded to include specific health checks
|
||||
// For now, it serves as a placeholder for future enhancements
|
||||
|
||||
// Future implementations could include:
|
||||
// - Test connection to SMTP server
|
||||
// - Check service status endpoints
|
||||
// - Validate remaining quota if API available
|
||||
}
|
||||
|
||||
/**
|
||||
* Set retry delay specifically for quota errors
|
||||
*/
|
||||
protected function retryWithQuotaDelay()
|
||||
{
|
||||
// Store quota error information for custom backoff
|
||||
cache()->put("bulk_mail_quota_error_{$this->mailing->id}", now(), now()->addHours(24));
|
||||
}
|
||||
|
||||
/**
|
||||
* Override backoff to handle quota errors differently
|
||||
*/
|
||||
public function backoff(): array
|
||||
{
|
||||
// Check if this is a quota error retry
|
||||
if (cache()->has("bulk_mail_quota_error_{$this->mailing->id}")) {
|
||||
$quotaDelayHours = timebank_config('mailing.quota_error_retry_delay_hours', 6);
|
||||
return [$quotaDelayHours * 3600]; // Convert hours to seconds
|
||||
}
|
||||
|
||||
// Use original backoff logic for non-quota errors
|
||||
$baseDelayMinutes = timebank_config('mailing.retry_delay_minutes', 15);
|
||||
$multiplier = timebank_config('mailing.retry_multiplier', 2);
|
||||
$maxDelayHours = timebank_config('mailing.max_retry_delay_hours', 24);
|
||||
$maxRetries = timebank_config('mailing.max_retries', 3);
|
||||
|
||||
$backoffSchedule = [];
|
||||
for ($attempt = 1; $attempt <= $maxRetries; $attempt++) {
|
||||
$delayMinutes = $baseDelayMinutes * pow($multiplier, $attempt - 1);
|
||||
$delayHours = $delayMinutes / 60;
|
||||
|
||||
// Cap the delay at max_retry_delay_hours
|
||||
if ($delayHours > $maxDelayHours) {
|
||||
$delayMinutes = $maxDelayHours * 60;
|
||||
}
|
||||
|
||||
$backoffSchedule[] = $delayMinutes * 60; // Convert to seconds
|
||||
}
|
||||
|
||||
return $backoffSchedule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the error indicates an email bounce
|
||||
*/
|
||||
protected function isBounceError(\Exception $e): bool
|
||||
{
|
||||
$message = strtolower($e->getMessage());
|
||||
|
||||
// Common bounce error patterns
|
||||
$bouncePatterns = [
|
||||
'mailbox unavailable',
|
||||
'user unknown',
|
||||
'no such user',
|
||||
'invalid recipient',
|
||||
'address rejected',
|
||||
'mailbox full',
|
||||
'quota exceeded',
|
||||
'550 ',
|
||||
'551 ',
|
||||
'552 ',
|
||||
'553 ',
|
||||
'bounce',
|
||||
'undeliverable',
|
||||
'does not exist',
|
||||
'mailbox not found',
|
||||
];
|
||||
|
||||
foreach ($bouncePatterns as $pattern) {
|
||||
if (strpos($message, $pattern) !== false) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine bounce type based on error message
|
||||
*/
|
||||
protected function determineBounceType(\Exception $e): string
|
||||
{
|
||||
$message = strtolower($e->getMessage());
|
||||
|
||||
// Hard bounce patterns (permanent failures)
|
||||
$hardBouncePatterns = [
|
||||
'user unknown',
|
||||
'no such user',
|
||||
'invalid recipient',
|
||||
'address rejected',
|
||||
'does not exist',
|
||||
'mailbox not found',
|
||||
'550 ',
|
||||
'551 ',
|
||||
];
|
||||
|
||||
foreach ($hardBouncePatterns as $pattern) {
|
||||
if (strpos($message, $pattern) !== false) {
|
||||
return 'hard';
|
||||
}
|
||||
}
|
||||
|
||||
// Soft bounce patterns (temporary failures)
|
||||
$softBouncePatterns = [
|
||||
'mailbox full',
|
||||
'quota exceeded',
|
||||
'552 ',
|
||||
'temporarily rejected',
|
||||
];
|
||||
|
||||
foreach ($softBouncePatterns as $pattern) {
|
||||
if (strpos($message, $pattern) !== false) {
|
||||
return 'soft';
|
||||
}
|
||||
}
|
||||
|
||||
return 'unknown';
|
||||
}
|
||||
}
|
||||
136
app/Jobs/SendDelayedEmail.php
Normal file
136
app/Jobs/SendDelayedEmail.php
Normal file
@@ -0,0 +1,136 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\NewMessageMail;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
use Namu\WireChat\Events\MessageCreated;
|
||||
|
||||
class SendDelayedEmail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $event;
|
||||
public $sender;
|
||||
public $recipient;
|
||||
public $participant;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param MessageCreated $event
|
||||
* @param mixed $sender
|
||||
* @param mixed $recipient
|
||||
* @param mixed $participant
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(MessageCreated $event, $sender, $recipient, $participant)
|
||||
{
|
||||
$this->event = $event;
|
||||
$this->sender = $sender;
|
||||
$this->recipient = $recipient;
|
||||
$this->participant = $participant;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
try {
|
||||
Log::info('SendDelayedEmail job starting (v2)', [
|
||||
'recipient_id' => $this->recipient->id,
|
||||
'recipient_type' => get_class($this->recipient),
|
||||
'participant_id' => $this->participant->id ?? 'null',
|
||||
'message_id' => $this->event->message->id ?? 'null',
|
||||
]);
|
||||
|
||||
// Refresh the participant to get the latest data
|
||||
$this->participant->refresh();
|
||||
|
||||
// Check if the message has been read
|
||||
// WireChat uses 'conversation_read_at' on the participant model
|
||||
$lastRead = $this->participant->conversation_read_at ? Carbon::parse($this->participant->conversation_read_at) : null;
|
||||
$messageCreatedAt = Carbon::parse($this->event->message->created_at);
|
||||
|
||||
Log::info('Read status check', [
|
||||
'conversation_read_at' => $lastRead ? $lastRead->toDateTimeString() : 'null',
|
||||
'message_created' => $messageCreatedAt->toDateTimeString(),
|
||||
]);
|
||||
|
||||
if ($lastRead && $lastRead->greaterThanOrEqualTo($messageCreatedAt)) {
|
||||
// The recipient has already read the message; do not send the email
|
||||
Log::info('Recipient has already read the message', [
|
||||
'recipient_id' => $this->recipient->id,
|
||||
'recipient_type' => get_class($this->recipient),
|
||||
'message_id' => $this->event->message->id,
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the recipient's language preference
|
||||
$language = $this->recipient->lang_preference ?? config('app.locale', 'en');
|
||||
|
||||
Log::info('Language detection', [
|
||||
'recipient_lang_preference' => $this->recipient->lang_preference ?? 'null',
|
||||
'fallback_locale' => config('app.locale', 'en'),
|
||||
'selected_language' => $language,
|
||||
]);
|
||||
|
||||
// Send the email with the appropriate language
|
||||
Mail::to($this->recipient->email)
|
||||
->locale($language)
|
||||
->send(new NewMessageMail(
|
||||
$this->event,
|
||||
$this->sender,
|
||||
$this->recipient,
|
||||
$language
|
||||
));
|
||||
|
||||
Log::info('Email sent to recipient', [
|
||||
'recipient_id' => $this->recipient->id,
|
||||
'recipient_type' => get_class($this->recipient),
|
||||
'email' => $this->recipient->email,
|
||||
'language' => $language,
|
||||
'message_id' => $this->event->message->id,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error sending SendDelayedEmail', [
|
||||
'recipient_id' => $this->recipient->id,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
|
||||
// Re-throw the exception so the job can be retried
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The job failed to process.
|
||||
*
|
||||
* @param \Throwable $exception
|
||||
* @return void
|
||||
*/
|
||||
public function failed(\Throwable $exception)
|
||||
{
|
||||
Log::error('SendDelayedEmail job failed', [
|
||||
'recipient_id' => $this->recipient->id,
|
||||
'recipient_type' => get_class($this->recipient),
|
||||
'message_id' => $this->event->message->id,
|
||||
'error' => $exception->getMessage(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
41
app/Jobs/SendEmailNewTag.php
Normal file
41
app/Jobs/SendEmailNewTag.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\TagAddedMail;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\App;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendEmailNewTag implements ShouldQueue
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $tagId;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param $tagId
|
||||
*/
|
||||
public function __construct($tagId)
|
||||
{
|
||||
$this->tagId = $tagId;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// Send the email notification
|
||||
Mail::to(timebank_config('mail.content_admin.email'))->send(new TagAddedMail($this->tagId));
|
||||
}
|
||||
}
|
||||
65
app/Jobs/SendProfileEditedByAdminMail.php
Normal file
65
app/Jobs/SendProfileEditedByAdminMail.php
Normal file
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\ProfileEditedByAdminMail;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendProfileEditedByAdminMail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
protected $profile;
|
||||
protected $changedFields;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param mixed $profile The profile that was edited
|
||||
* @param array $changedFields Array of field names that were changed
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($profile, $changedFields = [])
|
||||
{
|
||||
$this->profile = $profile;
|
||||
$this->changedFields = $changedFields;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
if (!$this->profile->email) {
|
||||
Log::warning('SendProfileEditedByAdminMail: Profile has no email address', [
|
||||
'profile_id' => $this->profile->id,
|
||||
'profile_type' => get_class($this->profile),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
Log::info('SendProfileEditedByAdminMail: Sending email', [
|
||||
'profile_id' => $this->profile->id,
|
||||
'profile_type' => get_class($this->profile),
|
||||
'profile_email' => $this->profile->email,
|
||||
'changed_fields' => $this->changedFields,
|
||||
]);
|
||||
|
||||
Mail::to($this->profile->email)->send(
|
||||
new ProfileEditedByAdminMail($this->profile, $this->changedFields)
|
||||
);
|
||||
|
||||
Log::info('SendProfileEditedByAdminMail: Email sent successfully');
|
||||
}
|
||||
}
|
||||
88
app/Jobs/SendProfileLinkChangedMail.php
Normal file
88
app/Jobs/SendProfileLinkChangedMail.php
Normal file
@@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\ProfileLinkChangedMail;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendProfileLinkChangedMail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $tries = 3;
|
||||
public $backoff = [5, 30, 300]; // wait for 5, 30, or 300 sec before worker tries again
|
||||
|
||||
protected $recipient;
|
||||
protected $linkedProfile;
|
||||
protected $action;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param mixed $recipient The profile receiving the notification
|
||||
* @param mixed $linkedProfile The profile that was attached/detached
|
||||
* @param string $action Either 'attached' or 'detached'
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($recipient, $linkedProfile, $action = 'attached')
|
||||
{
|
||||
$this->recipient = $recipient;
|
||||
$this->linkedProfile = $linkedProfile;
|
||||
$this->action = $action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Log::info('SendProfileLinkChangedMail: Job starting', [
|
||||
'recipient_id' => $this->recipient->id,
|
||||
'recipient_type' => get_class($this->recipient),
|
||||
'recipient_email' => $this->recipient->email ?? 'NO EMAIL',
|
||||
'linked_profile_id' => $this->linkedProfile->id,
|
||||
'linked_profile_type' => get_class($this->linkedProfile),
|
||||
'action' => $this->action,
|
||||
]);
|
||||
|
||||
// Check if recipient has an email
|
||||
if (empty($this->recipient->email)) {
|
||||
Log::warning('SendProfileLinkChangedMail: Recipient has no email address', [
|
||||
'recipient_id' => $this->recipient->id,
|
||||
'recipient_type' => get_class($this->recipient),
|
||||
]);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Send the email to the recipient
|
||||
Mail::to($this->recipient->email)->send(
|
||||
new ProfileLinkChangedMail($this->recipient, $this->linkedProfile, $this->action)
|
||||
);
|
||||
|
||||
Log::info('SendProfileLinkChangedMail: Email sent successfully', [
|
||||
'recipient_email' => $this->recipient->email,
|
||||
'action' => $this->action,
|
||||
]);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('SendProfileLinkChangedMail: Failed to send email', [
|
||||
'recipient_email' => $this->recipient->email ?? 'NO EMAIL',
|
||||
'action' => $this->action,
|
||||
'error' => $e->getMessage(),
|
||||
'trace' => $e->getTraceAsString(),
|
||||
]);
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
51
app/Jobs/SendReactionCreatedMail.php
Normal file
51
app/Jobs/SendReactionCreatedMail.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\ReactionCreatedEmail;
|
||||
use App\Mail\ReactionCreatedMail;
|
||||
use Cog\Contracts\Love\Reaction\Models\Reaction;
|
||||
use Cog\Laravel\Love\Reaction\Events\ReactionHasBeenAdded;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendReactionCreatedMail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
|
||||
public $tries = 3;
|
||||
public $backoff = [5,30,300]; // wait for 5, 30, or 300 sec before worker tries again
|
||||
public $reaction;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Reaction $reaction)
|
||||
{
|
||||
$this->reaction = $reaction;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// Send the email to the recipient (person receiving the reaction)
|
||||
Mail::to($this->reaction->getReactant()->getReactable()->email)->send(new ReactionCreatedMail($this->reaction));
|
||||
}
|
||||
|
||||
}
|
||||
52
app/Jobs/SendReservationCancelledMail.php
Normal file
52
app/Jobs/SendReservationCancelledMail.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\ReservationCancelledMail;
|
||||
use Cog\Contracts\Love\Reaction\Models\Reaction;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendReservationCancelledMail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $tries = 3;
|
||||
public $backoff = [5,30,300]; // wait for 5, 30, or 300 sec before worker tries again
|
||||
public $data;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($data)
|
||||
{
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
\Log::info('SendReservationCancelledMail: Starting to process job', [
|
||||
'reacter_email' => $this->data['reacter_email'],
|
||||
'post_id' => $this->data['post_id']
|
||||
]);
|
||||
|
||||
// Send the email to the reacter (person who cancelled the reservation)
|
||||
Mail::to($this->data['reacter_email'])->send(new ReservationCancelledMail($this->data));
|
||||
|
||||
\Log::info('SendReservationCancelledMail: Email sent successfully', ['to' => $this->data['reacter_email']]);
|
||||
}
|
||||
}
|
||||
60
app/Jobs/SendReservationCreatedMail.php
Normal file
60
app/Jobs/SendReservationCreatedMail.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\ReservationCreatedMail;
|
||||
use Cog\Contracts\Love\Reaction\Models\Reaction;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendReservationCreatedMail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $tries = 3;
|
||||
public $backoff = [5,30,300]; // wait for 5, 30, or 300 sec before worker tries again
|
||||
public $reactionId;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($reactionId)
|
||||
{
|
||||
$this->reactionId = $reactionId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
\Log::info('SendReservationCreatedMail: Starting to process job', ['reaction_id' => $this->reactionId]);
|
||||
|
||||
// Fetch the reaction from the database
|
||||
$reaction = \Cog\Laravel\Love\Reaction\Models\Reaction::findOrFail($this->reactionId);
|
||||
\Log::info('SendReservationCreatedMail: Reaction loaded', ['reaction_id' => $reaction->getId()]);
|
||||
|
||||
$reacter = $reaction->getReacter()->getReacterable();
|
||||
\Log::info('SendReservationCreatedMail: Reacter loaded', [
|
||||
'reacter_type' => get_class($reacter),
|
||||
'reacter_id' => $reacter->id,
|
||||
'reacter_email' => $reacter->email,
|
||||
]);
|
||||
|
||||
// Send the email to the reacter (person making the reservation)
|
||||
Mail::to($reacter->email)->send(new ReservationCreatedMail($reaction));
|
||||
|
||||
\Log::info('SendReservationCreatedMail: Email sent successfully', ['to' => $reacter->email]);
|
||||
}
|
||||
}
|
||||
69
app/Jobs/SendReservationUpdateMail.php
Normal file
69
app/Jobs/SendReservationUpdateMail.php
Normal file
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\ReservationUpdateMail;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendReservationUpdateMail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $tries = 3;
|
||||
public $backoff = [5, 30, 300]; // wait for 5, 30, or 300 sec before worker tries again
|
||||
|
||||
protected $reacter;
|
||||
protected $post;
|
||||
protected $message;
|
||||
protected $organizer;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @param mixed $reacter The profile who made the reservation
|
||||
* @param mixed $post The post/event
|
||||
* @param string $message The custom message from organizer
|
||||
* @param mixed $organizer The organizer sending the message
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($reacter, $post, $message, $organizer)
|
||||
{
|
||||
$this->reacter = $reacter;
|
||||
$this->post = $post;
|
||||
$this->message = $message;
|
||||
$this->organizer = $organizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
Log::info('SendReservationUpdateMail: Starting to process job', [
|
||||
'reacter_type' => get_class($this->reacter),
|
||||
'reacter_id' => $this->reacter->id,
|
||||
'post_id' => $this->post->id
|
||||
]);
|
||||
|
||||
// Send the email to the reacter
|
||||
Mail::to($this->reacter->email)->send(
|
||||
new ReservationUpdateMail($this->reacter, $this->post, $this->message, $this->organizer)
|
||||
);
|
||||
|
||||
Log::info('SendReservationUpdateMail: Email sent successfully', [
|
||||
'to' => $this->reacter->email,
|
||||
'post_id' => $this->post->id
|
||||
]);
|
||||
}
|
||||
}
|
||||
47
app/Jobs/SendUserDeletedMail.php
Normal file
47
app/Jobs/SendUserDeletedMail.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Mail\UserDeletedMail;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Mail;
|
||||
|
||||
class SendUserDeletedMail implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
|
||||
public $tries = 3;
|
||||
public $backoff = [5,10,30]; // wait for 5, 10, or 30 sec before worker tries again
|
||||
public $result;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($result)
|
||||
{
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
// Send the email
|
||||
Mail::to($this->result->email)->send(new UserDeletedMail($this->result));
|
||||
}
|
||||
|
||||
}
|
||||
39
app/Jobs/TestDelayJob.php
Normal file
39
app/Jobs/TestDelayJob.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
|
||||
class TestDelayJob implements ShouldQueue
|
||||
{
|
||||
use Dispatchable;
|
||||
use InteractsWithQueue;
|
||||
use Queueable;
|
||||
use SerializesModels;
|
||||
|
||||
public $delay;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct($delayInSeconds)
|
||||
{
|
||||
$this->delay = now()->addSeconds($delayInSeconds);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function handle()
|
||||
{
|
||||
\Log::info('Test delay job executed at ' . now());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user