<?php
namespace App\Models;
use App\Library\QuotaTrackerFile;
use App\Library\Tool;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
/**
* @method static status(bool $true)
* @method static where(string $string, string $uid)
* @method static cursor()
* @method static whereIn(string $string, array $sending_servers_ids)
* @method static find(int|mixed|string $id)
* @method static create(array $server)
* @method static limit(mixed $limit)
* @method static whereLike(string[] $array, mixed $search)
* @method static count()
* @property mixed quota_value
* @property mixed quota_base
* @property mixed quota_unit
* @property mixed quotaTracker
* @property mixed created_at
* @property mixed uid
* @property mixed name
*/
class SendingServer extends Model
{
// sending server type
const TYPE_TWILIO = 'Twilio';
const TYPE_TWILIOCOPILOT = 'TwilioCopilot';
const TYPE_EASYSENDSMS = 'EasySendSMS';
const TYPE_CHEAPGLOBALSMS = 'CheapGlobalSMS';
const TYPE_SAFARICOM = 'Safaricom';
const TYPE_FACILITAMOVEL = 'FacilitaMovel';
const TYPE_SMSDELIVERER = 'Smsdeliverer';
const TYPE_ROUNDSMS = 'RoundSMS';
const TYPE_YOSMS = 'YoSMS';
const TYPE_DIGINTRA = 'Digintra';
const TYPE_ALLMYSMS = 'AllMySMS';
const TYPE_ESOLUTIONS = 'ESolutions';
const TYPE_GUPSHUPIO = 'GupshupIO';
const TYPE_SEMAPHORE = 'SemaPhore';
const TYPE_ESTORESMS = 'EStoreSMS';
const TYPE_GOIP = 'GoIP';
const TYPE_MAILJET = 'Mailjet';
const TYPE_ADVANCEMSGSYS = 'AdvanceMSGSys';
const TYPE_UIPAPP = 'UIPApp';
const TYPE_SMSFRL = 'SMSFRL';
const TYPE_IMARTGROUP = 'IMartGroup';
/**
* The attributes that are mass assignable.
*
* @var array
* @note important! consider updating the $fillable variable, it will affect some other methods
*/
protected $fillable = [
'name',
'user_id',
'settings',
'api_link',
'port',
'username',
'password',
'route',
'sms_type',
'account_sid',
'auth_id',
'auth_token',
'access_key',
'access_token',
'secret_access',
'api_key',
'api_secret',
'user_token',
'project_id',
'api_token',
'auth_key',
'device_id',
'region',
'application_id',
'c1',
'c2',
'c3',
'c4',
'c5',
'c6',
'c7',
'type',
'sms_per_request',
'quota_value',
'quota_base',
'quota_unit',
'custom_order',
'schedule',
'custom',
'status',
'two_way',
'plain',
'mms',
'voice',
'whatsapp',
'source_addr_ton',
'source_addr_npi',
'dest_addr_ton',
'dest_addr_npi',
'success_keyword',
];
/**
* The attributes that should be cast.
*
* @var string[]
*/
protected $casts = [
'schedule' => 'boolean',
'custom' => 'boolean',
'status' => 'boolean',
'two_way' => 'boolean',
'plain' => 'boolean',
'mms' => 'boolean',
'voice' => 'boolean',
'whatsapp' => 'boolean',
'quota_value' => 'integer',
'quota_base' => 'integer',
'sms_per_request' => 'integer',
'port' => 'integer',
];
/**
* Plans
*
* @return BelongsToMany
*
*/
public function plans(): BelongsToMany
{
return $this->belongsToMany(Plan::class, 'plans_sending_servers');
}
/**
* Customer
*
* @return BelongsTo
*/
public function customer(): BelongsTo
{
return $this->belongsTo(Customer::class);
}
/**
* Admin
*
* @return BelongsTo
*
*/
public function admin(): BelongsTo
{
return $this->belongsTo(Admin::class);
}
/**
* get custom sending server
*
* @return BelongsTo
*
*/
public function customSendingServer(): BelongsTo
{
return $this->belongsTo(CustomSendingServer::class, 'id', 'server_id');
}
/**
* Bootstrap any application services.
*/
public static function boot()
{
parent::boot();
// Create uid when creating list.
static::creating(function ($item) {
// Create new uid
$uid = uniqid();
while (self::where('uid', $uid)->count() > 0) {
$uid = uniqid();
}
$item->uid = $uid;
});
}
/**
* Find item by uid.
*
* @param $uid
*
* @return object
*/
public static function findByUid($uid): object
{
return self::where('uid', $uid)->first();
}
/**
* Active status scope
*
* @param $query
* @param bool $status
*
* @return mixed
*/
public function scopeStatus($query, bool $status): mixed
{
return $query->where('status', $status);
}
/**
* Get sending limit types.
*
* @return array
*/
public static function sendingLimitValues(): array
{
return [
'unlimited' => [
'quota_value' => -1,
'quota_base' => -1,
'quota_unit' => 'day',
],
'100_per_minute' => [
'quota_value' => 100,
'quota_base' => 1,
'quota_unit' => 'minute',
],
'1000_per_hour' => [
'quota_value' => 1000,
'quota_base' => 1,
'quota_unit' => 'hour',
],
'10000_per_day' => [
'quota_value' => 10000,
'quota_base' => 1,
'quota_unit' => 'day',
],
];
}
/**
* Get sending server's quota.
*
* @return string
*/
public function getSendingQuota(): string
{
return $this->quota_value;
}
/**
* Get sending server's sending quota.
*
* @return string
* @throws Exception
*/
public function getSendingQuotaUsage(): string
{
$tracker = $this->getQuotaTracker();
return $tracker->getUsage();
}
/**
* Quota display.
*
* @return string
*/
public function displayQuota(): string
{
if ($this->quota_value == -1) {
return __('locale.plans.unlimited');
}
return $this->quota_value . '/' . $this->quota_base . ' ' . __('locale.labels.' . Tool::getPluralParse($this->quota_unit, $this->quota_base));
}
/**
* Quota display.
*
* @return string
*/
public function displayQuotaHtml(): string
{
if ($this->quota_value == -1) {
return __('locale.plans.unlimited');
}
return '<code>' . $this->quota_value . '</code>/<code>' . $this->quota_base . ' ' . __('locale.labels.' . Tool::getPluralParse($this->quota_unit, $this->quota_base)) . '</code>';
}
/**
* Get sending server's QuotaTracker.
*
* @return mixed
* @throws Exception
*/
public function getQuotaTracker(): mixed
{
if ( ! $this->quotaTracker) {
$this->initQuotaTracker();
}
return $this->quotaTracker;
}
/**
* Initialize the quota tracker.
*
* @throws Exception
*/
public function initQuotaTracker()
{
$this->quotaTracker = new QuotaTrackerFile($this->getSendingQuotaLockFile(), ['start' => $this->created_at->timestamp, 'max' => -1], [$this->getQuotaIntervalString() => $this->getSendingQuota()]);
$this->quotaTracker->cleanupSeries();
// @note: in case of multi-process, the following command must be issued manually
// $this->renewQuotaTracker();
}
/**
* Clean up the quota tracking files to prevent it from growing too large.
*
* @throws Exception
*/
public function cleanupQuotaTracker()
{
// @todo: hard-coded for 1 month
$this->getQuotaTracker()->cleanupSeries(null, '1 month');
}
/**
* Get sending quota lock file.
*
* @return string file path
*/
public function getSendingQuotaLockFile(): string
{
return storage_path("app/server/quota/{$this->uid}");
}
/**
* Get quota starting time.
*
* @return string
*/
public function getQuotaIntervalString(): string
{
return "{$this->quota_base} {$this->quota_unit}";
}
/**
* Get quota starting time.
*
* @return string
*/
public function getQuotaStartingTime(): string
{
return "{$this->getQuotaIntervalString()} ago";
}
/**
* Increment quota usage.
*
* @param Carbon|null $timePoint
*
* @return mixed
* @throws Exception
*/
public function countUsage(Carbon $timePoint = null): mixed
{
return $this->getQuotaTracker($timePoint)->add();
}
/**
* Check if user has used up all quota allocated.
*
* @return bool
* @throws Exception
*/
public function overQuota(): bool
{
return ! $this->getQuotaTracker()->check();
}
/**
* get route key by uid
*
* @return string
*/
public function getRouteKeyName(): string
{
return 'uid';
}
/**
* @return string
*/
public function __toString(): string
{
return $this->name;
}
}