<?php
namespace App\Jobs;
use App\Library\SMSCounter;
use App\Library\Tool;
use App\Models\Campaigns;
use App\Models\SpamWord;
use App\Models\User;
use Exception;
use Illuminate\Bus\Batchable;
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\DB;
use libphonenumber\NumberParseException;
use libphonenumber\PhoneNumberUtil;
use Throwable;
class ImportCampaign implements ShouldQueue
{
use Batchable, Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
protected $customer_id;
protected $campaign_id;
protected $list;
protected $db_fields;
protected $campaign_fields;
/**
* Delete the job if its models no longer exist.
*
* @var bool
*/
public $deleteWhenMissingModels = true;
/**
* Create a new job instance.
*
* @param $customer_id
* @param $campaign_id
* @param $list
* @param $db_fields
* @param $campaign_fields
*/
public function __construct($customer_id, $campaign_id, $list, $db_fields, $campaign_fields)
{
$this->list = $list;
$this->customer_id = $customer_id;
$this->campaign_id = $campaign_id;
$this->db_fields = $db_fields;
$this->campaign_fields = $campaign_fields;
}
/**
* render sms with tag
*
* @param $msg
* @param $data
*
* @return string|string[]
*/
public function renderSMS($msg, $data)
{
preg_match_all('~{(.*?)}~s', $msg, $datas);
foreach ($datas[1] as $value) {
if (array_key_exists($value, $data)) {
$msg = preg_replace("/\b$value\b/u", $data[$value], $msg);
} else {
$msg = str_ireplace($value, '', $msg);
}
}
return str_ireplace(["{", "}"], '', $msg);
}
/**
* Execute the job.
*
* @return void
* @throws NumberParseException
* @throws Throwable
*/
public function handle()
{
$prepareForTemplateTag = [];
$cutting_array = [];
$lines = $this->list;
$db_fields = $this->db_fields;
$campaign_fields = $this->campaign_fields;
$user = User::find($this->customer_id);
$campaign = Campaigns::find($this->campaign_id);
if ($this->batch()->cancelled()) {
$campaign->cancelled();
return;
}
$cutting_system = $user->customer->getOption('cutting_system');
$sending_server = $campaign->getSendingServers();
$coverage = $campaign->getCoverage();
$key = 0;
$cost = 0;
$total_unit = 0;
if ($cutting_system == 'yes') {
$cutting_value = $user->customer->getOption('cutting_value');
$cutting_unit = $user->customer->getOption('cutting_unit');
$cutting_logic = $user->customer->getOption('cutting_logic');
if ($cutting_unit == 'percentage') {
$cutting_value = ($cutting_value / 100) * $lines->count();
}
$cutting_amount = (int) round($cutting_value);
if ($cutting_logic == 'random') {
$cutting_array = $lines->random($cutting_amount)->all();
}
if ($cutting_logic == 'start') {
$cutting_array = $lines->slice(0, $cutting_amount)->all();
}
if ($cutting_logic == 'end') {
$cutting_array = $lines->slice(-$cutting_amount)->all();
}
}
collect($cutting_array)->chunk(1000)->each(function ($lines) use (&$prepareForTemplateTag, $cost, $campaign, $db_fields, $user, &$key, &$total_unit, $sending_server, $coverage, $campaign_fields) {
$check_sender_id = $campaign->getSenderIds();
foreach ($lines as $line) {
$message = null;
if (count($check_sender_id) > 0) {
$sender_id = $campaign->pickSenderIds();
} else {
$sender_id = null;
}
$data = array_combine($db_fields, $line);
if ($data['phone'] != null) {
$phone = str_replace(['+', '(', ')', '-', ' '], '', $data['phone']);
$sms_type = $campaign->sms_type;
$sms_count = 1;
if (Tool::validatePhone($phone)) {
if ($campaign->message) {
$b = array_map('trim', $line);
$modify_data = array_combine($campaign_fields, $b);
$message = $this->renderSMS($campaign->message, $modify_data);
$sms_counter = new SMSCounter();
$message_data = $sms_counter->count($message);
$sms_count = $message_data->messages;
if ($sms_type == 'plain') {
if ($message_data->encoding == 'UTF16') {
$sms_type = 'unicode';
}
}
}
try {
$phoneUtil = PhoneNumberUtil::getInstance();
$phoneNumberObject = $phoneUtil->parse('+'.$phone);
if ($phoneUtil->isPossibleNumber($phoneNumberObject)) {
$country_code = $phoneNumberObject->getCountryCode();
if (is_array($coverage) && array_key_exists($country_code, $coverage)) {
if ($sms_type == 'plain' || $sms_type == 'unicode') {
$cost = $coverage[$country_code]['plain_sms'];
}
if ($sms_type == 'voice') {
$cost = $coverage[$country_code]['voice_sms'];
}
if ($sms_type == 'mms') {
$cost = $coverage[$country_code]['mms_sms'];
}
if ($sms_type == 'whatsapp') {
$cost = $coverage[$country_code]['whatsapp_sms'];
}
$sms_price = $cost * $sms_count;
$total_unit += (int) $sms_price;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = $message;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = (int) $sms_price;
$preparedData['status'] = 'Delivered';
} else {
$sms_price = 1 * $sms_count;
$total_unit += (int) $sms_price;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = $message;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = (int) $sms_price;
$preparedData['status'] = "Permission to send an SMS has not been enabled for the region indicated by the 'To' number: ".$phone;
}
} else {
$sms_price = 1 * $sms_count;
$total_unit += (int) $sms_price;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = $message;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = (int) $sms_price;
$preparedData['status'] = __('locale.customer.invalid_phone_number', ['phone' => $phone]);
}
} catch (NumberParseException $exception) {
$sms_price = 1 * $sms_count;
$total_unit += (int) $sms_price;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = $message;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = (int) $sms_price;
$preparedData['status'] = $exception->getMessage();
}
} else {
$total_unit += $sms_count;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = null;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = $sms_count;
$preparedData['status'] = __('locale.customer.invalid_phone_number', ['phone' => $phone]);
}
if ($user->customer->getOption('send_spam_message') == 'no') {
$spamWords = SpamWord::all()->filter(function ($spamWord) use ($message) {
if ( ! false === str_contains(strtolower($message), strtolower($spamWord->word))) {
return true;
}
return false;
});
if ($spamWords->count()) {
$preparedData['status'] = 'Your message contains spam words.';
}
}
$preparedData['campaign_id'] = $campaign->id;
$preparedData['sending_server'] = $sending_server;
$prepareForTemplateTag[] = $preparedData;
}
}
});
$insertData = Tool::check_diff_multi($lines->all(), $cutting_array);
collect($insertData)->chunk(1000)->each(function ($lines) use (&$prepareForTemplateTag, $cost, $campaign, $db_fields, $user, &$key, &$total_unit, $sending_server, $coverage, $campaign_fields) {
$check_sender_id = $campaign->getSenderIds();
foreach ($lines as $line) {
if (count($check_sender_id) > 0) {
$sender_id = $campaign->pickSenderIds();
} else {
$sender_id = null;
}
$message = null;
$data = array_combine($db_fields, $line);
if ($data['phone'] != null) {
$phone = str_replace(['+', '(', ')', '-', ' '], '', $data['phone']);
$sms_type = $campaign->sms_type;
$sms_count = 1;
if (Tool::validatePhone($phone)) {
if ($campaign->message) {
$b = array_map('trim', $line);
$modify_data = array_combine($campaign_fields, $b);
$message = $this->renderSMS($campaign->message, $modify_data);
$sms_counter = new SMSCounter();
$message_data = $sms_counter->count($message);
$sms_count = $message_data->messages;
if ($sms_type == 'plain') {
if ($message_data->encoding == 'UTF16') {
$sms_type = 'unicode';
}
}
}
try {
$phoneUtil = PhoneNumberUtil::getInstance();
$phoneNumberObject = $phoneUtil->parse('+'.$phone);
if ($phoneUtil->isPossibleNumber($phoneNumberObject)) {
$country_code = $phoneNumberObject->getCountryCode();
if (is_array($coverage) && array_key_exists($country_code, $coverage)) {
if ($sms_type == 'plain' || $sms_type == 'unicode') {
$cost = $coverage[$country_code]['plain_sms'];
}
if ($sms_type == 'voice') {
$cost = $coverage[$country_code]['voice_sms'];
}
if ($sms_type == 'mms') {
$cost = $coverage[$country_code]['mms_sms'];
}
if ($sms_type == 'whatsapp') {
$cost = $coverage[$country_code]['whatsapp_sms'];
}
$sms_price = $cost * $sms_count;
$total_unit += (int) $sms_price;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = $message;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = (int) $sms_price;
$preparedData['status'] = null;
} else {
$sms_price = 1 * $sms_count;
$total_unit += (int) $sms_price;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = $message;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = (int) $sms_price;
$preparedData['status'] = "Permission to send an SMS has not been enabled for the region indicated by the 'To' number: ".$phone;
}
} else {
$sms_price = 1 * $sms_count;
$total_unit += (int) $sms_price;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = $message;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = (int) $sms_price;
$preparedData['status'] = __('locale.customer.invalid_phone_number', ['phone' => $phone]);
}
} catch (NumberParseException $exception) {
$sms_price = 1 * $sms_count;
$total_unit += (int) $sms_price;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = $message;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = (int) $sms_price;
$preparedData['status'] = $exception->getMessage();
}
} else {
$total_unit += $sms_count;
$key++;
$preparedData['id'] = $key;
$preparedData['user_id'] = $user->id;
$preparedData['phone'] = $phone;
$preparedData['sender_id'] = $sender_id;
$preparedData['message'] = null;
$preparedData['sms_type'] = $sms_type;
$preparedData['cost'] = $sms_count;
$preparedData['status'] = __('locale.customer.invalid_phone_number', ['phone' => $phone]);
}
if ($user->customer->getOption('send_spam_message') == 'no') {
$spamWords = SpamWord::all()->filter(function ($spamWord) use ($message) {
if ( ! false === str_contains(strtolower($message), strtolower($spamWord->word))) {
return true;
}
return false;
});
if ($spamWords->count()) {
$preparedData['status'] = 'Your message contains spam words.';
}
}
$preparedData['campaign_id'] = $campaign->id;
$preparedData['sending_server'] = $sending_server;
$prepareForTemplateTag[] = $preparedData;
}
}
});
if ($user->sms_unit != '-1' && $total_unit > $user->sms_unit) {
$campaign->failed(sprintf("Campaign `%s` (%s) halted, customer exceeds sms credit", $campaign->campaign_name, $campaign->uid));
sleep(60);
} else {
try {
$failed_cost = 0;
if ($user->sms_unit != '-1') {
DB::transaction(function () use ($user, $total_unit) {
$remaining_balance = $user->sms_unit - $total_unit;
$user->lockForUpdate();
$user->update(['sms_unit' => $remaining_balance]);
});
}
$campaign->processing();
collect($prepareForTemplateTag)->sortBy('id')->values()->chunk(3000)->each(function ($sendData) use ($user, $campaign, &$failed_cost) {
foreach ($sendData as $data) {
$status = 'Invalid sms type';
if ($campaign->sms_type == 'plain' || $campaign->sms_type == 'unicode') {
$status = $campaign->sendPlainSMS($data);
}
if ($campaign->sms_type == 'voice') {
$data['language'] = $campaign->language;
$data['gender'] = $campaign->gender;
$status = $campaign->sendVoiceSMS($data);
}
if ($campaign->sms_type == 'mms') {
$data['media_url'] = $campaign->media_url;
$status = $campaign->sendMMS($data);
}
if ($campaign->sms_type == 'whatsapp') {
$status = $campaign->sendWhatsApp($data);
}
if (substr_count($status, 'Delivered') == 1) {
$campaign->updateCache('DeliveredCount');
} else {
$failed_cost += $data['cost'];
$campaign->updateCache('FailedDeliveredCount');
}
}
});
unset($user);
$user = User::find($this->customer_id);
if ($user->sms_unit != '-1') {
DB::transaction(function () use ($user, $failed_cost) {
$remaining_balance = $user->sms_unit + $failed_cost;
$user->lockForUpdate();
$user->update(['sms_unit' => $remaining_balance]);
});
}
$campaign->delivered();
} catch (Exception $exception) {
$campaign->failed($exception->getMessage());
} finally {
$campaign::resetServerPools();
$data = json_decode($campaign->cache, true);
$total = $data['DeliveredCount'] + $data['FailedDeliveredCount'];
$campaign->cache = json_encode([
'ContactCount' => $total,
'DeliveredCount' => $data['DeliveredCount'],
'FailedDeliveredCount' => $data['FailedDeliveredCount'],
'NotDeliveredCount' => 0,
]);
$campaign->delivered();
}
}
}
/**
* @param Throwable $exception
*/
public function failed(Throwable $exception)
{
$campaign = Campaigns::find($this->campaign_id);
$campaign->failed($exception->getMessage());
}
}