<?php
namespace App\Library\TelegramBot\CandidatesPhotoTelegramBot;
use App\Entity\CalendarEvent;
use App\Entity\Person;
use App\Entity\TelegramUser;
use App\Entity\User;
use App\Enum\CalendarEvent\Type;
use App\Enum\Candidate\Status;
use App\Library\TelegramBot\CandidatesPhotoTelegramBot\Enum\Menu\Menu;
use App\Library\TelegramBot\CandidatesPhotoTelegramBot\Enum\EditableMessage\EditableMessage;
use App\Library\Utils\StringUtils;
use App\Service\CalendarEvent\CalendarEventService;
use App\Service\Candidate\CandidateService;
use App\Service\Person\PersonService;
use App\Service\User\UserService;
class BotNavigation
{
/**
* @var CandidatesPhotoTelegramBot
*/
private $bot;
/**
* @var CalendarEventService
*/
private $calendarEventService;
/**
* @var UserService
*/
private $userService;
/**
* @var CandidateService
*/
private $candidateService;
public function __construct(CandidatesPhotoTelegramBot $telegramBot,
CalendarEventService $calendarEventService, UserService $userService, CandidateService $candidateService)
{
$this->bot = $telegramBot;
$this->calendarEventService = $calendarEventService;
$this->userService = $userService;
$this->candidateService = $candidateService;
}
public function onAnyBotMenuItemSelect($chatId, TelegramUser $user, $selectedItemId, $navSelectedMenuItem)
{
switch($selectedItemId) {
case Menu::PERSON_MENU_ID:
$this->bot->onPersonMenuItemSelect($chatId, $user, $selectedItemId, $navSelectedMenuItem);
break;
case Menu::CALENDAR_EVENT_MENU_ID:
$this->bot->onCalendarEventMenuItemSelect($chatId, $user, $selectedItemId, $navSelectedMenuItem);
break;
case Menu::AUTHOR_MENU_ID:
$this->bot->onAuthorMenuItemSelect($chatId, $user, $selectedItemId, $navSelectedMenuItem);
break;
case Menu::STATUS_MENU_ID:
$this->bot->onStatusMenuItemSelect($chatId, $user, $selectedItemId, $navSelectedMenuItem);
break;
case Menu::CANDIDATE_FIELD_MENU_ID:
$this->onCandidateFieldMenuItemSelect($chatId, $user, $selectedItemId, $navSelectedMenuItem);
break;
case Menu::OTHER_MENU_ID:
$this->onOtherMenuItemSelect($chatId, $user, $selectedItemId, $navSelectedMenuItem);
break;
case Menu::AFTER_CANDIDATE_SEND_MENU_ID:
$this->onAfterCandidateSendMenuItemSelect($chatId, $user, $selectedItemId, $navSelectedMenuItem);
break;
default:
throw new \Exception("Unknown: " . $selectedItemId);
}
}
private function onAfterCandidateSendMenuItemSelect($chatId, TelegramUser $user, $selectedItemId, $navSelectedMenuItem)
{
switch($navSelectedMenuItem['id']) {
case Menu::COPY_LAST_CANDIDATE_MENU_ITEM_ID:
$this->bot->onCopyLastCandidateMenuItemSelect($chatId, $user, $navSelectedMenuItem, Menu::CANDIDATE_FIELD_MENU_ID);
break;
default:
throw new \Exception("Unknown: " . $navSelectedMenuItem['id']);
}
}
private function onOtherMenuItemSelect($chatId, TelegramUser $user, $selectedItemId, $navSelectedMenuItem)
{
switch($navSelectedMenuItem['id']) {
case Menu::COPY_LAST_CANDIDATE_MENU_ITEM_ID:
$this->bot->onCopyLastCandidateMenuItemSelect($chatId, $user, $navSelectedMenuItem);
break;
case Menu::RESET_CANDIDATE_MENU_ITEM_ID:
$this->bot->onResetCandidateMenuItemSelect($chatId, $user, $navSelectedMenuItem, Menu::CANDIDATE_FIELD_MENU_ID);
break;
case "back":
$this->sendSelectCandidateFieldMenu($chatId, $user);
break;
default:
throw new \Exception("Unknown: " . $navSelectedMenuItem['id']);
}
}
/**
* @param $chatId
* @param TelegramUser $user
* @param array|Person[] $persons
* @param $searchText
* @return void
*/
public function sendSelectPersonMenu($chatId, TelegramUser $user, array $persons, $searchText)
{
$msg = "По Вашему запросу \"" . $searchText . "\" найден" . StringUtils::getEnding(count($persons), "", "о", "о") . " " . count($persons)
. " человек" . StringUtils::getEnding(count($persons), "", "а", "ов") . ":\n";
$personIndex = -1;
$menuItemsData = [];
foreach ($persons as $person) {
$personIndex++;
$menuItemText =
PersonService::getFirstLastNameWithOld($person) .
(count($persons) > 1
? (" " . ($person->getCity() ? $person->getCity()->getName() : "Город не указан") . "")
: "")
. (!$person->isSameName($searchText) ? " – ‼️Имя НЕ совпадает" : " – ✅ Имя совпадает")
;
$menuItemsData[$person->getId()] = [
"text" => $menuItemText,
"city" => $person->getCity() ? $person->getCity()->getName() : "Город не указан",
];
$msg .= "\n" . ($personIndex + 1) . ". " . $menuItemText;
}
$msg .= "\n\nПодтвердите выбор участника, нажав на его имя или введите имя заново:";
$msg = $this->createMainEditableMessageText(null, $msg);
$this->createAndSendMenu($chatId, $user, $msg, Menu::PERSON_MENU_ID, $menuItemsData);
}
public function createMainEditableMessageText(?string $topText = null, string $footerText = null): string
{
$msg = "";
if ($topText) {
$msg .= $topText . "\n\n";
} else {
$msg .= "Черновик анкеты нового участника мероприятия:\n\n";
}
$navStatusSelectedItem = $this->bot->getNavigationSelectedItem($this->bot->telegramUser, Menu::STATUS_MENU_ID);
$candidate = $this->bot->candidate;
$student = $candidate->getPerson() ? $candidate->getPerson()->getStudent() : null;
$ce = $candidate->getCalendarEvent();
$person = $candidate->getPerson();
$nameText = ($candidate->getPerson() ? $candidate->getPerson()->getName() : "Не указано");
$levelText = ($candidate->getPerson() && $candidate->getPerson()->getStudent()
? $candidate->getPerson()->getStudent()->getLevelText()
: "нет уровня");
$cityText = ($candidate->getPerson() && $candidate->getPerson()->getCity()
? ", " . $candidate->getPerson()->getCity()->getName()
: "");
$imageText = ($candidate->getImage() ? "Указана" : "Не указана");
$ceText = ($candidate->getCalendarEvent()
? $candidate->getCalendarEvent()->getShortInfo()
: "Не указано");
$commentText = StringUtils::addDot(($candidate->getComment() ?: "Не указан"));
$reviewApproveCommentText = ($candidate->getReviewApproveComment() ?: "Не указан");
$curatorText = ($candidate->getCurator()
? $candidate->getCurator()->getName()
: "Не указан");
$statusText = ($navStatusSelectedItem
? Status::getText($navStatusSelectedItem['id'])
: ($candidate->getStatus() == Status::STATUS_DRAFT
? Status::getText(Status::STATUS_NEW) : $candidate->getStatusText()));
$formDateText = ($candidate->getCreatedAt()
? $candidate->getCreatedAt()->format("d.m.Y")
: "Не указана");
$msg .= "🙍♂️ <b>Имя</b>: $nameText ($levelText{$cityText}).
📷 <b>Фотография</b>: $imageText.
🗓️ <b>Мероприятие</b>: $ceText.";
if ($ce && $person) {
$msg .= "
{$ce->getFromLevelEmoji()} <b>Уровень</b>: "
. ($ce->getFromLevelText() ?: "без уровня")
. ($ce->isStudentLevelCorrespond($person) ? " ✅" : " ❌") . ".";
}
if ($ce && $person && $ce->getRequireCalendarEventsParticipations()->count()) {
$msg .= "
📅 <b>Участие</b>: {$ce->getRequireCalendarEventsParticipationsText()}"
. ($this->calendarEventService->getMissingRequiredCandidateCalendarEvents($person, $ce) ? " ❌" : " ✅") . ".";
}
if ($ce && $person && $ce->getDenyCalendarEventsParticipations()->count()) {
$msg .= "
📅 <b>Исключить</b>: {$ce->getDenyCalendarEventsParticipationsText()}"
. ($this->calendarEventService->getDeniedCalendarEventsForPerson($person, $ce) ? " ❌" : " ✅") . ".";
}
$msg .= "
💬 <b>Комментарий</b>: $commentText";
if (!($student && $student->isCandidateReviewIsNotRequired()) && ($candidate->getCalendarEvent() && !$candidate->getCalendarEvent()->isRegistrationOpen())
|| ($navStatusSelectedItem
&& $navStatusSelectedItem['id'] == Status::STATUS_APPROVED
&& $candidate->getCalendarEvent()
&& Type::isApproveReviewCommentRequireType($candidate->getCalendarEvent()->getType()))) {
$msg .= "
💬 <b>Комментарий согласования о пройденном отборе</b>: $reviewApproveCommentText.";
}
$msg .= "
👤 <b>Куратор</b>: $curatorText.
📊 <b>Статус</b>: $statusText.
📅 <b>Дата анкеты</b>: $formDateText.";
if ($footerText) {
$msg .= "\n\n" . $footerText;
}
return $msg;
}
public function editOrSendMainEditableMessage(TelegramUser $user, string $chatId, string $text, array $buttons = null)
{
$replyMarkup = null;
if ($buttons) {
$replyMarkup = $this->bot->createReplyMarkupFromButtonsArray($buttons);
}
$this->bot->editOrSendEditableMessage($user, $chatId, EditableMessage::EDITABLE_MESSAGE_ID__MAIN, $text, $replyMarkup);
}
public function editOrSendPhotoEditableMessage(TelegramUser $user, string $chatId, string $imageFileId)
{
$this->bot->editOrSendEditableMessage($user, $chatId, EditableMessage::EDITABLE_MESSAGE_ID__PHOTO, null, null, $imageFileId);
}
public function deleteMainEditableMessage(TelegramUser $user, string $chatId)
{
$editableMessageId = $this->bot->getEditableMessageId($user, EditableMessage::EDITABLE_MESSAGE_ID__MAIN);
if ($editableMessageId) {
try {
$this->bot->deleteRemoteMessage($chatId, $editableMessageId);
} catch (\Throwable $exception) {
//ignore
}
}
}
public function sendSelectCandidateFieldMenu($chatId, TelegramUser $user, $msg = null,
$resetNav = true)
{
$msg = $this->createMainEditableMessageText(null,
$msg ?? "Выберите поле анкеты участника для изменения:");
if ($resetNav) {
$this->resetCandidateFieldNamedNav($user);
}
$this->createAndSendMenu($chatId, $user, $msg, Menu::CANDIDATE_FIELD_MENU_ID,
$this->getCandidateFieldNameData2());
}
public function createCandidateFieldsMenu(): array
{
return $this->createMenu(Menu::CANDIDATE_FIELD_MENU_ID,
$this->getCandidateFieldNameData2());
}
public function resetCandidateFieldNamedNav(TelegramUser $user, $defaultNavItemId = null)
{
$candidateFieldNameData = $this->createCandidateFieldsMenu();
$this->bot->setNavigationItems($user, Menu::CANDIDATE_FIELD_MENU_ID,
$candidateFieldNameData['navItems']);
if (!$defaultNavItemId) {
$defaultNavItemId = $candidateFieldNameData['navItemIds'][Menu::PERSON_MENU_ID];
}
$this->bot->setNavigationSelectedItemId($user,
Menu::CANDIDATE_FIELD_MENU_ID, $defaultNavItemId);
}
public function resetEditableMessages(TelegramUser $user)
{
$this->bot->setEditableMessageId($user, EditableMessage::EDITABLE_MESSAGE_ID__MAIN, null);
$this->bot->setEditableMessageId($user, EditableMessage::EDITABLE_MESSAGE_ID__PHOTO, null);
$this->bot->setNavigationSelectedItemId($this->bot->telegramUser, Menu::STATUS_MENU_ID, null);
}
public function getCandidateFieldNameData2(): array
{
$navStatusSelectedItem = $this->bot->getNavigationSelectedItem($this->bot->telegramUser, Menu::STATUS_MENU_ID);
$dataToEdit = [
Menu::PERSON_MENU_ID => ["text" => "🙍♂️ Имя"],
Menu::PHOTO_MENU_ID => ["text" => "📷 Фотография"],
Menu::CALENDAR_EVENT_MENU_ID => ["text" => "🗓️ Мероприятие"],
Menu::COMMENT_MENU_ID => ["text" => "💬 Комментарий"],
];
if ($this->bot->candidate && $this->bot->candidate->getCalendarEvent()
&& (!$this->bot->candidate->getCalendarEvent()->isRegistrationOpen() ||
$navStatusSelectedItem
&& $navStatusSelectedItem['id'] == Status::STATUS_APPROVED)) {
$dataToEdit[Menu::REVIEW_APPROVE_COMMENT_MENU_ID] = [
"text" => "💬 Комментарий согласования о пройденном отборе"
];
}
$dataToEdit = array_merge($dataToEdit, [
Menu::AUTHOR_MENU_ID => ["text" => "👤 Куратор"],
Menu::STATUS_MENU_ID => ["text" => "📊 Статус"],
Menu::DATE_MENU_ID => ["text" => "📅 Дата анкеты"],
"confirm" => ["text" => "✅ Отправить анкету участника"],
Menu::OTHER_MENU_ID => ["text" => "Другое"]
]);
return $dataToEdit;
}
public function sendSelectCalendarEventMenu($chatId, TelegramUser $user)
{
$msg = $this->createMainEditableMessageText(null, "Выберите мероприятие:");
/**
* @var CalendarEvent[] $calendarEvents
*/
$calendarEvents = $this->calendarEventService->getCalendarEventsForRegister();
$menuItemsData = [];
foreach ($calendarEvents as $calendarEvent) {
$menuItemsData[$calendarEvent->getId()] = ["text" => $calendarEvent->getText(['quotes' => true])];
}
$this->createAndSendMenu($chatId, $user, $msg, Menu::CALENDAR_EVENT_MENU_ID, $menuItemsData);
}
private function createLastCandidateMenuItemData($text = null): array
{
$menuItemData = [
Menu::COPY_LAST_CANDIDATE_MENU_ITEM_ID => ["text" => "📄 Копировать анкету последнего кандидата"],
];
if ($text) {
$menuItemData[Menu::COPY_LAST_CANDIDATE_MENU_ITEM_ID]['text'] = $text;
}
return $menuItemData;
}
private function createResetCandidateMenuItemData($text = null): array
{
$menuItemData = [
Menu::RESET_CANDIDATE_MENU_ITEM_ID => ["text" => "🧹 Сбросить анкету участника и начать заново"],
];
if ($text) {
$menuItemData[Menu::RESET_CANDIDATE_MENU_ITEM_ID]['text'] = $text;
}
return $menuItemData;
}
public function sendAfterCandidateSendMenu($chatId, TelegramUser $user)
{
$msg = "✅ Участник успешно зарегистрирован.\n\nДля регистрации следующего участника" .
" введите 🙍♂️ имя или отправьте 📷 фотографию."
. " Либо можете зарегистрировать на еще одно мероприятие этого кандидата.";
$msg = $this->createMainEditableMessageText("Анкета"
. " зарегистрированного участника мероприятия:", $msg);
$menuItemsData = array_merge(
$this->createLastCandidateMenuItemData("📋 Копировать анкету и изменить Имя или Меропирятие")
);
$this->createAndSendMenu($chatId, $user, $msg, Menu::AFTER_CANDIDATE_SEND_MENU_ID, $menuItemsData);
}
public function sendSelectAuthorMenu($chatId, TelegramUser $user)
{
$msg = $this->createMainEditableMessageText(null, "Выберите куратора участника:");
/**
* @var User[] $authors
*/
$authors = $this->userService->getCurators();
$menuItemsData = [];
foreach ($authors as $author) {
$menuItemsData[$author->getId()] = ["text" => $author->getName()];
}
$this->createAndSendMenu($chatId, $user, $msg, Menu::AUTHOR_MENU_ID, $menuItemsData);
}
public function sendSelectStatusMenu($chatId, TelegramUser $user)
{
$msg = $this->createMainEditableMessageText(null, "Выберите статус участника:");
$menuItemsData = [
Status::STATUS_NEW => ["text" => Status::getText(Status::STATUS_NEW)],
Status::STATUS_APPROVED => ["text" => Status::getText(Status::STATUS_APPROVED)],
];
$this->createAndSendMenu($chatId, $user, $msg, Menu::STATUS_MENU_ID, $menuItemsData);
}
/**
* @param array{string, array} $menuItemsData
* Example:
* [
* "menu_item_id_1" => ["text" => "Menu Item 1", "some_data_key" => "data", ...],
* ]
* @return array{buttons: array, navItems: array}
*/
public function createMenu($menuId, array $menuItemsData): array
{
$buttons = [];
$navItems = [];
$navItemIds = [];
foreach ($menuItemsData as $menuItemId => $menuItemData) {
$navItems[$menuId . "__" . $menuItemId] = ['id' => $menuItemId];
foreach ($menuItemData as $key => $value) {
$navItems[$menuId . "__" . $menuItemId][$key] = $value;
}
$buttons[$menuId . "__" . $menuItemId] = $menuItemData['text'];
$navItemIds[$menuItemId] = $menuId . "__" . $menuItemId;
}
return [
'buttons' => $buttons,
'navItems' => $navItems,
'navItemIds' => $navItemIds,
];
}
/**
* @param array{string, array} $menuItemsData
* Example:
* [
* "menu_item_id_1" => ["text" => "Menu Item 1", "some_data_key" => "data", ...],
* ]
*/
public function createAndSendMenu($chatId, TelegramUser $user, $msg, $menuId, array $menuItemsData)
{
$menuResult = $this->createMenu($menuId, $menuItemsData);
$buttons = $menuResult['buttons'];
$navItems = $menuResult['navItems'];
$this->bot->setNavigationItems($user, $menuId, $navItems);
$this->editOrSendMainEditableMessage($user, $chatId, $msg, $buttons);
}
public function sendSelectOtherMenu($chatId, TelegramUser $user, $msg = null)
{
$msg = $this->createMainEditableMessageText(null, $msg ?? "Выберите пункт меню:");
$menuItemsData = array_merge(
$this->createLastCandidateMenuItemData(),
$this->createResetCandidateMenuItemData(),
[
"back" => ["text" => "🔙 Назад"]
]
);
$this->createAndSendMenu($chatId, $user, $msg, Menu::OTHER_MENU_ID, $menuItemsData);
}
public function onCandidateFieldMenuItemSelect($chatId, TelegramUser $user, $selectedItemId, $navSelectedMenuItem)
{
$selectedCandidateFieldId = $navSelectedMenuItem['id'];
switch($selectedCandidateFieldId) {
case Menu::PERSON_MENU_ID:
$msg = "🙍♂️ Введите имя участника:";
$this->sendSelectCandidateFieldMenu($chatId, $user, $msg, false);
return;
case Menu::COMMENT_MENU_ID:
$msg = "💬 Введите комментарий для участника:";
$this->sendSelectCandidateFieldMenu($chatId, $user, $msg, false);
return;
case Menu::REVIEW_APPROVE_COMMENT_MENU_ID:
$msg = "💬 Введите комментарий согласования о пройденном отборе участника:";
$this->sendSelectCandidateFieldMenu($chatId, $user, $msg, false);
return;
case Menu::DATE_MENU_ID:
$msg = "📅 Введите дату анкеты участника в формате ДД.ММ.ГГГГ:";
$this->sendSelectCandidateFieldMenu($chatId, $user, $msg, false);
return;
case Menu::CALENDAR_EVENT_MENU_ID:
$this->sendSelectCalendarEventMenu($chatId, $user);
break;
case Menu::AUTHOR_MENU_ID:
$this->sendSelectAuthorMenu($chatId, $user);
break;
case Menu::STATUS_MENU_ID:
$this->sendSelectStatusMenu($chatId, $user);
break;
case Menu::PHOTO_MENU_ID:
$msg = "📷 Прикрепите и отправьте или"
. " перешлите из другого чата фотографию:";
$this->sendSelectCandidateFieldMenu($chatId, $user, $msg, false);
return;
case "confirm":
$this->bot->onCandidateConfirmMenuItemSelect($chatId, $user);
break;
case "other":
$this->sendSelectOtherMenu($chatId, $user);
break;
default:
throw new \Exception("Unknown: " . $navSelectedMenuItem['id']);
}
}
}