Как привязать чат-бота в Telegram к текущему пользователю в Laravel

27 июня 2019

В большинстве инструкций по работе с телеграм-ботом не сказано про то, как получить тот самый 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 будет отрицательный.

Комментарии 9
  1. Александр 3 ноября 2019

    Здравствуйте, Кирилл.
    Спасибо, Вы мне очень помогли.

  2. Кирилл Арутюнов 3 ноября 2019

    Рад, что материал оказался полезным 🙂

  3. Александр 4 ноября 2019

    Кирилл, подскажите пожалуйста, в какой части кода, необходимо прописывать ответ серверу Telegram?
    Мне нужно после каждого обновления возвращать header («HTTP/1.1 200 OK»);
    Сейчас на мой сайт, с телеграмма ежесекундно приходят десятки запросов типа:
    «POST /токен-моего-бота/webhook HTTP/1.1» 500
    Спасибо.

  4. Кирилл Арутюнов 4 ноября 2019

    Лучше в адресе вебхука не указывать токен бота, это не безопасно. Вы можете указать вебхук так: 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, иначе телеграм будет засыпать сервер запросами.

  5. Александр 4 ноября 2019

    Кирилл, спасибо Вам за ответ.
    У меня Laravel 5.8
    Использую компонент irazasyed/telegram-bot-sdk
    Бот рабочий, на мои сообщения — отвечает.
    Проблема как раз в ответе 200 ОК, телеграм сервер уже засыпает мой сайт запросами.
    Прочитал, что нужно вернуть ему header («HTTP/1.1 200 OK»);
    Но где это прописывать? После sendMessage?
    Не могли бы Вы привести часть своего кода, где вы возвращаете серверу код 200.
    Спасибо.

  6. Кирилл Арутюнов 4 ноября 2019

    У меня это примерно так происходит:
    $this→sendMessage ($chatId, $text, $replyMarkup);
    return 'OK';

    Можно и так:
    return response ('OK', 200)→header ('Content-Type', 'text/plain');
    Сначала отправляете сообщение, потом посылаете response с нужными заголовками. SendMessage, по сути, это же curl-запрос к телеграму.

    Единственное, могут быть тонкости при использовании Async-настроек в этом пакете — тут ничего не смогу подсказать, потому что не работал с этим пакетом.

  7. Александр 4 ноября 2019

    Кирилл, благодарю Вас!
    Последний вопрос, если позволите.
    Если отключить webHook, и потом заново его включить setWebHook, от бота приходят старые сообщения.
    Читал, что в случае с getUpdates, нужно отправить update_id последнего обновления api.telegram.org/bot/getUpdates?offset=
    и тогда, бот не будет присылать старые сообщения.
    А как это реализовать с webHook?
    Спасибо.

  8. Кирилл Арутюнов 4 ноября 2019

    Не сталкивался с такой задачей. Я бы в крайнем случае дописал какой-нибудь обработчик, в котором по timestamp’у отсёк бы старые сообщения и в ответ на такие запросы просто возвращал бы 200 ОК и ничего больше не делал бы. Ещё для исключения ддоса со стороны серверов телеграма, если что-то идёт не так, мы писали middleware, который кидал в кеш md5 всех полей запроса и каждый входящий запрос проверял, и если такой md5 уже был в кеше, то вебхук ничего не делал и отдавал 200 ОК. Костыль, но позволял фильтровать запросы от Телеграма, чтобы сервак не перегружался :-).

  9. Александр 4 ноября 2019

    Спасибо, Кирилл. Костыли у меня как раз хорошо получаются в последнее время:)

Добавить комментарий