Делаю чат-бота для телеграм. На данный момент есть многомерное меню, которое работает на callback-ах инлайнкнопок. Все бы было хорошо, но есть проблема: к примеру, человек нажимает заказать, затем выбирает товар. Затем ему нужно ввести адрес и время доставки, а также телефон и тд.
Скажите, пожалуйста, каким образом это можно сделать? Т.е. если человек просто введет адрес доставки, то ему будет выдаваться "не выбран товар" или "ошибка", а если он выберет товар, то он пойдет по этому сценарию и так далее.
Вопрос - как сделать сценарий?
Мой код (на примере php, библиотека telegram-bot-sdk):
$telegram = new Api('api token here'); //Устанавливаем токен, полученный у BotFather
$result = $telegram -> getWebhookUpdates(); //Передаем в переменную $result полную информацию о сообщении пользователя
$keyboard = Keyboard::make()
->inline()
->row(
Keyboard::inlineButton(['text' => 'Показать меню', 'callback_data' => '/show_menu']),
Keyboard::inlineButton(['text' => 'Купить цветы', 'callback_data' => '/buyflowers'])
);
$keyboard_flowers = Keyboard::make()
->inline()
->row(
Keyboard::inlineButton(['text' => 'Розы', 'callback_data' => '/buyflowers_roses'])
)
->row(
Keyboard::inlineButton(['text' => 'Пионы', 'callback_data' => '/buyflowers_piony'])
)
->row(
Keyboard::inlineButton(['text' => 'Шляпные коробки', 'callback_data' => '/buyflowers_hatbox'])
)
->row(
Keyboard::inlineButton(['text' => 'Коробочки со сладостями', 'callback_data' => '/buyflowers_sweetbox'])
)
->row(
Keyboard::inlineButton(['text' => 'Акции и спецпредложения', 'callback_data' => '/buyflowers_sales'])
);
$keyboard_menu = Keyboard::make()
->inline()
->row(
Keyboard::inlineButton(['text' => 'Связаться с нами', 'callback_data' => '/contact_us'])
)
->row(
Keyboard::inlineButton(['text' => 'Купить цветы', 'callback_data' => '/buyflowers'])
)
->row(
Keyboard::inlineButton(['text' => 'Стать партнером', 'callback_data' => '/partner'])
)
->row(
Keyboard::inlineButton(['text' => 'Перейти на сайт', 'url' => 'https://kands.spb.ru/'])
)
->row(
Keyboard::inlineButton(['text' => 'Акции и спецпредложения', 'callback_data' => '/buyflowers_sales'])
);
$contact = $result['message']['contact'];
$text = $result["message"]["text"]; //Текст сообщения
$chat_id = $result["message"]["chat"]["id"];
$name = $result["message"]["from"]["username"]; //Юзернейм пользователя
if ($result->isType('callback_query')) {
$data = $result['callback_query']['data'];
$chat = $result['callback_query']['message']['chat']['id'];
switch ($data) {
case '/buyflowers':
$telegram->sendMessage([
'chat_id' => $chat,
'text' => 'Выберите категорию:',
'reply_markup' => $keyboard_flowers
]);
break;
case '/show_menu':
$telegram->sendMessage([
'chat_id' => $chat,
'text' => 'Меню: ',
'reply_markup' => $keyboard_menu
]);
break;
default:
# code...
break;
}
}
$telegram->sendMessage([
'chat_id' => $chat_id,
'text' => 'Стартовая страница. Выберите дальнейшие действия.',
'reply_markup' => $keyboard
]);
?>
`
Ответ
Есть как минимум три варианта (варианты можно совмещать):
Используйте коллекцию типа ключ-значение, где в качестве ключа используется chat_id, а в качестве значения - произвольный идентификатор предыдущего/следующего шага сценария (концептуально, это могут вообще абсолютно любые необходимые данные). Получив очередное сообщение от пользователя, сверяйте статус сценария со значением элемента коллекции для данного чата
Используйте
ForseReply при
отправке сообщений с запросами адреса и пр., а затем проверяйте
входящие сообщения на наличие непустого поля reply_to_message
и смотрите в текст этого поля. В зависимости от его содержимого
можно будет понять, какой именно шаг сценария выполняется
Используйте встроенные кнопки (Inline buttons). Inline button отличается от обычной кнопки тем, что не отправляет текст в чат. Вместо этого отправляется callback, заданный программно и невидимый пользователю. Соответственно, в качестве callback_data можно использовать любой текст (типа step1 или this_is_unexpected_step), ожидая затем нужный коллбэк при получении обновлений
Пример для варианта с ForseReply (псевдокод):
// Производится запрос адреса пользователя
Bot.SendChatAction(update.Message.Chat.Id, ChatAction.Typing);
Bot.SendTextMessage(update.Message.Chat.Id, "Укажите адрес доставки", replyMarkup: force_reply);
...
// Проверка полученных сообщений от пользователя
if (update.Message.ReplyToMessage.Text.Contains("адрес доставки"))
{
if (!IsValidAddress(update.Message.ReplyToMessage.Text)) return;
SaveAddressToDb(update.Message.Chat.Id, update.Message.ReplyToMessage.Text);
Bot.SendChatAction(update.Message.Chat.Id, ChatAction.Typing);
Bot.SendTextMessage(update.Message.Chat.Id, "Адрес успешно сохранён, ожидайте поступления заказа");
}
else
{
...
}
Похожий вопрос на en SO