615 lines
13 KiB
PHP
615 lines
13 KiB
PHP
<?php
|
|
namespace Bitrix\MessageService;
|
|
|
|
use Bitrix\Main\Localization\Loc;
|
|
use Bitrix\Main\ArgumentTypeException;
|
|
use Bitrix\Main\ORM\Data\AddResult;
|
|
use Bitrix\Main\Error;
|
|
use Bitrix\Main;
|
|
use Bitrix\Main\Type\DateTime;
|
|
use Bitrix\MessageService\Integration\Pull;
|
|
use Bitrix\MessageService\Internal\Entity\MessageTable;
|
|
use Bitrix\MessageService\Restriction\RestrictionManager;
|
|
use Bitrix\MessageService\Sender\Result;
|
|
|
|
Loc::loadMessages(__FILE__);
|
|
|
|
class Message
|
|
{
|
|
public const EVENT_MESSAGE_UPDATED = 'messageUpdated';
|
|
|
|
protected $id;
|
|
|
|
/** @var Sender\Base $sender */
|
|
protected $sender;
|
|
|
|
/** @var string $type */
|
|
protected $type;
|
|
/** @var int $authorId */
|
|
protected $authorId = 0;
|
|
/** @var string $from */
|
|
protected $from;
|
|
/** @var string $to */
|
|
protected $to;
|
|
/** @var array $headers */
|
|
protected $headers = array();
|
|
/** @var string $body */
|
|
protected $body;
|
|
|
|
protected $statusId;
|
|
protected $externalStatus;
|
|
|
|
protected ?Error $error = null;
|
|
|
|
protected bool $checkRestrictions = false;
|
|
|
|
/**
|
|
* Message constructor.
|
|
* @param Sender\Base|null $sender
|
|
*/
|
|
public function __construct(Sender\Base $sender = null)
|
|
{
|
|
if ($sender)
|
|
{
|
|
$this->setSender($sender);
|
|
}
|
|
}
|
|
|
|
public static function loadById(int $id): ?Message
|
|
{
|
|
$fields = MessageTable::getRowById($id);
|
|
if (!$fields)
|
|
{
|
|
return null;
|
|
}
|
|
$instance = new static();
|
|
$instance->setFields($fields);
|
|
|
|
return $instance;
|
|
}
|
|
|
|
public static function loadByExternalId(string $senderId, string $externalId, ?string $from = null): ?Message
|
|
{
|
|
$fields = MessageTable::getByExternalId($senderId, $externalId, $from)->fetch();
|
|
if (!$fields)
|
|
{
|
|
return null;
|
|
}
|
|
$instance = new static();
|
|
$instance->setFields($fields);
|
|
|
|
return $instance;
|
|
}
|
|
|
|
/**
|
|
* @param array $fields
|
|
* @param Sender\Base|null $sender
|
|
* @return Message
|
|
*/
|
|
public static function createFromFields(array $fields, Sender\Base $sender = null): Message
|
|
{
|
|
$message = new static($sender);
|
|
$message->setFields($fields);
|
|
|
|
return $message;
|
|
}
|
|
|
|
public static function getFieldsById($messageId)
|
|
{
|
|
$iterator = Internal\Entity\MessageTable::getById($messageId);
|
|
return $iterator->fetch();
|
|
}
|
|
|
|
/**
|
|
* @param string $type
|
|
* @return Message
|
|
* @throws ArgumentTypeException
|
|
*/
|
|
public function setType(string $type): Message
|
|
{
|
|
if (!MessageType::isSupported($type))
|
|
{
|
|
throw new ArgumentTypeException('Unsupported message type');
|
|
}
|
|
|
|
$this->type = $type;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getType(): string
|
|
{
|
|
return $this->type;
|
|
}
|
|
|
|
public function checkFields(): Main\Result
|
|
{
|
|
$result = new Main\Result();
|
|
|
|
$sender = $this->getSender();
|
|
$from = $this->getFrom();
|
|
|
|
if (!$sender)
|
|
{
|
|
$result->addError(new Error(Loc::getMessage('MESSAGESERVICE_MESSAGE_ERROR_SENDER')));
|
|
}
|
|
elseif (!$sender->canUse())
|
|
{
|
|
$result->addError(new Error(Loc::getMessage('MESSAGESERVICE_MESSAGE_ERROR_SENDER_CAN_USE')));
|
|
}
|
|
elseif ($sender->getType() !== $this->getType())
|
|
{
|
|
$result->addError(new Error(Loc::getMessage('MESSAGESERVICE_MESSAGE_ERROR_TYPE')));
|
|
}
|
|
elseif (!$from || !$sender->isCorrectFrom($from))
|
|
{
|
|
$result->addError(new Error(Loc::getMessage('MESSAGESERVICE_MESSAGE_ERROR_FROM')));
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @return AddResult Created Message result.
|
|
*/
|
|
public function send(): Main\Result
|
|
{
|
|
global $USER;
|
|
|
|
$checkResult = $this->checkFields();
|
|
|
|
if (!$checkResult->isSuccess())
|
|
{
|
|
$result = new AddResult();
|
|
$result->addErrors($checkResult->getErrors());
|
|
return $result;
|
|
}
|
|
|
|
if ($this->checkRestrictions)
|
|
{
|
|
$restrictionManager = new RestrictionManager($this);
|
|
if (!$restrictionManager->isCanSendMessage())
|
|
{
|
|
return (new Main\Result())->addError(
|
|
new Error(Loc::getMessage('MESSAGESERVICE_MESSAGE_ERROR_RESTRICTION'))
|
|
);
|
|
}
|
|
}
|
|
|
|
$sender = $this->getSender();
|
|
$headers = $this->getHeaders();
|
|
|
|
$result = Internal\Entity\MessageTable::add([
|
|
'TYPE' => $this->getType(),
|
|
'SENDER_ID' => $sender->getId(),
|
|
'AUTHOR_ID' => $this->getAuthorId(),
|
|
'MESSAGE_FROM' => $this->getFrom(),
|
|
'MESSAGE_TO' => $this->getTo(),
|
|
'MESSAGE_HEADERS' => count($headers) > 0 ? $headers : null,
|
|
'MESSAGE_BODY' => $this->getBody(),
|
|
'CLUSTER_GROUP' => defined('BX_CLUSTER_GROUP') ? BX_CLUSTER_GROUP : null
|
|
]);
|
|
if ($result->isSuccess())
|
|
{
|
|
$this->id = $result->getId();
|
|
if (Main\Config\Option::get('messageservice', 'event_log_message_send', 'N') === 'Y')
|
|
{
|
|
$userId = $USER instanceof \CUser ? $USER->getId() : 0;
|
|
\CEventLog::Log('INFO', 'MESSAGE_SEND', 'messageservice', $userId, $this->getTo());
|
|
}
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public function sendDirectly(): Result\SendMessage
|
|
{
|
|
global $USER;
|
|
|
|
$checkResult = $this->checkFields();
|
|
|
|
if (!$checkResult->isSuccess())
|
|
{
|
|
$result = new Result\SendMessage();
|
|
return $result->addErrors($checkResult->getErrors());
|
|
}
|
|
|
|
if ($this->checkRestrictions)
|
|
{
|
|
$restrictionManager = new RestrictionManager($this);
|
|
if (!$restrictionManager->isCanSendMessage())
|
|
{
|
|
return (new Result\SendMessage)->addError(
|
|
new Error(Loc::getMessage('MESSAGESERVICE_MESSAGE_ERROR_RESTRICTION'))
|
|
);
|
|
}
|
|
}
|
|
|
|
$sender = $this->getSender();
|
|
|
|
if (!Sender\Limitation::checkDailyLimit($sender->getId(), $this->getFrom()))
|
|
{
|
|
$result = new Result\SendMessage();
|
|
$result->addError(new Error(Loc::getMessage('MESSAGESERVICE_MESSAGE_ERROR_LIMITATION')));
|
|
$result->setStatus(MessageStatus::DEFERRED);
|
|
|
|
return $result;
|
|
}
|
|
|
|
$headers = $this->getHeaders();
|
|
$messageFields = [
|
|
'TYPE' => $this->getType(),
|
|
'SENDER_ID' => $sender->getId(),
|
|
'AUTHOR_ID' => $this->getAuthorId(),
|
|
'MESSAGE_FROM' => $this->getFrom(),
|
|
'MESSAGE_TO' => $this->getTo(),
|
|
'MESSAGE_HEADERS' => count($headers) > 0 ? $headers : null,
|
|
'MESSAGE_BODY' => $this->getBody(),
|
|
'CLUSTER_GROUP' => defined('BX_CLUSTER_GROUP') ? BX_CLUSTER_GROUP : null
|
|
];
|
|
|
|
$sender->setSocketTimeout(5);
|
|
$sender->setStreamTimeout(15);
|
|
|
|
$result = $sender->sendMessage($messageFields);
|
|
|
|
$messageFields['DATE_EXEC'] = new DateTime();
|
|
$messageFields['SUCCESS_EXEC'] = $result->isSuccess() ? 'Y' : 'N';
|
|
|
|
if ($result->getExternalId() !== null)
|
|
{
|
|
$messageFields['EXTERNAL_ID'] = $result->getExternalId();
|
|
}
|
|
if ($result->getStatus() !== null)
|
|
{
|
|
$messageFields['STATUS_ID'] = $result->getStatus();
|
|
}
|
|
|
|
$addResult = Internal\Entity\MessageTable::add($messageFields);
|
|
if (!$addResult->isSuccess())
|
|
{
|
|
$result->addErrors($addResult->getErrors());
|
|
$result->setStatus(MessageStatus::ERROR);
|
|
|
|
return $result;
|
|
}
|
|
|
|
$this->id = $addResult->getId();
|
|
$result->setId($this->id);
|
|
|
|
if (Main\Config\Option::get('messageservice', 'event_log_message_send', 'N') === 'Y')
|
|
{
|
|
$userId = $USER instanceof \CUser ? $USER->getId() : 0;
|
|
\CEventLog::Log('INFO', 'MESSAGE_SEND', 'messageservice', $userId, $this->getTo());
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
/**
|
|
* @return mixed
|
|
*/
|
|
public function getFrom()
|
|
{
|
|
return $this->from;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $from
|
|
* @return $this
|
|
*/
|
|
public function setFrom($from): Message
|
|
{
|
|
$this->from = (string)$from;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return mixed
|
|
*/
|
|
public function getBody()
|
|
{
|
|
return $this->body;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $body
|
|
* @return $this
|
|
*/
|
|
public function setBody($body): Message
|
|
{
|
|
$this->body = (string)$body;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return mixed
|
|
*/
|
|
public function getTo()
|
|
{
|
|
return $this->to;
|
|
}
|
|
|
|
/**
|
|
* @param mixed $to
|
|
* @return $this
|
|
*/
|
|
public function setTo($to): Message
|
|
{
|
|
$this->to = (string)$to;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return Sender\Base|null
|
|
*/
|
|
public function getSender(): ?Sender\Base
|
|
{
|
|
return $this->sender;
|
|
}
|
|
|
|
/**
|
|
* @param Sender\Base $sender
|
|
* @return $this
|
|
*/
|
|
public function setSender(Sender\Base $sender)
|
|
{
|
|
$this->sender = $sender;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @param array $headers
|
|
* @return Message
|
|
*/
|
|
public function setHeaders(array $headers): Message
|
|
{
|
|
$this->headers = $headers;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return array
|
|
*/
|
|
public function getHeaders()
|
|
{
|
|
return $this->headers;
|
|
}
|
|
|
|
/**
|
|
* @param int $authorId
|
|
* @return Message
|
|
*/
|
|
public function setAuthorId(int $authorId): Message
|
|
{
|
|
$this->authorId = $authorId;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
public function getAuthorId()
|
|
{
|
|
return $this->authorId;
|
|
}
|
|
|
|
/**
|
|
* @return Error|null
|
|
*/
|
|
public function getError(): ?Error
|
|
{
|
|
return $this->error;
|
|
}
|
|
|
|
/**
|
|
* @param Error $error
|
|
* @return Message
|
|
*/
|
|
public function setError(Error $error): self
|
|
{
|
|
$this->error = $error;
|
|
|
|
return $this;
|
|
}
|
|
|
|
public function getStatusId()
|
|
{
|
|
return $this->statusId;
|
|
}
|
|
|
|
public function update(array $fields): bool
|
|
{
|
|
$updateResult = MessageTable::update($this->id, $fields);
|
|
if (!$updateResult->isSuccess())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$this->setFields($fields);
|
|
|
|
// events
|
|
$eventFields = array_merge(['ID' => $this->id], $fields);
|
|
Main\EventManager::getInstance()->send(new Main\Event(
|
|
'messageservice',
|
|
static::EVENT_MESSAGE_UPDATED,
|
|
$eventFields)
|
|
);
|
|
Pull::onMessagesUpdate([$eventFields]);
|
|
|
|
return true;
|
|
}
|
|
|
|
public function updateWithSendResult(Result\SendMessage $result, DateTime $nextExec): void
|
|
{
|
|
$toUpdate = ['SUCCESS_EXEC' => 'E', 'DATE_EXEC' => new DateTime()];
|
|
if ($result->isSuccess())
|
|
{
|
|
$toUpdate['SUCCESS_EXEC'] = 'Y';
|
|
if ($result->getExternalId() !== null)
|
|
{
|
|
if (is_array($result->getExternalId()))
|
|
{
|
|
$systemException = new \Bitrix\Main\SystemException('ExternalId is array: ' . print_r($result->getExternalId(), true));
|
|
\Bitrix\Main\Application::getInstance()->getExceptionHandler()->writeToLog($systemException);
|
|
|
|
$result->addError(new \Bitrix\Main\Error(
|
|
'ExternalId is array',
|
|
'WRONG_EXTERNAL_ID',
|
|
$result->getExternalId()
|
|
));
|
|
}
|
|
|
|
$toUpdate['EXTERNAL_ID'] = $result->getExternalId();
|
|
}
|
|
if ($result->getStatus() !== null)
|
|
{
|
|
$toUpdate['STATUS_ID'] = $result->getStatus();
|
|
}
|
|
}
|
|
elseif ($result->getStatus() === MessageStatus::DEFERRED)
|
|
{
|
|
$toUpdate = array(
|
|
'SUCCESS_EXEC' => 'N',
|
|
'NEXT_EXEC' => $nextExec,
|
|
'STATUS_ID' => MessageStatus::DEFERRED
|
|
);
|
|
}
|
|
else
|
|
{
|
|
$toUpdate['STATUS_ID'] = MessageStatus::ERROR;
|
|
}
|
|
|
|
$errors = $result->getErrorMessages();
|
|
if ($errors)
|
|
{
|
|
$toUpdate['EXEC_ERROR'] = implode(PHP_EOL, $errors);
|
|
}
|
|
|
|
$this->update($toUpdate);
|
|
}
|
|
|
|
public function updateStatusByExternalStatus(string $externalStatus): bool
|
|
{
|
|
|
|
$newInternalStatus = $this->sender::resolveStatus($externalStatus);
|
|
|
|
$isUpdateSuccess = MessageTable::updateMessageStatuses(
|
|
$this->id,
|
|
$newInternalStatus,
|
|
$externalStatus
|
|
);
|
|
|
|
if (!$isUpdateSuccess)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$this->statusId = $newInternalStatus;
|
|
|
|
// events
|
|
$eventFields = ['ID' => $this->id, 'STATUS_ID' => $this->statusId];
|
|
Main\EventManager::getInstance()->send(new Main\Event(
|
|
'messageservice',
|
|
static::EVENT_MESSAGE_UPDATED,
|
|
$eventFields)
|
|
);
|
|
Pull::onMessagesUpdate([$eventFields]);
|
|
|
|
return true;
|
|
}
|
|
|
|
public function updateStatus(int $newStatusId): bool
|
|
{
|
|
$updateResult = MessageTable::updateStatusId($this->id, $newStatusId);
|
|
if (!$updateResult)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
$this->statusId = $newStatusId;
|
|
|
|
// events
|
|
$eventFields = ['ID' => $this->id, 'STATUS_ID' => $this->statusId];
|
|
Main\EventManager::getInstance()->send(new Main\Event(
|
|
'messageservice',
|
|
static::EVENT_MESSAGE_UPDATED,
|
|
$eventFields)
|
|
);
|
|
Pull::onMessagesUpdate([$eventFields]);
|
|
|
|
return true;
|
|
}
|
|
|
|
private function setFields(array $fields): void
|
|
{
|
|
if (!$this->sender && isset($fields['SENDER_ID']))
|
|
{
|
|
$sender = Sender\SmsManager::getSenderById($fields['SENDER_ID']);
|
|
if ($sender)
|
|
{
|
|
$this->setSender($sender);
|
|
}
|
|
}
|
|
if (isset($fields['ID']))
|
|
{
|
|
$this->id = (int)$fields['ID'];
|
|
}
|
|
if (isset($fields['TYPE']))
|
|
{
|
|
$this->setType($fields['TYPE']);
|
|
}
|
|
if (isset($fields['AUTHOR_ID']))
|
|
{
|
|
$this->setAuthorId((int)$fields['AUTHOR_ID']);
|
|
}
|
|
if (isset($fields['MESSAGE_FROM']))
|
|
{
|
|
$this->setFrom($fields['MESSAGE_FROM']);
|
|
}
|
|
if (isset($fields['MESSAGE_TO']))
|
|
{
|
|
$this->setTo($fields['MESSAGE_TO']);
|
|
}
|
|
if (
|
|
isset($fields['MESSAGE_TEMPLATE'])
|
|
&& $this->sender->isConfigurable()
|
|
&& $this->sender->isTemplatesBased()
|
|
)
|
|
{
|
|
$fields['MESSAGE_HEADERS'] = is_array($fields['MESSAGE_HEADERS']) ? $fields['MESSAGE_HEADERS'] : [];
|
|
$fields['MESSAGE_HEADERS']['template'] = $this->sender->prepareTemplate($fields['MESSAGE_TEMPLATE']);
|
|
}
|
|
if (isset($fields['MESSAGE_HEADERS']) && is_array($fields['MESSAGE_HEADERS']))
|
|
{
|
|
$this->setHeaders($fields['MESSAGE_HEADERS']);
|
|
}
|
|
if (isset($fields['MESSAGE_BODY']))
|
|
{
|
|
$messageBody = $this->sender
|
|
? $this->sender->prepareMessageBodyForSave($fields['MESSAGE_BODY'])
|
|
: $fields['MESSAGE_BODY']
|
|
;
|
|
$this->setBody($messageBody);
|
|
}
|
|
if (isset($fields['STATUS_ID']))
|
|
{
|
|
$this->statusId = $fields['STATUS_ID'];
|
|
}
|
|
if (isset($fields['EXTERNAL_STATUS']))
|
|
{
|
|
$this->externalStatus = $fields['EXTERNAL_STATUS'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param bool $checkRestrictions
|
|
*/
|
|
public function setCheckRestrictions(bool $checkRestrictions): void
|
|
{
|
|
$this->checkRestrictions = $checkRestrictions;
|
|
}
|
|
}
|