159 lines
5.4 KiB
PHP
159 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace App\Services\Automation;
|
|
|
|
use App\Models\Automation;
|
|
use App\Models\AutomationSend;
|
|
use App\Models\Customer;
|
|
use App\Models\Message;
|
|
use App\Services\ConfigService;
|
|
use App\Services\CustomerAppNotifyService;
|
|
use App\Services\SmsBilling\SmsGatewayService;
|
|
use App\Exceptions\SmsBlockedException;
|
|
use Illuminate\Support\Facades\Log;
|
|
|
|
class AutomationSender
|
|
{
|
|
public function __construct(
|
|
private readonly SmsGatewayService $sms,
|
|
private readonly CustomerAppNotifyService $notify,
|
|
private readonly ConfigService $config,
|
|
) {
|
|
}
|
|
|
|
public function send(Automation $auto, Customer $customer, string $channel): bool
|
|
{
|
|
if (! $this->maySend($auto, $customer, $channel)) {
|
|
return false;
|
|
}
|
|
|
|
$title = $this->render((string) $auto->title, $customer);
|
|
$body = $this->render((string) $auto->body, $customer);
|
|
|
|
try {
|
|
if ($channel === AutomationCatalog::CHANNEL_SMS) {
|
|
$mobile = trim((string) $customer->mobile);
|
|
if ($mobile === '') {
|
|
throw new \RuntimeException('موبایل خالی است');
|
|
}
|
|
$text = $this->ensureAppNameFooter(trim($title."\n".$body));
|
|
$result = $this->sms->sendOrThrow($mobile, $text, 'automation');
|
|
if (! $result->sent) {
|
|
throw new \RuntimeException($result->errorMessage ?: 'ارسال پیامک ناموفق');
|
|
}
|
|
Message::create([
|
|
'sms' => $text,
|
|
'receiver' => $mobile,
|
|
'platforms' => ($result->provider ?: 'sms').':'.$mobile,
|
|
]);
|
|
} else {
|
|
$row = $this->notify->notifyCustomer((int) $customer->id, $title, $body, 'info');
|
|
if (! $row) {
|
|
throw new \RuntimeException('اعلان ساخته نشد');
|
|
}
|
|
}
|
|
|
|
$this->log($auto, $customer, $channel, 'sent');
|
|
$auto->increment('sent_count');
|
|
|
|
return true;
|
|
} catch (SmsBlockedException $e) {
|
|
$this->log($auto, $customer, $channel, 'failed', 'اعتبار پیامک کافی نیست یا مسدود است');
|
|
$auto->increment('failed_count');
|
|
Log::warning('Automation SMS blocked', ['automation_id' => $auto->id]);
|
|
|
|
return false;
|
|
} catch (\Throwable $e) {
|
|
$this->log($auto, $customer, $channel, 'failed', $e->getMessage());
|
|
$auto->increment('failed_count');
|
|
|
|
return false;
|
|
}
|
|
}
|
|
|
|
public function maySend(Automation $auto, Customer $customer, string $channel): bool
|
|
{
|
|
if (! $auto->usesChannel($channel)) {
|
|
return false;
|
|
}
|
|
|
|
$max = $auto->max_sends;
|
|
if ($max !== null && (int) $max > 0) {
|
|
$count = AutomationSend::query()
|
|
->where('automation_id', $auto->id)
|
|
->where('customer_id', $customer->id)
|
|
->where('channel', $channel)
|
|
->where('status', 'sent')
|
|
->count();
|
|
if ($count >= (int) $max) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
$cooldown = max(0, (int) $auto->cooldown_hours);
|
|
if ($cooldown > 0) {
|
|
$recent = AutomationSend::query()
|
|
->where('automation_id', $auto->id)
|
|
->where('customer_id', $customer->id)
|
|
->where('channel', $channel)
|
|
->where('status', 'sent')
|
|
->where('created_at', '>=', now()->subHours($cooldown))
|
|
->exists();
|
|
if ($recent) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private function ensureAppNameFooter(string $message): string
|
|
{
|
|
$appName = trim($this->config->getString('name_fa'));
|
|
$trimmed = rtrim($message);
|
|
if ($appName === '') {
|
|
return $trimmed;
|
|
}
|
|
|
|
$lines = preg_split('/\R/u', $trimmed) ?: [];
|
|
$last = trim((string) end($lines));
|
|
if ($last === $appName) {
|
|
return $trimmed;
|
|
}
|
|
|
|
return $trimmed === '' ? $appName : ($trimmed."\n".$appName);
|
|
}
|
|
|
|
private function render(string $text, Customer $customer): string
|
|
{
|
|
$name = trim((string) ($customer->name ?: trim(($customer->fname ?? '').' '.($customer->lname ?? ''))));
|
|
$fname = (string) ($customer->fname ?? '');
|
|
$mobile = (string) ($customer->mobile ?? '');
|
|
|
|
// سازگاری با قالب قدیمی {{name}}
|
|
$out = strtr($text, [
|
|
'{{name}}' => $name,
|
|
'{{fname}}' => $fname,
|
|
'{{mobile}}' => $mobile,
|
|
]);
|
|
|
|
// الگوی پیامک/تنظیمات: %نام ، ٪نام ، % name ، %name%
|
|
$out = preg_replace('/[%٪]\s*(?:name|نام)[%٪]?/iu', $name, $out) ?? $out;
|
|
$out = preg_replace('/[%٪]\s*(?:fname|نام_کوچک|نامکوچک)[%٪]?/iu', $fname, $out) ?? $out;
|
|
$out = preg_replace('/[%٪]\s*(?:mobile|موبایل)[%٪]?/iu', $mobile, $out) ?? $out;
|
|
|
|
return $out;
|
|
}
|
|
|
|
private function log(Automation $auto, Customer $customer, string $channel, string $status, ?string $error = null): void
|
|
{
|
|
AutomationSend::create([
|
|
'automation_id' => $auto->id,
|
|
'customer_id' => $customer->id,
|
|
'channel' => $channel,
|
|
'status' => $status,
|
|
'error' => $error,
|
|
]);
|
|
}
|
|
}
|