company_banner

Пишем простого чат-бота для Telegram на PHP

  • Tutorial
Шукюров Заур, разработчик @KinomanBot и @GaidarForum_bot, написал руководство по созданию простого чат-бота на PHP.

24 июня 2015 года разработчики Telegram открыли платформу для создания ботов (программ, которые выполняют определенные действия по заданному алгоритму).

За полтора года работы платформы набралось много интересных чат-ботов, решающих множество проблем и позволяющих с пользой провести время в мессенджере.



Шаг 1: регистрация бота у @BotFather


Прежде чем начать писать код, нового бота нужно зарегистрировать у «папы всех ботов» — @BotFather, чтобы получить токен (ключ) для работы с Telegram API.

Регистрация проходит в 5 простых этапов:

1) Открываете чат с @BotFather;
2) Вводите или выбираете из списка команду /newbot;
3) Отправляете желаемое название для бота;
4) Пишете юзернейм бота, по которому его будут находить через поиск. Обязательно на конце вашего юзернейма должно быть слово «bot» или «_bot». Например, NetologyRSSbot;
5) По желанию можно сразу настроить полное или краткое описание, список команд и аватарку.


По итогу регистрации получаем наш токен — 375466075:AAEARK0r2nXjB67JiB35JCXXhKEyT42Px8s.
Будьте осторожны: никогда и никому не показывайте токен, иначе ваш бот может быть скомпрометирован. Если по несчастливой случайности кто-то нехороший все-таки узнал ваш токен, то заменить его можно всё в том же @BotFather, нажав на кнопку «Revoke current token» в разделе «API Token».

Шаг 2: выбираем способ обработки запросов


Исходя из официальной документации, Telegram API основан на простых HTTP-запросах. Существует всего два различных способа обрабатывать запросы, которые пользователи будут посылать боту:

1) проверять «вручную», используя «Long Polling»;
2) доверить всё Telegram, поставив «Webhook». В этом случае любой запрос от пользователя Telegram сам будет посылать нам на сервер.

Мы остановимся на втором варианте, но у него есть ограничение: у вас на сайте обязательно должен быть установлен SSL-сертификат, чтобы все запросы проходили через безопасный протокол HTTPS. Самоподписанные и бесплатные сертификаты «Let’s Encrypt», которые поддерживает большинство хостингов, также подходят.

Пример настройки самоподписанного сертификата из официальной документации Telegram.

Шаг 3: пишем код


Писать код бота будем на PHP, но чтобы не изобретать заново колесо, воспользуемся уже готовой и очень удобной библиотекой.

Перво-наперво привязываем через метод SetWebhook бота к нашему файлу-обработчику. Сделать это можно при помощи библиотеки, но есть вариант быстрее и проще – это построить вот такую ссылку:
https:// api. telegram. org/bot375466075:AAEARK0r2nXjB67JiB35JCXXhKEyT42Px8s/setWebhook?url=https:// yoursitehere .ru/directory/bot.php,
где
375466075:AAEARK0r2nXjB67JiB35JCXXhKEyT42Px8s — это наш токен,
https:// yousitehere. ru/directory/bot.php — ссылка на файл-обработчик на нашем сайте.

Открыв в браузере эту ссылку, должен прийти JSON-ответ со значением «Webhook was set», что будет означать, что вебхук установлен, и теперь все запросы от пользователей будут присылаться по адресу файла-обработчика.

Переходим к самому главному — обработке этих самых запросов и написанию функционала бота.

Ниже представлен полный листинг файла-обработчика:

<?php
    include('vendor/autoload.php'); //Подключаем библиотеку
    use Telegram\Bot\Api; 

    $telegram = new Api('375466075:AAEARK0r2nXjB67JiB35JCXXhKEyT42Px8s'); //Устанавливаем токен, полученный у BotFather
    $result = $telegram -> getWebhookUpdates(); //Передаем в переменную $result полную информацию о сообщении пользователя
    
    $text = $result["message"]["text"]; //Текст сообщения
    $chat_id = $result["message"]["chat"]["id"]; //Уникальный идентификатор пользователя
    $name = $result["message"]["from"]["username"]; //Юзернейм пользователя
    $keyboard = [["Последние статьи"],["Картинка"],["Гифка"]]; //Клавиатура

    if($text){
         if ($text == "/start") {
            $reply = "Добро пожаловать в бота!";
            $reply_markup = $telegram->replyKeyboardMarkup([ 'keyboard' => $keyboard, 'resize_keyboard' => true, 'one_time_keyboard' => false ]);
            $telegram->sendMessage([ 'chat_id' => $chat_id, 'text' => $reply, 'reply_markup' => $reply_markup ]);
        }elseif ($text == "/help") {
            $reply = "Информация с помощью.";
            $telegram->sendMessage([ 'chat_id' => $chat_id, 'text' => $reply ]);
        }elseif ($text == "Картинка") {
            $url = "https://68.media.tumblr.com/6d830b4f2c455f9cb6cd4ebe5011d2b8/tumblr_oj49kevkUz1v4bb1no1_500.jpg";
            $telegram->sendPhoto([ 'chat_id' => $chat_id, 'photo' => $url, 'caption' => "Описание." ]);
        }elseif ($text == "Гифка") {
            $url = "https://68.media.tumblr.com/bd08f2aa85a6eb8b7a9f4b07c0807d71/tumblr_ofrc94sG1e1sjmm5ao1_400.gif";
            $telegram->sendDocument([ 'chat_id' => $chat_id, 'document' => $url, 'caption' => "Описание." ]);
        }elseif ($text == "Последние статьи") {
            $html=simplexml_load_file('http://netology.ru/blog/rss.xml');
            foreach ($html->channel->item as $item) {
	     $reply .= "\xE2\x9E\xA1 ".$item->title." (<a href='".$item->link."'>читать</a>)\n";
        	}
            $telegram->sendMessage([ 'chat_id' => $chat_id, 'parse_mode' => 'HTML', 'disable_web_page_preview' => true, 'text' => $reply ]);
        }else{
        	$reply = "По запросу \"<b>".$text."</b>\" ничего не найдено.";
        	$telegram->sendMessage([ 'chat_id' => $chat_id, 'parse_mode'=> 'HTML', 'text' => $reply ]);
        }
    }else{
    	$telegram->sendMessage([ 'chat_id' => $chat_id, 'text' => "Отправьте текстовое сообщение." ]);
    }
?>

Разберем всё по порядку.

1. Сначала мы подключаем скачанную библиотеку, указав путь (лучше полный) до файла автозагрузчика.

include('vendor/autoload.php'); //Подключаем библиотеку
use Telegram\Bot\Api;

2. Создаем экземпляр класса в переменной $telegram и передаем в него наш токен.

В переменной $result получаем информацию о сообщении, которое пришлет нам Telegram.

$telegram = new Api('375466075:AAEARK0r2nXjB67JiB35JCXXhKEyT42Px8s'); //Устанавливаем токен, полученный у BotFather
$result = $telegram -> getWebhookUpdates(); //Передаем в переменную $result полную информацию о сообщении пользователя

3. Затем определяем главные переменные: текстовое сообщение, уникальный идентификатор пользователя и его юзернейм. Если предстоит работа с БД, то не забывайте про фильтрацию (или лучше используйте PDO).

$text = $result["message"]["text"]; //Текст сообщения
$chat_id = $result["message"]["chat"]["id"]; //Уникальный идентификатор пользователя
$name = $result["message"]["from"]["username"]; //Юзернейм пользователя

4. Создаем нашу клавиатуру, состоящую из трех кнопок.

$keyboard = [["Последние статьи"],["Картинка"],["Гифка"]]; //Клавиатура

5. Теперь, когда мы обозначили все переменные, можно перейти к обработке полученного сообщения. Для этого можно использовать конструкцию switch-case либо if-else. Так как принципиальной разницы между ними нет, остановимся на втором варианте, как наиболее привычном.

В самом начале проверяем, заполнена ли переменная $text и является ли сообщение пользователя текстовым.

if($text){
         …
         //код
         ...
}else{
         $telegram->sendMessage([ 'chat_id' => $chat_id, 'text' => "Отправьте текстовое сообщение." ]);
}

Если нет, то отправляем пользователю с помощью метода sendMessage сообщение с просьбой ввести текстовое сообщение.

6. Рассмотрим вариант, когда пользователь прислал сообщение с командами /start или /help

if ($text == "/start") {
            $reply = "Добро пожаловать в бота!";
            $reply_markup = $telegram->replyKeyboardMarkup([ 'keyboard' => $keyboard, 'resize_keyboard' => true, 'one_time_keyboard' => false ]);
            $telegram->sendMessage([ 'chat_id' => $chat_id, 'text' => $reply, 'reply_markup' => $reply_markup ]);
 }elseif ($text == "/help") {
            $reply = "Информация с помощью.";
            $reply_markup = $telegram->replyKeyboardMarkup([ 'keyboard' => $keyboard, 'resize_keyboard' => true, 'one_time_keyboard' => false ]);
            $telegram->sendMessage([ 'chat_id' => $chat_id, 'text' => $reply, 'reply_markup' => $reply_markup ]);
  }

В этом случае помимо текста из переменной $reply будет подгружаться клавиатура, состоящая из трёх кнопок: «Последние статьи», «Картинка» и «Гифка».

Реализуется это с помощью метода replyKeyboardMarkup, параметрами которого являются:

  • 'keyboard' => $keyboard, передаем нашу клавиатуру
  • 'resize_keyboard' => true, клавиатура будет сжата в размерах.
  • 'one_time_keyboard' => false, клавиатура не исчезнет после нажатия на какую-то кнопку.

7. После появления клавиатуры пользователь явно захочет попробовать потыкать на расположенные на ней кнопки, и вот что у нас «под капотом» в этом случае:

}elseif ($text == "Картинка") {
            $url = "https://68.media.tumblr.com/6d830b4f2c455f9cb6cd4ebe5011d2b8/tumblr_oj49kevkUz1v4bb1no1_500.jpg";
            $telegram->sendPhoto([ 'chat_id' => $chat_id, 'photo' => $url, 'caption' => "Описание." ]);
        }elseif ($text == "Гифка") {
            $url = "https://68.media.tumblr.com/bd08f2aa85a6eb8b7a9f4b07c0807d71/tumblr_ofrc94sG1e1sjmm5ao1_400.gif";
            $telegram->sendDocument([ 'chat_id' => $chat_id, 'document' => $url, 'caption' => "Описание." ]);
        }elseif ($text == "Последние статьи") {
            $html=simplexml_load_file('http://netology.ru/blog/rss.xml');
            foreach ($html->channel->item as $item) {
	     $reply .= "\xE2\x9E\xA1 ".$item->title." (<a href='".$item->link."'>читать</a>)\n";
            }
            $telegram->sendMessage([ 'chat_id' => $chat_id, 'parse_mode' => 'HTML', 'disable_web_page_preview' => true, 'text' => $reply ]);
        }

8. Для отправки картинки используется метод sendPhoto, для отправки гифки – sendDocument. В обоих случаях Telegram позволяет передавать прямую ссылку на файл, что безусловно очень удобно, но не так быстро, как если бы мы передавали file_id уже отправленной на сервера Telegram картинки или гифки.

9. Для получения последних статей используется простой парсинг RSS ленты Нетологии при помощи встроенной в PHP функции simplexml_load_file.

В параметрах метода sendMessage можно заметить два новых значения:

1) 'parse_mode' => 'HTML', чтобы в сообщение можно было вставить HTML-теги
<b>, <a>, <i>, <code>
или
<pre>

2)
'disable_web_page_preview' => true
чтобы к сообщению со ссылкой не подгружалось превью.

10. В качестве смайла (стрелка вправо) используются символы \xE2\x9E\xA1. Список всех смайлов в таком виде можно найти на специальном сайте.



11. После того, как вы протестируете бота и будете уверены в его работоспособности, можно отправлять его на всеобщее обозрение.

Благодаря удобному API, боты Telegram могут стать хорошей платформой для автоматизации рутинных действий, настройки уведомлений, удобному и быстрому получению информации и созданию игр.

Бесплатными площадками для продвижения могут послужить каталоги ботов Telegram Bot Store, TeleChappy или 50bots.

А анализировать активность пользователей можно с помощью бесплатного инструмента для аналитики ботов от Яндекса — Botan.

От редакции


PHP — один из самых популярных языков программирования. Его легко изучать, с ним легко работать, у него мощное сообщество. 5 мая «Нетология» запускает курс «PHP/SQL: back-end разработка и базы данных», где ведущие программисты расскажут об управляющих конструкциях, циклах, функциях, о строках и массивах. Вы узнаете все про реляционные базы данных и язык запросов SQL, научитесь устанавливать и настраивать веб-сервер nginx и php, управлять базами данных различной сложности. Ждем вас!
Нетология
Университет современных профессий

Комментарии 15

    +3
    Серьезно? 2017 год на дворе…

    composer? — нет не слышали.
    ООП? — нет не слышали.
    psr'ы? — нет не наше.

    Вот из за таких статей, и таких код семплов потом все говорят, что php «плохой».
      –2
      А может быть каждый по-своему понимает слово «простой», которое присутствует в заголовке статьи?
        0
        Простой не означает понятный и легко воспринимающийся. Я когда смотрю на эти код сниппеты у меня голова начинает кружится, о какой простоте идет речь простите?
        Вот посмотрите пример простого и красиовго кода
        https://github.com/php-telegram-bot/core/blob/master/examples/Commands/ForcereplyCommand.php

        разницу чувствуете?
          0
          php не плохой и не хороший — это просто инструмент.
          Главное, что бы код работал.
          И человек пишет его так, как «видит», как у него проект складывается в голове.
          А вы пытаетесь загнать всех в одни рамки.

            +1
            Никто не пытается загнать кого-то в рамки, просто код в статье ужасен и в профессиональной среде его может написать только джуниор, который ещё ничего не понимает в разработке ПО, и к серьёзному проекту его не подпустят — это факт.

            Думаю, что в данном случае этот код был написан осознанно, «для демонстрации», однако продемонстрировать всё то же самое можно было и немного более приличным кодом.
              +1
              Код в статье работает. Это все, что можно о нём сказать.
              Остальное на усмотрение автора.
              И этот код никак не характеризует его навыки, помимо того, что он может создавать рабочие решения.

              Ваш комментарий в полной мере не характеризует ваши навыки, но может кое-что сказать о вас)
              И в следствии этого, к серьёзному проекту я бы сомневался пускать вас)
                0
                Этот код понятен.

                Толку, если там будет туева куча ваших непонятных дерьмоабстракций? :)
                  0
                  Конечно, никакого толку не будет.
                  ООП и SOLID это вообще свалка «непонятных дерьмоабстракций», непонятно для чего придуманная.
                  Поэтому пишите в том же духе дальше, и успехов вам :)
              0
              return Request::sendMessage($data);
              


              невелика разница
                –1
                У вас не рабочая ссылка. Код там конечно есть ( github.com/php-telegram-bot/core ), но его там столько, что даром не надо. Я почувствовал разницу.
                  0
                  Логично предположить, что за два года ссылка могла потерять актуальность
            –1
            Статья о телеграмм ботах в стиле «Hello world!». Свою задачу выполняет на 100%. А ООП, композер и стандарты из другой оперы. Кстати, автору спасибо, за статью.
              0
              Вы правы, но отчасти.
              Целью было максимально просто донести до читателей (в первую очередь Нетологии) как на уровне новичка-среднячка можно самому попробовать написать бота. Зачем таких людей сейчас напрягать тем, о чём вы написали? Если они продолжат изучать язык, они сами с этим столкнутся, и я убежден, что справятся с проблемами на отлично.

              Можно ли было использовать
              $chat_id = getMessage()->getChat()->getId();
              вместо
              $chat_id = $result["message"]["chat"]["id"];
              ? Конечно.
              Можно ли было использовать тот «красивый» кусок кода, что вы скинули? Да, можно и нужно, но на проектах побольше этого. Так же в статье не затронута тема обработки исключений и других важных моментов, о которых нужно обязательно потом позаботиться.

              Ваша претензия чем-то напоминает вот эту картинку, где цель этой статьи – научить рисовать два круга, а вы хотите, чтобы сразу получился весь рисунок. И в вашем желании нет ничего зазорного, но начать хотелось с малого. Спасибо!
              –1
              В самом телеграме есть поиск ботов? :)
              Какой спрос на боты? :)

              Тот же вопрос и касательно других месенджеров.

              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

              Самое читаемое