use app\jobs\SendTelegramMessageDBJob;
use app\jobs\SendTelegramMessageJob;
+use app\jobs\SendWhatsappMessageJob;
use DateTime;
use DateTimeZone;
use Yii;
return ExitCode::OK;
}
+
+ public function actionSendWhatsappMessage()
+ {
+ $messagesSettings = UsersMessageManagement::find()->one();
+ if (!$messagesSettings) {
+ $this->stdout(
+ "Рассылка неактивна (не найдена настройка). Отправка сообщений прервана.\n",
+ BaseConsole::FG_RED
+ );
+ return ExitCode::UNAVAILABLE;
+ }
+
+ if (!$messagesSettings->active) {
+ $this->stdout(
+ "Рассылка неактивна (поле active = 0). Отправка сообщений прервана.\n",
+ BaseConsole::FG_RED
+ );
+ return ExitCode::UNAVAILABLE;
+ }
+
+ $this->stdout("Рассылка активна. Начинаем отправку второго сообщения...\n", BaseConsole::FG_GREEN);
+ date_default_timezone_set('Europe/Moscow');
+
+ $step1 = $messagesSettings ? $messagesSettings->day_before_step1 : 10;
+ $step2 = $messagesSettings ? $messagesSettings->day_before_step2 : 4;
+ $currentDate = date('Y-m-d');
+ $targetDate = date('Y-m-d', strtotime("+$step2 days", strtotime($currentDate)));
+ $kogortDate = date('Y-m-d', strtotime("-$step1 days", strtotime($targetDate)));
+
+ $kogortPhones = SentKogort::find()
+ ->select('phone')
+ ->andWhere(['kogort_number' => 2])
+ ->andWhere(['target_date' => $targetDate])
+ ->andWhere(['purchase' => 0])
+ ->column();
+
+ if (!empty($kogortPhones)) {
+ $countPhones = count($kogortPhones);
+ $this->stdout(
+ "Всего телефонов в когорте {$countPhones} записей.\n",
+ BaseConsole::FG_GREEN
+ );
+
+ $sentStatusKogort = SentKogort::find()
+ ->select('phone')
+ ->andWhere(['kogort_number' => 2])
+ ->andWhere(['target_date' => $targetDate])
+ ->andWhere(['status' => 3])
+ ->column();
+
+ $phonesArray = array_diff($kogortPhones, $sentStatusKogort);
+ $countWhatsappPhones = count($phonesArray);
+ $this->stdout(
+ "Всего телефонов в рассылке телеграма {$countWhatsappPhones} записей.\n",
+ BaseConsole::FG_GREEN
+ );
+
+ $channel = self::getChannelByName('WABA');
+ $limit = $channel['limit'] ?? 250;
+
+ if (!empty($phonesArray)) {
+ $messageText = $messagesSettings
+ ->replaceShortcodes($messagesSettings->offer_whatsapp, $targetDate);
+ $phonesSentArray = [];
+ foreach ($phonesArray as $index => $phone) {
+ if ($index >= $limit) {
+ break;
+ }
+ $messageData = [];
+ $messageData['phone'] = $phone;
+ $messageData['kogort_date'] = $kogortDate;
+ $messageData['target_date'] = $targetDate;
+ $messageData['message'] = $messageText;
+
+ Yii::$app->queue->push(new SendWhatsappMessageJob([
+ 'messageData' => $messageData,
+ 'isTest' => false
+ ]));
+ $phonesSentArray[] = $phone;
+ }
+ //TODO - перенос в отправку
+ $updatedCount = SentKogort::updateAll(
+ [
+ 'status' => SentKogort::STATUSES['second'], // Устанавливаем статус "вторая рассылка"
+ 'updated_at' => date('Y-m-d H:i:s'),
+ ],
+ [
+ 'target_date' => $targetDate,
+ 'kogort_number' => SentKogort::KOGORT_NUMBERS['whatsapp'],
+ 'phone' => $phonesSentArray,
+ ]
+ );
+
+ if ($updatedCount) {
+ $this->stdout(
+ "Статус записей для когорты {$kogortDate} обновлён на 'second' для {$updatedCount} записей.\n",
+ BaseConsole::FG_GREEN
+ );
+ } else {
+ $this->stdout(
+ "Не найдено записей для обновления статуса на 'second'.\n",
+ BaseConsole::FG_RED
+ );
+ }
+ }
+ } else {
+ $this->stdout(
+ "Нет данных для отправки второго сообщения в телеграм
+ на {$kogortDate} для целевой даты {$targetDate}.\n",
+ BaseConsole::FG_RED
+ );
+ }
+
+ $this->stdout(
+ "Отправка второго сообщения в телеграм для корорты (ватсап)
+ на {$kogortDate} для целевой даты {$targetDate}.\n",
+ BaseConsole::FG_GREEN
+ );
+ return ExitCode::OK;
+ }
+
public function actionGenerateCallKogorts()
{
$messagesSettings = UsersMessageManagement::find()->one();
<?php
return [
+ 'WHATSAPP_API_KEY' => '10c81eda-1dfb-42ed-a458-944f8dae4a67',
'API2_URL' => YII_DEBUG ? 'http://host.docker.internal:5555' : 'https://api2.bazacvetov24.ru',
//'TELEGRAM_API_URL' => "https://api.telegram.org/bot6189425433:AAFQ91OYiMiyj2jgIgmx3O2yTBl4enywySM/",
'TELEGRAM_API_URL' => "https://api.telegram.org/bot8063257458:AAGnMf4cxwJWlYLF1wS_arn4PrOaLs9ERQQ/",
namespace app\controllers;
+use app\jobs\SendWhatsappMessageJob;
+use Yii;
+use yii\web\Response;
use yii_app\records\UsersWhatsappMessage;
use yii_app\records\UsersWhatsappMessageSearch;
use yii\web\Controller;
use yii\web\NotFoundHttpException;
use yii\filters\VerbFilter;
+use yii_app\services\WhatsAppService;
/**
* UsersWhatsappMessageController implements the CRUD actions for UsersWhatsappMessage model.
throw new NotFoundHttpException('The requested page does not exist.');
}
+
+ /**
+ * Отображает форму для тестирования отправки сообщений и вызова методов API.
+ */
+ public function actionTest()
+ {
+ return $this->render('test');
+ }
+
+ /**
+ * Обрабатывает нажатие кнопки "Отправить сообщения".
+ * Из поля ввода берутся телефоны, разделённые запятыми, и для каждого телефона
+ * ставится задача в очередь на отправку сообщения.
+ */
+ public function actionSendMessages()
+ {
+ $phonesStr = Yii::$app->request->post('phones', '');
+ $phones = array_filter(array_map('trim', explode(',', $phonesStr)));
+
+ if (empty($phones)) {
+ Yii::$app->session->setFlash('error', 'Не указаны телефоны.');
+ return $this->redirect(['index']);
+ }
+
+ foreach ($phones as $phone) {
+ $messageData = [
+ 'phone' => $phone,
+ 'message' => '',
+ 'kogort_date' => date('Y-m-d'),
+ 'target_date' => date('Y-m-d'),
+ ];
+ Yii::$app->queue->push(new SendWhatsappMessageJob([
+ 'messageData' => $messageData,
+ 'isTest' => true
+ ]));
+ }
+
+ Yii::$app->session->setFlash('success', 'Сообщения поставлены в очередь для отправки.');
+ return $this->redirect(['index']);
+ }
+
+ /**
+ * Вызывает метод получения канала по имени и возвращает результат в формате JSON.
+ */
+ public function actionGetChannel()
+ {
+ Yii::$app->response->format = Response::FORMAT_JSON;
+ $channelName = Yii::$app->request->post('channelName', '');
+ $result = WhatsAppService::getChannelByName($channelName);
+ return ['result' => $result];
+ }
+
+ /**
+ * Вызывает метод получения id каскада по имени и возвращает результат в формате JSON.
+ */
+ public function actionGetCascade()
+ {
+ Yii::$app->response->format = Response::FORMAT_JSON;
+ $cascadeName = Yii::$app->request->post('cascadeName', '');
+ $result = WhatsAppService::getCascadeIdByName($cascadeName);
+ return ['result' => $result];
+ }
+
+ /**
+ * Вызывает метод получения id шаблона по subjectId и имени шаблона и возвращает результат в формате JSON.
+ */
+ public function actionGetTemplate()
+ {
+ Yii::$app->response->format = Response::FORMAT_JSON;
+ $subjectId = Yii::$app->request->post('subjectId', '');
+ $templateName = Yii::$app->request->post('templateName', '');
+ $result = WhatsAppService::getMessageMatcherIdBySubjectId($subjectId, $templateName);
+ return ['result' => $result];
+ }
}
--- /dev/null
+<?php
+
+namespace app\jobs;
+
+use yii\base\BaseObject;
+use yii\queue\JobInterface;
+use Yii;
+use yii_app\services\WhatsAppMessageResponse;
+use yii_app\services\WhatsAppService;
+use yii_app\records\UsersMessageManagement;
+use yii_app\records\UsersWhatsappMessage;
+
+/**
+ * Job для отправки сообщения WhatsApp.
+ *
+ * @property array $messageData Массив с данными сообщения. Ожидается наличие по крайней мере:
+ * - phone: номер телефона получателя,
+ * - kogort_date: дата когорты (опционально),
+ * - target_date: целевая дата (опционально).
+ *
+ * @property bool $isTest тестовое ли сообщение
+ */
+class SendWhatsappMessageJob extends BaseObject implements JobInterface
+{
+ public $messageData;
+
+ public $isTest;
+
+ public static $messagesSent = 0;
+ public static $lastResetTime;
+
+ public function execute($queue)
+ {
+ // Rate limiting: не более 30 сообщений в секунду
+ if (!self::$lastResetTime || (microtime(true) - self::$lastResetTime) > 1) {
+ self::$lastResetTime = microtime(true);
+ self::$messagesSent = 0;
+ }
+ if (self::$messagesSent >= 30) {
+ $delay = 1 - (microtime(true) - self::$lastResetTime);
+ if ($delay > 0) {
+ usleep($delay * 1e6); // Спим оставшееся время в микросекундах
+ }
+ self::$lastResetTime = microtime(true);
+ self::$messagesSent = 0;
+ }
+
+ $phone = $this->messageData['phone'];
+
+ $apiKey = Yii::$app->params['WHATSAPP_API_KEY'];
+ $cascadeId = WhatsAppService::getCascadeIdByName('WABA') ?? 5686;
+ $whatsappService = new WhatsAppService($apiKey, $cascadeId);
+ $requestId = uniqid();
+ try {
+
+ $message = $this->messageData['message'];
+ if ($this->isTest) {
+ $message = "Здравствуйте\n
+ Узнать подробности вы можете на нашем сайте https://bazacvetov24.ru.";
+ }
+ $response = $whatsappService->sendMessage($requestId, $phone, $message, $this->isTest);
+
+
+ $status = 'sent';
+ if (!$status instanceof WhatsAppMessageResponse) {
+ $status = $response ?? 'error';
+ }
+ $record = new UsersWhatsappMessage();
+ $record->request_id = $requestId;
+ $record->phone = $phone;
+ $record->message = $message;
+ $record->kogort_date = $this->messageData['kogort_date'];
+ $record->target_date = $this->messageData['target_date'];
+ $record->status = $status;
+ $record->created_at = date('Y-m-d H:i:s');
+
+ if ($record->save()) {
+ Yii::warning("WhatsApp сообщение успешно отправлено и сохранено для телефона {$phone}. Request ID: " . $response->requestId, 'whatsapp');
+ } else {
+ Yii::warning("WhatsApp сообщение отправлено, но не удалось сохранить запись для телефона {$phone}.", 'whatsapp');
+ }
+ } catch (\Exception $e) {
+ Yii::error("Ошибка отправки WhatsApp сообщения для телефона {$phone}: " . $e->getMessage(), 'whatsapp');
+ }
+
+ self::$messagesSent++;
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace app\controllers;
+
+use Yii;
+use yii\rest\Controller;
+use yii\web\BadRequestHttpException;
+use yii\web\MethodNotAllowedHttpException;
+use yii\web\ServerErrorHttpException;
+use yii_app\records\UsersWhatsappMessage;
+use yii\web\Response;
+
+class WhatsappMessageStatus extends Controller
+{
+ /**
+ * Принимает callback-статус сообщения от API edna и сохраняет новый статус в БД.
+ *
+ * Пример тела запроса:
+ * {
+ * "requestId": "test-00135",
+ * "cascadeId": 11,
+ * "cascadeStageUUID": "001-test001",
+ * "subject": "test_subject",
+ * "subjectId": 2,
+ * "status": "READ",
+ * "statusAt": "2023-10-31T11:07:56Z",
+ * "error": null,
+ * "comment": null,
+ * "paymentData": {
+ * "@type": "WhatsAppConversationPaymentData",
+ * "conversationId": "test0001",
+ * "conversationType": "marketing",
+ * "chargeable": true,
+ * "type": "WHATSAPP_CONVERSATION"
+ * }
+ * }
+ *
+ * @return array Ответ в формате JSON.
+ *
+ * @throws BadRequestHttpException Если отсутствует обязательный параметр или неверный JSON.
+ * @throws MethodNotAllowedHttpException Если запрос не методом POST.
+ * @throws ServerErrorHttpException Если не удалось сохранить запись.
+ */
+ public function actionCallbackStatus()
+ {
+ Yii::$app->response->format = Response::FORMAT_JSON;
+ $request = Yii::$app->request;
+
+ if (!$request->isPost) {
+ throw new MethodNotAllowedHttpException("Метод не разрешён. Используйте POST.");
+ }
+
+ $rawBody = $request->getRawBody();
+ $data = json_decode($rawBody, true);
+
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ Yii::error("Неверный JSON: " . $rawBody, __METHOD__);
+ throw new BadRequestHttpException("Неверный формат JSON.");
+ }
+
+ if (empty($data['requestId'])) {
+ throw new BadRequestHttpException("Отсутствует обязательный параметр: requestId.");
+ }
+
+ $requestId = $data['requestId'];
+
+ $model = UsersWhatsappMessage::findOne(['request_id' => $requestId]);
+ if (!$model) {
+ Yii::warning("Запись с request_id {$requestId} не найдена.", __METHOD__);
+ return ['message' => "Запись с request_id {$requestId} не найдена."];
+ }
+
+ if (isset($data['status'])) {
+ $model->status = $data['status'];
+ } else {
+ throw new BadRequestHttpException("Отсутствует обязательный параметр: status.");
+ }
+
+ if ($model->save()) {
+ Yii::info("Статус сообщения с request_id {$requestId} обновлён на {$model->status}.", __METHOD__);
+ return ['message' => "Статус сообщения обновлён."];
+ } else {
+ Yii::error("Не удалось сохранить обновлённый статус для request_id {$requestId}.", __METHOD__);
+ throw new ServerErrorHttpException("Не удалось сохранить обновлённый статус.");
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+
+namespace yii_app\services;
+
+/**
+ * Класс для представления ответа от API WhatsApp.
+ */
+class WhatsAppMessageResponse
+{
+ /**
+ * Идентификатор сообщения, сгенерированный на клиенте.
+ *
+ * @var string|null
+ */
+ public $requestId;
+
+ /**
+ * Конструктор на основе данных, полученных в ответе.
+ *
+ * @param array $data Ассоциативный массив с данными ответа.
+ */
+ public function __construct(array $data)
+ {
+ $this->requestId = $data['requestId'] ?? null;
+ }
+}
--- /dev/null
+<?php
+
+namespace yii_app\services;
+
+use GuzzleHttp\Client;
+use GuzzleHttp\Exception\GuzzleException;
+use GuzzleHttp\Exception\RequestException;
+use Psr\Http\Message\ResponseInterface;
+use Yii;
+use yii\base\Arrayable;
+use yii\base\Exception;
+use yii_app\records\UsersMessageManagement;
+
+/**
+ * Сервис для отправки сообщений WhatsApp через API.
+ *
+ * Для массовой рассылки создаётся один экземпляр сервиса,
+ * а метод sendMessage принимает только номер телефона получателя.
+ * Текст сообщения (офер) берётся из таблицы UsersMessageManagement.
+ */
+class WhatsAppService
+{
+ private static $apiBaseUrl = 'https://app.edna.ru/api';
+
+ /**
+ * API-ключ для авторизации.
+ *
+ * @var string
+ */
+ private $apiKey;
+
+ /**
+ * Идентификатор каскада.
+ *
+ * @var mixed
+ */
+ private $cascadeId;
+
+ /**
+ * HTTP-клиент Guzzle.
+ *
+ * @var Client
+ */
+ private $client;
+
+ /**
+ * Конструктор.
+ *
+ * @param string $apiKey API-ключ для авторизации.
+ * @param mixed $cascadeId Идентификатор каскада для отправки сообщений.
+ */
+ public function __construct($apiKey, $cascadeId)
+ {
+ $this->apiKey = $apiKey;
+ $this->cascadeId = $cascadeId;
+ $this->client = new Client();
+ }
+
+ /**
+ *
+ * @param string $text Исходный текст.
+ *
+ * @return string Обработанный текст.
+ */
+ protected function escapeText($text)
+ {
+ return str_replace(
+ ['"', '“', '”', '‘', '’'],
+ ['\"', '\"', '\"', "\'", "\'"],
+ $text
+ );
+ }
+
+ /**
+ *
+ * @param int $statusCode Код ответа HTTP.
+ * @param string|null $errorKey Код ошибки, полученный из ответа API.
+ *
+ * @return string|null Описание ошибки или null, если соответствие не найдено.
+ */
+ protected function getErrorMessage($statusCode, $errorKey)
+ {
+ $errorMappings = [
+ 400 => [
+ 'requestId-is-not-unique' => 'Такой идентификатор запроса уже использовался. Используйте новый идентификатор для каждого запроса.',
+ 'content-not-specified' => 'Не указан тип контента и его свойства. Например, smsContent, viberContent или whatsappContent.',
+ 'contentType-not-specified' => 'Не указан тип контента. Например, text или image.',
+ 'text-not-specified' => 'Не заполнено текстовое поле.',
+ 'caption-not-specified' => 'Не заполнено текстовое поле подписи.',
+ 'action-not-specified' => 'Не указано действие для кнопки.',
+ 'attachmentName-not-specified' => 'Не указано имя прикрепляемого документа.',
+ 'attachmentName-is-too-long' => 'Имя прикрепляемого документа слишком длинное. Максимальная длина — 70 символов.',
+ 'latitude-not-specified' => 'Не задана широта при указании координат.',
+ 'longitude-not-specified' => 'Не задана долгота при указании координат.',
+ 'cascade-not-found' => 'Указан неверный идентификатор каскада. Проверьте корректность указанного вами идентификатора.',
+ 'request-doesn’t-contain-content-for-all-cascade-stages' => 'Каскад содержит много каналов. Добавьте еще один канал в объект content запроса.',
+ 'matched-template-not-found' => 'Схема тела запроса не соответствует схеме шаблона. Проверьте взаимное расположение и наличие всех свойств запроса.',
+ 'cascade-scheduling-request-not-valid' => 'Переданный контент для каскада не соответствует настройкам каскада. Проверьте корректность заполнения данных.',
+ 'template-parameter-is-not-valid' => 'Длина значений параметров listPicker.sections.items.title или listPicker.sections.items.subtitle превышает 24 символа с учетом пробелов.',
+ 'out-of-balance' => 'Недостаточно средств на балансе.',
+ 'button-validation-error' => 'Ошибка валидации шаблона WhatsApp с кнопками. Превышено максимальное количество кнопок или неверный тип кнопки.',
+ ],
+ 401 => [
+ 'auth-error' => 'Ошибка авторизации. Проверьте правильность написания и срок действия ключа API.',
+ ],
+ 404 => [
+ 'not-found' => 'Запрошенный URL-адрес не найден. Проверьте корректность указанного вами адреса.',
+ ],
+ 405 => [
+ 'method-not-allowed' => 'Метод HTTP-запроса не разрешен. Используйте POST, GET и другие запросы согласно документации.',
+ ],
+ 500 => [
+ 'system-error' => 'Ошибка сервера. Обратитесь в службу технической поддержки support@edna.ru.',
+ ],
+ ];
+
+ if (isset($errorMappings[$statusCode][$errorKey])) {
+ return $errorMappings[$statusCode][$errorKey];
+ }
+ return null;
+ }
+
+ /**
+ * Отправляет сообщение WhatsApp для заданного номера телефона.
+ *
+ * @param string $phone Номер телефона получателя (например, "79000000000").
+ * @param string|null $startTime Время отправки (ISO 8601) или null для немедленной отправки.
+ * @param string|null $ttl Время жизни задания (например, 'PT1M').
+ *
+ * @return WhatsAppMessageResponse|null|string Объект с данными ответа API.
+ *
+
+ */
+ public function sendMessage($requestId, $phone, $message, $startTime = null, $ttl = null, $isTest = false)
+ {
+
+ if (!$message) {
+ Yii::error("Текст сообщения для WhatsApp не передан.");
+ return null;
+ }
+ $channel = self::getChannelByName('WABA');
+ $subjectId = $channel['subjectId'] ?? 11374;
+
+ $requestId = $requestId ?? uniqid();
+ // Формируем фильтр получателя по номеру телефона
+ $subscriberFilter = [
+ 'address' => $phone,
+ 'type' => 'PHONE'
+ ];
+
+ $buttons = [
+ 'rows' => [
+ [
+ 'buttons' => [
+ [
+ 'text' => 'Наш сайт',
+ 'url' => 'https://bazacvetov24.ru',
+ 'urlPostfix' => '',
+ 'type' => 'URL'
+ ],
+ [
+ 'text' => '1000 руб. забрать',
+ 'url' => 'https://bazacvetov24.ru/akcii',
+ 'urlPostfix' => '',
+ 'type' => 'URL'
+ ]
+ ]
+ ]
+ ]
+ ];
+ // Формируем содержимое WhatsApp-сообщения
+ $whatsappContent = [
+ 'contentType' => 'TEXT',
+ 'text' => $this->escapeText($message),
+ ];
+
+ if (!$isTest) {
+ $whatsappContent['keyboard'] = $buttons;
+ $whatsappContent['messageMatcherId'] = self::getMessageMatcherIdBySubjectId($subjectId, 'kogort_message') ?? 120669;
+ } else {
+ $whatsappContent['messageMatcherId'] = self::getMessageMatcherIdBySubjectId($subjectId, 'podrobnosti') ?? 117216;
+ }
+
+
+ $payload = [
+ 'requestId' => $requestId,
+ 'cascadeId' => $this->cascadeId,
+ 'subscriberFilter' => $subscriberFilter,
+ 'content' => [
+ 'whatsappContent' => $whatsappContent,
+ ],
+ 'errorIfNotMatched' => true,
+ 'comment' => '',
+ 'priority' => 'DEFAULT',
+ ];
+
+ if ($startTime !== null) {
+ $payload['startTime'] = $startTime;
+ }
+ if ($ttl !== null) {
+ $payload['ttl'] = $ttl;
+ }
+ $apiUrl = self::$apiBaseUrl . '/cascade/schedule';
+ try {
+ $response = $this->client->request('POST', $apiUrl, [
+ 'headers' => [
+ 'Content-Type' => 'application/json',
+ 'X-API-KEY' => $this->apiKey,
+ ],
+ 'json' => $payload,
+ ]);
+
+ if ($response->getStatusCode() == 200) {
+ $data = json_decode($response->getBody(), true);
+ return new WhatsAppMessageResponse($data);
+ } else {
+ $data = json_decode($response->getBody(), true);
+ $errorKey = $data['title'] ?? null;
+ $errorDetail = $data['detail'] ?? '';
+ $errorMessage = $this->getErrorMessage($response->getStatusCode(), $errorKey);
+ if (!$errorMessage) {
+ $errorMessage = "Ошибка: код " . $response->getStatusCode() . ". " . $errorDetail;
+ }
+ Yii::error($errorMessage);
+ return $errorKey;
+ }
+ } catch (RequestException $e) {
+ Yii::error("Ошибка при выполнении запроса: " . $e->getMessage());
+ return null;
+ }
+ }
+
+
+ /**
+ * Получает subjectId канала по его имени.
+ *
+ * @param string $channelName Название канала (например, "WABA").
+ *
+ * @return int|null subjectId, если найден, иначе null.
+ *
+ */
+ public static function getChannelByName($channelName)
+ {
+ $apiKey = Yii::$app->params['WHATSAPP_API_KEY'];
+ $client = new Client();
+
+ $url = self::$apiBaseUrl . '/channel-profile?types=WHATSAPP';
+
+ try {
+ $response = $client->request('GET', $url, [
+ 'headers' => [
+ 'Content-Type' => 'application/json',
+ 'X-API-KEY' => $apiKey,
+ ],
+ 'json' => (object)[]
+ ]);
+
+ if ($response->getStatusCode() == 200) {
+ $data = json_decode($response->getBody(), true);
+ foreach ($data as $channel) {
+ if (isset($channel['name']) && $channel['name'] === $channelName) {
+ return $channel ?? null;
+ }
+ }
+ return null;
+ } else {
+ self::handleErrorResponse($response);
+ return null;
+ }
+ } catch (RequestException $e) {
+ Yii::error("Ошибка при выполнении запроса: " . $e->getMessage());
+ return null;
+ }
+ }
+
+ public static function getCascadeIdByName($cascadeName)
+ {
+ $apiKey = Yii::$app->params['WHATSAPP_API_KEY'];
+ $client = new Client();
+ $url = self::$apiBaseUrl . '/cascade/get-all';
+
+ try {
+ $response = $client->request('POST', $url, [
+ 'headers' => [
+ 'Content-Type' => 'application/json',
+ 'X-API-KEY' => $apiKey,
+ ],
+ 'json' => (object)[]
+ ]);
+
+ if ($response->getStatusCode() == 200) {
+ $data = json_decode($response->getBody(), true);
+ if (!is_array($data)) {
+ throw new Exception("Некорректный формат ответа.");
+ }
+ foreach ($data as $cascade) {
+ if (
+ isset($cascade['name'], $cascade['status'], $cascade['id']) &&
+ $cascade['name'] === $cascadeName &&
+ strtoupper($cascade['status']) === 'ACTIVE'
+ ) {
+ return $cascade['id'];
+ }
+ }
+ return null;
+ } else {
+ self::handleErrorResponse($response);
+ return null;
+ }
+ } catch (RequestException $e) {
+ Yii::error("Ошибка при выполнении запроса: " . $e->getMessage());
+ return null;
+ }
+ }
+
+ public static function getMessageMatcherIdBySubjectId(
+ $subjectId,
+ $templateName,
+ $matcherTypes = ["OPERATOR", "USER", "CUSTOM"]
+ ) {
+ $apiKey = Yii::$app->params['WHATSAPP_API_KEY'];
+ $client = new Client();
+ $url = self::$apiBaseUrl . '/message-matchers/get-by-request';
+
+ try {
+ $response = $client->request('POST', $url, [
+ 'headers' => [
+ 'Content-Type' => 'application/json',
+ 'X-API-KEY' => $apiKey,
+ ],
+ 'json' => [
+ 'subjectId' => $subjectId,
+ 'matcherTypes' => $matcherTypes,
+ ]
+ ]);
+
+ if ($response->getStatusCode() == 200) {
+ $data = json_decode($response->getBody(), true);
+ if (!is_array($data)) {
+ throw new Exception("Некорректный формат ответа.");
+ }
+
+ // Ищем шаблон с нужным именем и каналом WHATSAPP
+ foreach ($data as $matcher) {
+ if (
+ isset($matcher['name'], $matcher['channelType'], $matcher['id']) &&
+ $matcher['name'] === $templateName &&
+ strtoupper($matcher['channelType']) === 'WHATSAPP'
+ ) {
+ return $matcher['id'];
+ }
+ }
+ return null;
+ } else {
+ self::handleErrorResponse($response);
+ return null;
+ }
+ } catch (RequestException $e) {
+ Yii::error("Ошибка при выполнении запроса: " . $e->getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Обрабатывает ошибку ответа API.
+ *
+ * @param ResponseInterface $response
+ *
+ */
+ private static function handleErrorResponse($response)
+ {
+ $data = json_decode($response->getBody(), true);
+ $errorKey = $data['title'] ?? $data['error'] ?? 'unknown_error';
+ $errorDetail = $data['detail'] ?? '';
+
+ $errorMessages = [
+ 'error-subject-unknown' => 'Указанное имя подписи отсутствует.',
+ 'error-syntax' => 'Неверно указан тип канала.',
+ ];
+
+ $errorMessage = $errorMessages[$errorKey] ??
+ "Ошибка: " . $response->getStatusCode() . ". " . $errorKey . ". " . $errorDetail;
+ Yii::error($errorMessage);
+ }
+
+}
\ No newline at end of file
/** @var yii_app\records\UsersWhatsappMessageSearch $searchModel */
/** @var yii\data\ActiveDataProvider $dataProvider */
-$this->title = 'Users Whatsapp Messages';
+$this->title = 'Сообщения из WA когорты';
$this->params['breadcrumbs'][] = $this->title;
?>
-<div class="users-whatsapp-message-index">
+<div class="users-whatsapp-message-index p-4">
<h1><?= Html::encode($this->title) ?></h1>
<p>
- <?= Html::a('Create Users Whatsapp Message', ['create'], ['class' => 'btn btn-success']) ?>
+ <?= Html::a('Тестировать', ['test'], ['class' => 'btn btn-primary']) ?>
</p>
<?php // echo $this->render('_search', ['model' => $searchModel]); ?>
--- /dev/null
+<?php
+
+use yii\helpers\Html;
+use yii\helpers\Url;
+use yii\widgets\ActiveForm;
+use yii\base\DynamicModel;
+
+$model = new DynamicModel([
+ 'phones', 'channelName', 'cascadeName', 'subjectId', 'templateName'
+]);
+
+$model->addRule(['phones', 'channelName', 'cascadeName', 'subjectId', 'templateName'], 'safe');
+
+?>
+<div class="test-whatsapp col-6 p-4">
+<p>
+ <?= Html::a('Назад', ['index'], ['class' => 'btn btn-primary']) ?>
+</p>
+
+<?php $form = ActiveForm::begin(); ?>
+
+ <h3>Отправка сообщений</h3>
+<?= $form->field($model, 'phones')->textInput([
+ 'maxlength' => true,
+ 'placeholder' => 'Введите телефоны через запятую'
+]) ?>
+
+ <div class="form-group">
+ <?= Html::submitButton('Отправить сообщения', [
+ 'class' => 'btn btn-primary',
+ 'formaction' => Url::to(['users-whatsapp-message/send-messages'])
+ ]) ?>
+ </div>
+
+ <hr>
+
+ <h3>Получить канал по имени</h3>
+<?= $form->field($model, 'channelName')->textInput([
+ 'maxlength' => true,
+ 'placeholder' => 'Введите имя канала'
+]) ?>
+
+ <div class="form-group">
+ <?= Html::submitButton('Получить канал', [
+ 'class' => 'btn btn-info',
+ 'formaction' => Url::to(['users-whatsapp-message/get-channel'])
+ ]) ?>
+ </div>
+
+ <hr>
+
+ <h3>Получить каскад по имени</h3>
+<?= $form->field($model, 'cascadeName')->textInput([
+ 'maxlength' => true,
+ 'placeholder' => 'Введите имя каскада'
+]) ?>
+
+ <div class="form-group">
+ <?= Html::submitButton('Получить каскад', [
+ 'class' => 'btn btn-info',
+ 'formaction' => Url::to(['users-whatsapp-message/get-cascade'])
+ ]) ?>
+ </div>
+
+ <hr>
+
+ <h3>Получить шаблон по subjectId и имени шаблона</h3>
+<?= $form->field($model, 'subjectId')->textInput([
+ 'maxlength' => true,
+ 'placeholder' => 'Введите subjectId'
+]) ?>
+<?= $form->field($model, 'templateName')->textInput([
+ 'maxlength' => true,
+ 'placeholder' => 'Введите имя шаблона'
+]) ?>
+
+ <div class="form-group">
+ <?= Html::submitButton('Получить шаблон', [
+ 'class' => 'btn btn-info',
+ 'formaction' => Url::to(['users-whatsapp-message/get-template'])
+ ]) ?>
+ </div>
+
+<?php ActiveForm::end(); ?>
+
+</div>