Как привязать чат-бота в Telegram к текущему пользователю в Laravel
В большинстве инструкций по работе с телеграм-ботом не сказано про то, как получить тот самый RECIPIENT_CHAT_ID
, в который надо слать сообщения из PHP.
Вот типичный пример отправки сообщения из Laravel-приложения в Телеграм:
Route::post('bot/sendmessage', function() {
Telegram::sendMessage([
'chat_id' => 'RECIPIENT_CHAT_ID',
'text' => 'Привет, мир!'
]);
return;
});
Предположим, что наша задача — слать уведомления пользователю в телеграм, если на сайте ему ответили на комментарий. Такое уведомление нужно отправлять только конкретному пользователю, а не всем сразу.
Простой способ для привязки пользователя на сайте и чата с ботом в телеграме — использование ссылки-инвайта для добавления чат-бота. Вы даёте пользователю специальную ссылку с параметром, и когда он на неё кликает — открывается чат с ботом, бот получает инструкцию «начать работу» и отправляет информацию на сервер вместе с тем самым параметром из ссылки, а также CHAT_ID
. По этому параметру вы находите в своей базе пользователя и назначаете ему CHAT_ID
.
Показываем пользователю на сайте ссылку:
https://telegram.me/arutyunov_bot?start=asd123
Когда пользователь перейдёт по ссылке, у него откроется чат 1-на-1 с ботом. Как только пользователь нажмёт кнопку Start, бот получит сообщение вида:
/start PAYLOAD
Вместо PAYLOAD
и будет тот самый токен. В нашем примере это asd123
. Теперь вам нужно обработать это сообщение на своём сервере.
Вот пример для обработчика, который привязан к Webhook:
if ($message->getText()) {
if (strpos('/start', $message->getText()) !== false) {
$textStrings = explode(' ', $message->getText());
if (isset($textStrings[1])) {
$token = $textStrings[1];
$chatId = $message->getChat()->getId();
if ($token) {
$user = User::find(['telegram_token' => $token]);
if ($user !== null) {
$user->telegram_chat_id = $chatId;
return response()->json(['status' => $user->save()]);
}
}
}
}
}
В официальной документации Телеграм есть описание этого механизма, который называется Deep linking
.
В телеграме каждый чат имеет свой уникальный CHAT_ID
. Обратите внимание, что чат с ботом будет иметь числовой CHAT_ID
больше 0, а если вы добавили бота в группу, то CHAT_ID
будет отрицательный.
Здравствуйте, Кирилл.
Спасибо, Вы мне очень помогли.
Рад, что материал оказался полезным 🙂
Кирилл, подскажите пожалуйста, в какой части кода, необходимо прописывать ответ серверу Telegram?
Мне нужно после каждого обновления возвращать header («HTTP/1.1 200 OK»);
Сейчас на мой сайт, с телеграмма ежесекундно приходят десятки запросов типа:
«POST /токен-моего-бота/webhook HTTP/1.1» 500
Спасибо.
Лучше в адресе вебхука не указывать токен бота, это не безопасно. Вы можете указать вебхук так:
https://api.telegram.org/bot{my_bot_token}/setWebhook?url=https://mysite.ru/bot/telegram_webhook
. В таком случае телеграм будет слать вам POST-запросы на адресhttps://mysite.ru/bot/telegram_webhook
. Собственно если у вас Laravel, то настраиваете роуты, контроллер и обрабатываете ответ от телеграма. Если не Laravel, то обрабатываете по-своему. Но суть везде одна и та же — у вас входящий ПОСТ-запрос, который надо разобрать и в ответ что-то отдать. В случае успеха не забыть код 200, иначе телеграм будет засыпать сервер запросами.Кирилл, спасибо Вам за ответ.
У меня Laravel 5.8
Использую компонент irazasyed/telegram-bot-sdk
Бот рабочий, на мои сообщения — отвечает.
Проблема как раз в ответе 200 ОК, телеграм сервер уже засыпает мой сайт запросами.
Прочитал, что нужно вернуть ему header («HTTP/1.1 200 OK»);
Но где это прописывать? После sendMessage?
Не могли бы Вы привести часть своего кода, где вы возвращаете серверу код 200.
Спасибо.
У меня это примерно так происходит:
$this→sendMessage ($chatId, $text, $replyMarkup);
return 'OK';
Можно и так:
return response ('OK', 200)→header ('Content-Type', 'text/plain');
Сначала отправляете сообщение, потом посылаете response с нужными заголовками. SendMessage, по сути, это же curl-запрос к телеграму.
Единственное, могут быть тонкости при использовании Async-настроек в этом пакете — тут ничего не смогу подсказать, потому что не работал с этим пакетом.
Кирилл, благодарю Вас!
Последний вопрос, если позволите.
Если отключить webHook, и потом заново его включить setWebHook, от бота приходят старые сообщения.
Читал, что в случае с getUpdates, нужно отправить update_id последнего обновления api.telegram.org/bot/getUpdates?offset=
и тогда, бот не будет присылать старые сообщения.
А как это реализовать с webHook?
Спасибо.
Не сталкивался с такой задачей. Я бы в крайнем случае дописал какой-нибудь обработчик, в котором по timestamp’у отсёк бы старые сообщения и в ответ на такие запросы просто возвращал бы 200 ОК и ничего больше не делал бы. Ещё для исключения ддоса со стороны серверов телеграма, если что-то идёт не так, мы писали middleware, который кидал в кеш md5 всех полей запроса и каждый входящий запрос проверял, и если такой md5 уже был в кеше, то вебхук ничего не делал и отдавал 200 ОК. Костыль, но позволял фильтровать запросы от Телеграма, чтобы сервак не перегружался :-).
Спасибо, Кирилл. Костыли у меня как раз хорошо получаются в последнее время:)
Добрый день, а что это за переменная, где она обяляется? $message
Я использовал пакет
irazasyed/telegram-bot-sdk
для Laravel, с помощью которого можно было сделать так:$telegram = new Api (config ('services.telegram-bot-api.token'));
$updates = $telegram→getWebhookUpdates ();
$message = $updates→getMessage ();
Отличная статья, спасибо!
Кирилл добрый день. Подскажите пожалуйста, для тестирования данного сервиса локально — нужно ли использовать https:// протокол? Или же можно протестировать это всё локально через OpenServer?
Алексей, https использовать обязательно, без него телеграм не позволяет прописать вебхук. Более того, если вы локально поднимаете сервер, то вам нужно настроить так, чтобы он был доступен снаружи. Сервера телеграма будут слать запросы по адресу вебхука. По-моему, на Опенсервере есть такая возможность, но нужно покопаться в настройках.