<?php
namespace Slivki\Controller\Telegram;
use Slivki\Dao\User\UserOrderDaoInterface;
use Slivki\Entity\BotChatSession;
use Slivki\Entity\FoodOfferExtension;
use Slivki\Entity\FoodOrder;
use Slivki\Entity\GeoLocation;
use Slivki\Exception\User\InvalidPasswordException;
use Slivki\Services\BelTransSat\RouteBuilderBelTransSatService;
use Slivki\Services\DeliveryService;
use Slivki\Services\Sms\Contract\SmsSenderInterface;
use Slivki\Util\Telegram\CourierBot;
use Slivki\Validator\User\PasswordValidator;
use Symfony\Component\HttpKernel\KernelInterface;
use Symfony\Component\Routing\Annotation\Route;
use Slivki\Entity\Director;
use Slivki\Entity\User;
use Slivki\Util\CommonUtil;
use Slivki\Util\Logger;
use Slivki\Util\Telegram\DeliveryBot;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class DeliveryBotController extends TelegramBotController
{
private DeliveryService $deliveryService;
private UserOrderDaoInterface $userOrderDao;
private PasswordValidator $passwordValidator;
public function __construct(KernelInterface $kernel, UserOrderDaoInterface $userOrderDao, PasswordValidator $passwordValidator)
{
parent::__construct($kernel);
$this->userOrderDao = $userOrderDao;
$this->passwordValidator = $passwordValidator;
}
/** @Route("/tg/delivery/bot/hook") */
public function webHookAction(
Request $request,
DeliveryService $deliveryService,
RouteBuilderBelTransSatService $routeBuilderBelTransSatService,
SmsSenderInterface $smsSender
): Response {
$this->deliveryService = $deliveryService;
$logger = Logger::instance('DeliveryBot');
$logger->info('hook');
$data = json_decode($request->getContent());
$logger->info($request->getContent());
if (property_exists($data, 'message')) {
$this->handleMessage($request, $data);
} else if (property_exists($data, 'callback_query')) {
$this->handleCallback($routeBuilderBelTransSatService, $smsSender, $data, $request->getHost());
}
return new Response();
}
public function handleMessage(Request $request, $data) {
if (!property_exists($data->message, 'text')) {
return;
}
$message = $data->message->text;
$chatID = $data->message->chat->id;
$this->log('chatID ' . $chatID);
$logger = Logger::instance('DeliveryBot');
$logger->info('handleMessage');
$logger->info($message);
$response = null;
$replyMarkup = false;
$entityManager = $this->getDoctrine()->getManager();
$bot = new DeliveryBot($entityManager);
switch ($message) {
case '/start':
$response = "Здравствуйте! Необходима авторизация. Введите email партнерского аккаунта на slivki.by";
$bot->writeSession($chatID, DeliveryBot::STATE_INIT);
break;
default:
$logger->info($message);
$session = $bot->getSession($chatID);
if (CommonUtil::isBeginsWith($message, '/')) {
$exploded = explode(' ', $message);
if ($exploded[0] == '/email') {
$message = $exploded[1];
$session->setLastPage(DeliveryBot::STATE_INIT);
}
$exploded = explode(' ', $message);
if ($exploded[0] == '/password') {
$message = $exploded[1];
$session->setLastPage(DeliveryBot::STATE_PASSWORD);
}
}
if (!$session) {
break;
}
switch ($session->getLastPage()) {
case DeliveryBot::STATE_INIT:
$email = trim(mb_strtolower($message));
/*$emailConstraint = new \Symfony\Component\Validator\Constraints\Email();
$error = $this->get('validator')->validate($email, $emailConstraint);
$this->log(print_r($error, true));
if ($error->count() > 0) {
$response = 'Введите корректный email';
break;
}*/
$director = $entityManager->getRepository(Director::class)->findOneByEmail($email);
$user = $entityManager->getRepository(User::class)->findOneByEmail($email);
if (!$director || !$user) {
$response = 'Пользователь не найден. Введите корректный email';
break;
}
$response = 'Введите пароль';
$bot->writeSession($chatID, DeliveryBot::STATE_PASSWORD, $email);
break;
case DeliveryBot::STATE_PASSWORD:
$email = $session->getData();
$user = $entityManager->getRepository(User::class)->findOneByEmail($email);
try {
$this->passwordValidator->validate($user, $message);
} catch (InvalidPasswordException $exception) {
$response = 'Неправильный пароль';
break;
}
$response = 'Успешно авторизовано, ожидайте заказы.';
$director = $entityManager->getRepository(Director::class)->findOneByEmail($email);
$session->setUserID($director->getID());
$entityManager->flush();
$this->writeSession($chatID, DeliveryBot::STATE_AUTHENTICATED);
break;
case DeliveryBot::STATE_AUTHENTICATED:
$response = 'шо?';
}
}
$this->log('RESPONSE');
$this->log($response);
if ($response != null) {
$bot->sendMessage($chatID, $response, $replyMarkup);
}
}
public function handleCallback(
RouteBuilderBelTransSatService $routeBuilderBelTransSatService,
SmsSenderInterface $smsSender,
$data,
string $domain
) {
$chatID = $data->callback_query->from->id;
$orderID = null;
$message = null;
$status = FoodOrder::DELIVERY_STATUS_INIT;
$entityManager = $this->getDoctrine()->getManager();
$bot = new DeliveryBot($entityManager);
if (CommonUtil::isBeginsWith($data->callback_query->data, 'accept')) {
$orderID = (int)str_replace("accept=", '', $data->callback_query->data);
$status = FoodOrder::DELIVERY_STATUS_ACCEPTED;
$message = 'Заказ принят';
$phoneNumberInOrder = $this->userOrderDao->findPhoneNumberInOrderByOfferOrderId($orderID) ?: $this->getUser()->getPhone();
$smsSender->send(
$this->convertPhoneNumber($phoneNumberInOrder),
'Ваш заказ принят. Благодарим вас за выбор!',
$domain,
);
} else if (CommonUtil::isBeginsWith($data->callback_query->data, 'decline')) {
$orderID = (int)str_replace("decline=", '', $data->callback_query->data);
$status = FoodOrder::DELIVERY_STATUS_DECLINED;
$message = 'Заказ отклонен';
$bot->sendApiRequest('editMessageReplyMarkup', [
'chat_id' => $chatID,
'message_id' => $data->callback_query->message->message_id,
'reply_markup' => null,
]);
} else if (CommonUtil::isBeginsWith($data->callback_query->data, 'cooking_time')) {
$parameters = [];
parse_str($data->callback_query->data, $parameters);
//Dima, hello
$orderId = (int)$parameters['order_id'];
$cookingTime = (int)$parameters['cooking_time'];
$bot->sendApiRequest('editMessageReplyMarkup', [
'chat_id' => $chatID,
'message_id' => $data->callback_query->message->message_id,
'reply_markup' => null,
]);
return;
}
if (!$orderID) {
return;
}
$order = $entityManager->find(FoodOrder::class, $orderID);
if (!$order) {
return;
}
$logger = Logger::instance(\sprintf('DeliveryBotCallback %d', $orderID));
$logger->info(\sprintf('status: %d', $status));
$order->setDeliveryStatus($status);
$entityManager->flush();
if ($status === FoodOrder::DELIVERY_STATUS_ACCEPTED) {
try {
$routeBuilderBelTransSatService->build($order);
} catch (\Exception $e) {
$logger->error($e->getMessage());
}
}
$bot->sendMessage($chatID, $message);
$courierBot = new CourierBot($entityManager);
$chatSessions = $entityManager->getRepository(BotChatSession::class)->findBy(['botID' => CourierBot::BOT_ID]);
if (!$chatSessions || count($chatSessions) == 0) {
$logger->info('no chat session for courier bot');
return;
}
if ($order->getDeliveryStatus() == FoodOrder::DELIVERY_STATUS_ACCEPTED) {
$message = $this->getCourierBotMessage($order);
} else {
$message = 'ВНИМАНИЕ! ЗАКАЗ ОТКЛОНЕН! №' . $orderID;
}
foreach ($chatSessions as $chatSession) {
$logger->info(\sprintf('send message to %d', $chatSession->getChatID()));
$result = $courierBot->sendMessage($chatSession->getChatID(), $message);
$logger->info($result);
}
}
private function getCourierBotMessage(FoodOrder $foodOrder):string {
$message = 'Служба доставки ';
$directors = $foodOrder->getOffer()->getDirectors();
if ($directors->count() > 0) {
$message .= $directors->first()->getName();
}
$message .= "\n";
$locations = $foodOrder->getOffer()->getGeoLocations();
if ($locations->count() > 0) {
/** @var GeoLocation $location */
$location = $locations->first();
$message .= 'Ресторан: ' . $location->getStreet() . ' ' . $location->getHouse() . ' ' . $location->getLabel() . "\n";
}
$message .= 'Заказ №' . $foodOrder->getID() . "\nДоставка: ";
$address = $foodOrder->getDeliveryAddress();
$message .= $address->getStreet()->getCity() . ' ' . $address->getStreet()->getName() . ' ';
$message .= $address->getHouse() . ' ' . $address->getBlock();
if ($address->getAppartment()) {
$message .= ', квартира ' . $address->getAppartment();
}
if ($address->getEntrance()) {
$message .= ', подъезд ' . $address->getEntrance();
}
if ($address->getFloor()) {
$message .= ', этаж ' . $address->getFloor();
}
if ($address->getDoorphone()) {
$message .= ', домофон ' . $address->getDoorphone();
}
$message .= "\nСтоимость доставки " . $foodOrder->getDeliveryCost() . ' руб';
$message .= "\nВремя доставки " . $this->deliveryService->getOrderDate($foodOrder->getDeliveryTime()) . "\n";
$partnerTotal = 0;
foreach ($foodOrder->getOfferOrderDetails() as $detail) {
/** @var FoodOfferExtension $offerExtension */
$offerExtension = $detail->getOfferExtension();
$price = $detail->getPurchasePrice();
$partnerTotal += $detail->getItemsCount() * $price;
$message .= $offerExtension->getName() . ' (' . $offerExtension->getShortDescription() . ') ' . $price . ' руб * ' . $detail->getItemsCount() . "шт. \n";
}
$partnerTotal += $foodOrder->getDeliveryCost();
switch ($foodOrder->getPaymentType()) {
case 1:
$message .= "\nОплачено " . $partnerTotal . ' руб';
break;
case 2:
if ($address->isPickup()) {
$message .= "\nОплата наличными на месте " . $partnerTotal . ' руб';
} else {
$message .= "\nОплата наличными курьеру " . $partnerTotal . ' руб';
}
break;
case 3:
if ($address->isPickup()) {
$message .= "\nОплата картой на месте " . $partnerTotal . ' руб';
} else {
$message .= "\nОплата картой курьеру " . $partnerTotal . ' руб';
}
}
$message .= "\nКлиент: " . $address->getName() . ', номер телефона ' . $address->getPhone();
if ($foodOrder->getChangeAmount()) {
$message .= "\nСдача с: " . $foodOrder->getChangeAmount() . ' руб';
}
if ($foodOrder->getComment()) {
$message .= "\nКомментарий: " . $foodOrder->getComment();
}
return $message;
}
private function convertPhoneNumber(string $phoneNumber): string
{
return \str_replace(['+', '(', ')', '-', ' '], '', $phoneNumber);
}
}