Хотел привести пример как отобразить в Телеграм многоуровневое меню, но получился простой в создании информационный бот с вложенностью, ограниченной только возможной длинной значения в параметре callback_data
inline-кнопки (1-64 bytes).
Еще интересный момент это - в одном боте можно создать неограниченное количество сценариев, для этого достаточно просто стартовать бота по html-ссылке со специальными параметрами. При обычном старте бота - запускается первый сценарий. Об этом чуть ниже.
Настройки
В настройках бота нужно указать токен бота, id админа бота и заполнить массив с информацией о "шагах".
steps
|__
|__
|__
|__
|__
|__
|__
|__
|__
Каждый шаг имеет простой набор параметров:
[
"name" => "", // string
"line" => 0, // int
"type" => "text|photo|video|audio|document", // string
"text" => "", // string
"media" => "" // string | null,
"steps" => [] // array
]
- name* - Название шага, будет отображено на кнопке
- line* - уровень ряда в наборе кнопок
- type*- тип сообщения (text|photo|video|audio|document)
- text - текстовое сообщение, обязательно для
type="text"
- media - ссылка или file_id медиа файла, при
type="text"
должно быть значениеnull
- steps - это массив вложенных шагов,
---
* - обязательный параметр
Для ускорения отрисовки экранов желательно в параметре media
указывать file_id файла, он для каждого бота уникальный, поэтому из моего примера медиа у вас не будут подгружаться - их надо будет заменить.
Чтобы вам получить file_id
я добавил небольшой функционал (только если указана настройка bot_admin), нужно просто отправить в бот файл: документ, картинку, видео или аудио файлы.
В ответ бот пришлет строку - она же и будет file_id
, просто скопируйте ее и подставьте в параметр media
в нужном шаге (массива $steps).
* * *
Что из интересного?
Весь контент бота упакован в массив steps он же $content
. Важным моментом является конечно же валидность массива.
При старте бота настроен выбор сценария под индексом 0 массива (можно указать любой).
/**
1 параметр это индекс элемента массива
2 параметр это индексы родительских элементов массива steps, разделены тире (-), если null то выводим из верхнего уровня вложенности
3 параметр это id чата пользователя
*/
$printUpdate(0, null, $chat_id);
Если старт по HTML-ссылке то сценарий будет выбран из параметров ссылки
tg://resolve?domain=iMakeBot&start=s_3_0-1
где,
domain - это username вашего бота
start - это значение для выбора сценария
Параметр start имеет 3 вложенных параметра разделенных знаком нижнего подчеркивания (_
), где
1 подпараметр - это action, он всегда будет s
2 подпараметр - это индекс элемента массива steps
3 подпараметр - это индексы родительских элементов массива steps, разделены тире (-
)
3 подпараметр может быть пустым, то есть можно передать только первые 2 подпараметра (s_0 или s_3)
Если разбирать приведенный в ссылке пример параметра start (s_3_0-1) то это будет означать, что на экран по ссылке выведется элемент массива steps
$content['steps'][0]['steps'][1]['steps'][3];
// в раскрытом виде
$content = [
'steps' => [
[
'name' => 'Название элемента 0',
// ...
'steps' => [
[
'name' => 'Название элемента 0-0'
// ...
],
[
'name' => 'Название элемента 0-1',
// ...
'steps' => [
[
'name' => 'Название элемента 0-1-0'
// ...
],
[
'name' => 'Название элемента 0-1-1'
// ...
],
[
'name' => 'Название элемента 0-1-2'
// ...
],
[
'name' => 'Название элемента 0-1-3'
// ... Вот этот элемент будет отработан для вывода на экран
],
]
]
]
]
]
]
Чтобы получить из массива нужный элемент, пропускаем запрос через рекурсивную (самовызывающуюся) функцию
/** Получаем контент
* @param $step_idx
* @param $parents
* @param $data
* @return array
*/
$getContent = function ($step_idx, $parents, $data) use (&$getContent) {
// определим результат по умолчанию
$result = null;
// проверим родительские элементы
if (!is_null($parents)) {
// получим первого по списку родителя
$parent = array_shift($parents);
// проверим наличие родителя в массиве
if (isset($data['steps'][$parent])) {
// проверим путь - если еще остались в списке
if (count($parents)) {
// отправим на рекурсию - подставив новые параметры
$result = $getContent($step_idx, $parents, $data['steps'][$parent]);
} else {
// определим результат
$result = $data['steps'][$parent]['steps'][$step_idx];
}
}
} else {
// определим результат
$result = $data["steps"][$step_idx];
}
// вернем результат
return $result;
};
* * *
Отрисовка экрана
/** Выводим сообщение по запросу
* @param $step_idx
* @param $parents
* @param $chat_id
* @param null $cbq_id
* @param null $message_id
*/
$printUpdate = function ($step_idx, $parents, $chat_id, $cbq_id = null, $message_id = null) use ($getContent, $query, $notice, $content) {
// переопределим вложенность
$parents = !is_null($parents) ? explode("-", $parents) : null;
// получаем шаг
$step = $getContent($step_idx, $parents, $content);
// проверим
if (!is_null($step)) {
// готовим данные
$data = [
"chat_id" => $chat_id,
];
// если это нажатие по кнопке то удалим текущее сообщение
if (!is_null($cbq_id)) {
// гасим запрос
$notice($cbq_id);
// удаляем текущее сообщение
$query("deleteMessage", array_merge($data, ["message_id" => $message_id]));
}
// дополним данные
$data["parse_mode"] = "html";
// определим кнопки если они есть
$buttons = [];
// проверим
if (count($step['steps'])) {
// определим путь
$parents_ = !is_null($parents) ? implode("-", array_merge($parents, [$step_idx])) : $step_idx;
// переберем
foreach ($step['steps'] as $key => $next) {
// добавим кнопку
$buttons[$next['line']][] = [
"text" => $next['name'],
"callback_data" => "s_" . $key . "_" . $parents_
];
}
}
// кнопка вернуться
if (!is_null($parents)) {
// получим первого
$parent = array_pop($parents);
// добавим кнопку последним рядом
$buttons[count($buttons)][] = [
"text" => "Вернуться",
"callback_data" => "s_" . $parent . "_" . implode("-", $parents)
];
}
// проверим добавление кнопок
if (count($buttons)) {
// добавим кнопки
$data["reply_markup"] = json_encode(['inline_keyboard' => array_values($buttons)]);
}
// поддерживаемые типы
if (!is_null($step['media']) && in_array($step['type'], ['photo', 'video', 'audio', 'document'])) {
// проверим описание
if (!empty($step['text'])) {
// добавим описание
$data['caption'] = $step['text'];
}
// добавим медиа
$data[$step['type']] = $step['media'];
// отправим сообщение
$query("send" . ucfirst($step['type']), $data);
} elseif ($step['type'] === "text" && !empty($step['text'])) {
// добавим текст
$data['text'] = $step['text'];
// отправим сообщение
$query("sendMessage", $data);
} else {
// выведем ошибку о не поддерживаемом методе
$query("sendMessage", array_merge($data, ["text" => "Sorry, error 405"]));
}
} else {
// проверим на нажатие кнопки
if (!is_null($cbq_id)) {
// выведем уведомление
$notice($cbq_id, "Error 404 STEP");
}
}
};
* * *
Простой роутер для бота
/**
* Простой роутер бота
*/
if (isset($data->message)) {
// получим id чата
$chat_id = $data->message->from->id;
// если это текстовое сообщение
if (isset($data->message->text)) {
// проверим что это старт бота
if ($data->message->text == "/start") {
// выводим сообщение
$printUpdate(0, null, $chat_id);
}
// если это старт по ссылке
elseif (preg_match("~\/start s_([\d]+)_?([\d-]*)~", $data->message->text, $matches)) {
// выведем сообщение по ссылке
$printUpdate($matches[1], $matches[2], $chat_id);
}
}
// другие типы сообщений
else {
// если это админ бота направляет сообщение
if ($chat_id === $bot_admin) {
// по умолчанию
$file_id = null;
// если это картинка
if (isset($data->message->photo)) {
// file_id последней картикни
$file_id = end($data->message->photo)->file_id;
}
// если это видео-файл
elseif (isset($data->message->video)) {
// file_id видео-файла
$file_id = $data->message->video->file_id;
}
// если это аудио-файл
elseif (isset($data->message->audio)) {
// file_id аудио-файла
$file_id = $data->message->audio->file_id;
}
// если это документ
elseif (isset($data->message->document)) {
// file_id документа
$file_id = $data->message->document->file_id;
}
// проверим необходимость отправки
if (!is_null($file_id)) {
// отправим file_id
$query("sendMessage", [
"chat_id" => $chat_id,
"text" => $file_id
]);
}
}
}
// если это нажатие по кнопке
} elseif (isset($data->callback_query)) {
// получим id чата
$chat_id = $data->callback_query->from->id;
// получим callBackQuery_id
$cbq_id = $data->callback_query->id;
// получим переданное значение в кнопке
$c_data = $data->callback_query->data;
// спарсим значения
$params = explode("_", $c_data);
// если это переход по шагам
if ($params[0] == "s") {
// выводим сообщение
$printUpdate(
$params[1],
($params[2] !== "")
? $params[2]
: null,
$chat_id,
$cbq_id,
$data->callback_query->message->message_id
);
}
// если это другие кнопки
else {
// заглушим просто запрос
$notice($cbq_id, "This is notice for bot");
}
}
* * *
Исходный код бота
Бот настроен под работу с Webhook
Откройте по ссылке или QR бот @iMakeBot, нажмите кнопку Старт/Start.
Следуйте инструкциям бота.
либо у меня руки кривые либо в архиве что то не хватает
указал токин поставил вебхук бот не отвечает на старт
В архиве должен быть один файл.
Посмотрите в логах сервера, были ли запросы от Телеграм. И еще посмотрите
getWebHookInfo
Хороший бот)
А как в этот код можно добавить кнопку с ссылкой помимо кнопок для перехода по меню бота
Подскажите пожалуйста 😫🙏🙏💓
Спасибо.
Достаточно расширить настройки шага, например добавить параметры:
И в отрисовке экрана исправить:
Здравствуйте! А вы можете в коде бота добавить эти исправления?
Что то пробую заменить код на ваш, но бот молчит
Спасибо большое!
Один момент ещё, а для чего нужен параметр "text" если у ссылки есть имя "name"?
Торопился )) не усмотрел и ошибся, хотел вот так:
Отлично! Теперь подскажите как в вашем коде ссылки вывести в веб меню, по типу:
По тому же принципу как и url, добавляете параметры в настройки, в отрисовке делаете проверку и выводите нужный функционал на экран бота.
Добавил, но что то по ходу накосячил. Можете подправить пожалуйста если что не так?
Конструкция точно не правильно:
Чёт снова не так:
Ничего не поменялось:
У вас бот переключен в режим inline?
Вы сделали кнопку типа поделиться, а мне нужно открыть по ссылке веб меню (webview или как оно там называется)
)) это было ваша просьба прикрутить switch_inline_query, вам необходимо web_app?
Ну я же там сразу написал что нужно вывести в веб меню (web_app)
При нажатии на switch кнопку должна всплывать web_app страница
Что за switch кнопка?
web_app можно вывести несколькими способами:
1. через inline кнопку
2. через кнопку клавиатуры
3. через кнопку меню (настройки бота в @BotFather)
4. через прикрепленную ссылку в меню
Как здесь: @asmico_attach_bot через inline кнопку
таким образом я могу вывести
а как в вашем боте не понял, поэтому и попросил помочь.
Видимо выше не так указал код
switch_inline_query и web_app - это совершенно разные типы кнопок:
switch_inline_query - это кнопка позволяет выбрать чат, в который необходимо направить inline запрос для бота с выводом в этот чат answerInlineQuery
web_app - это возможность в специальном интерфейсе Телеграм клиента выводить web-ресурс
Если у вас все таки есть необходимость работы с switch_inline_query то этот пример бота в представленном виде не поддерживает inline запросы
Если вы имеете в виду web_app - то тут проще:
Здравствуйте! Для небольшого бота годится.
А если я к примеру хочу выводить каталог продуктов с сайта, где их количество больше 100?
В одном файле прописывать запросы не очень как то.
Что посоветуете?
Здравствуйте, как пример: вы можете подтягивать из бд данные формировать их в json-файл, json-файл подключать - конвертировать в данные массив или сразу формировать массив из данных в бд - использовать этот бот.
Подскажите как вставить фото и др. файлы с сайта вместо id файлов.
Укажите url файла
http….
Спасибо, получилось.
А какие ограничения на размер файлов, если брать файлы с сервера?
При методе
query()
, который в примере, то 5 мб для картинок и 20 мб для других типов, если отправлять черезmultipart/form-data
то 10 мб и 50 мб соответственно.1. If the file is already stored somewhere on the Telegram servers, you don't need to reupload it: each file object has a file_id field, simply pass this file_id as a parameter instead of uploading. There are no limits for files sent this way.
2. Provide Telegram with an HTTP URL for the file to be sent. Telegram will download and send the file. 5 MB max size for photos and 20 MB max for other types of content.
3. Post the file using multipart/form-data in the usual way that files are uploaded via the browser. 10 MB max size for photos, 50 MB for other files.
https://core.telegram.org/bots/api#inputfile
Может ли бот так же как и фото, видео прислать геолокацию с указанной точкой на карте?
Может конечно, в API для этого есть метод sendLocalion(). Нужно также передать ему в качестве обязательных параметров широту и долготу [latitude, longitude].
Широту и долготу как передать? Через запятую, в кавычках или как?
Посмотрите я привел пример для этого бота - добавьте это условие в логику.
И в настройки шага:
Спасибо. Поизучаю
Покажите пожалуйста как добавить в данного бота список, что бы при вводе @mybot запрос открывался всплывающий список с массивом данных
1. Нужно у бота включить inline режим
2. В методе
route()
добавить инструкцию на проверкуinline_query
3. Создать массив даных для ответа
4. Настроить логику для вывода ответов
Не думал, что бот будет настолько популярен)) прикольно. Уже с учетом правок через бот обратной связи и через комментарии в статье - превращаем бота в более универсального)
Всё это хорошо конечно, но на примере действующего кода ориентироваться более понятно, что бы в дальнейшем и в комментах лишний раз не мусорить.
Сможете помочь работающим примером?
Лучше запланировать тогда вторую статью с учетом всех пожеланий )
Ну или так. Давайте тогда вторую статью, но только что бы вся логика этого бота сохранилась. Когда сможете?
В план поставил, но по срокам и формату не смогу сориентировать.
Есть мысли дополнить формат подачи информации на сайте. Над этим сейчас работаю.
Вы пробовали отправлять скрытый текст?
Посмотрел по коду, там координаты задаются то есть: Номер символа считая от самого начала и количество символов в слове. Всё это оборачивается в единый блок. Сможете пример написать?
Попробуйте использовать html разметку
Вот блин, оказывается ещё проще! Спасибо!
Приветствую! Когда сделаете продолжение функционала данного бота с вышеописанными хотелками?
Странная подача информации. Публикации в вашем телеграм канале появляются через месяц после публикации на сайте. Почему не в одно и тоже время? По одной статье в месяц?
Добрый. Сейчас занят - пишу игру.
Когда было время написал сразу много статей. Выложил чтобы индексировались. В канал публикую с переодичностью, чтобы не спамить людей уведомлениями.
Подскажите пожалуйста как вывести Имя пользователя, да и вообще информацию о нём
Хороший пример есть в статье Простой бот для приветствия пользователя в группе Телеграм - HelloBot
Код по вашей ссылке весь просто добавить в текущего бота или что то править надо? Потому что есть одинаковые строки с некоторыми изменениями.
Нужно посмотреть - понять как и что делается, и применить в том месте где вам необходимо.
Вы заранее определяете какие алиасы бы будете использовать для подмены значений. Создаете например массив, где ключ это алиас - значение это необходимый вам текст для замены.
В настройках шага вы в тексте подставляете нужный вам алиас.
В отрисовке делаете подмену, так как вы не знаете что и когда (в отрисовке) нужно будет подставлять - прогоняйте весь массив алиасов. Обнаруженный заменится.
Примеры (не тестировал)
Здравствуйте! Уже 2023 год. Вы Обещали продолжение развития данной темы.
Доброе утро. В плане обещанное, по срокам не подскажу.
Здравствуйте, прежде всего - Спасибо Вам.
Не могли бы Вы подсказать, как можно в этот скрипт интегрировать один из Ваших предыдущих, а именно: Делаем бот для обратной связи в Telegram (imakebots.ru)
Типа обратная связь из бота в этой статье, по кнопке или команде - без разницы.
Спасибо!
Здравствуйте я не понял как запустить бота вроде ссылку поменял как надо ну наверное, помогите пожалуйста с запуском.
Здравствуйте! Год прошёл, но тема так и не получила развития...
Может уже допишете бота что бы он стал более функциональным?
Здравствуйте! Как у вас успехи? Сделали форк бота? Или ждете готовых решений? ))
Есть планы на продолжение, но это надо чтобы было желание им заниматься, пока желания нет.
Бота сделал, но с интеграцией в CMS возникли сложности. Почему то не хочет запрос выводиться в блоке PHP кода, а мне именно запросы там и нужны были для создания массива. Пока разбираюсь.
Подскажите пожалуйста где ошибка? Почему то на этом месте бот замолчал, надо что бы грузился файл:
Здравствуйте! Посмотрите в чём дело? Почему то по ссылке не выдаёт файл. Файл весит около 1мб где то. Если скормить файл боту и вставить его id то работает.
Смотрели какой возвращается ответ при запросе?
никакого ответа
Если ответа никакого нет, тогда запрос до Телеграм не доходит - зависает. Телеграм всегда отвечает на запрос, даже если он долгий.
Запишите то что вернет запрос
$query()
в файл