1. Code
  2. PHP

Пишем бота Telegram на PHP

Scroll to top

Russian (Pусский) translation by Sergey Zhuk (you can also view the original English article)

Если вы читаете это, то скорее всего понимаете, что боты для чатов является популярным трендом в 2016 году.

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

И наиболее популярным мессенджером с открытым API для ботов является Telegram.

Что мы собираемся делать

В этой статье мы создадим простой бот-секундомер для Telegram. Я покажу вам как создавать собственного бота, связываться с аналитикой, напишем немножко кода и в конце добавим своего бота в магазин ботов.

Кстати я заранее приготовил демо, так что вы можете протестировать бота, просто добавив @stopwatchbot в свой контакт лист в Telegram.

Создаем бота с помощью BotFather

Первым шагом при создании бота нужно зарегистрировать аккаунт для своего бота в Telegram. И для этого есть собственный бот, который называется BotFather. Просто добавьте его в свой контакт лист и вы сможете создавать и настраивать ботов Telegram, просто напечатав команду /newbot и следуя инструкциям от BotFather.

BotFather running TelegramBotFather running TelegramBotFather running Telegram

После регистрации вашего нового бота, вы получите сообщение с поздравлением и токен для авторизации. Мы скоро будем использовать этот токен для авторизации бота и отправки запросов к Bot API.

Позднее вы сможете использовать BotFather для добавления описаний и фото к профилям ваших ботов, регенерации токенов, задания списка доступных боту команд, удаления аккаунтов и прочее. Чтобы получить полный список команд, просто напечатайте /help в чате с BotFather.

Соединяемся с Botan Analytics

Не существует никакой встроенной аналитики в Telegrem Bots API, но все равно важно знать, сколько пользователей у вас есть, как они себя ведут и какие команды используют чаще других. Конечно можно собрать такую информацию, используя собственный движок, но если мы собираемся сфокусироваться на функционале самого бота, а не метриках, то нужно использовать решение из коробки.

И для этого уже есть простой инструмент, который называется Botan. Он основан на Yandex AppMetric и абсолютно бесплатный. Используя Botan, можно сегментировать вашу аудиторию, получать информацию о профилях пользователей, получить наиболее часто используемые команды, а так же получить красивые графики прямо в вашем мессенджере:

Bot Analytics GraphBot Analytics GraphBot Analytics Graph

Для того чтобы начать, нужно зарегистрировать ваше бота в Botan и получить свой токен. И опять же это можно сделать в боте, используя BotanioBot:

Registering a bot in TelegramRegistering a bot in TelegramRegistering a bot in Telegram

Просто нажмите на "Add bot" на вашей клавиатуре в диалоговом окне, введите ник вашего бота, и получите ваш токен для трекинга. Теперь Botanio готов регистрировать события вашего бота, а вы можете получать статистику по пользователям, сессиям и событиям прямо в вашем мессенджере.

Создание и регистрация SSL Webhook

В Telegram есть два способа получения сообщений от пользователей: длинный опрос и webhooks.

A diagram of the Telegram APIA diagram of the Telegram APIA diagram of the Telegram API

Обычно с долгим опросом, вам необходимо запрашивать новые сообщения из API, а с webhooks вы устанавливаете свой обратный вызов, который будет вызван API Telegram, если появится новое сообщение от пользователя. Я предпочитаю использовать webhooks потому что это больше похоже на взаимодействие в реальном времени, так что в этой статье мы так же будем использовать этот способ. Теперь нам нужно выбрать URL для обратного вызова нашего webhook, который будет вызван по HTTPS протоколу, и нужно установить его достаточно безопасным, и расположить свой скрипт в безопасной директории как сказано в руководстве:

Если вы хотите убедится что запрос Webhook действительно исходит от Telegream, мы рекомендуем использовать секретный путь в URL, например:https://www.example.com/<token>. Так как никто больше не знает токена для вашего бота, то вы можете быть уверены, что запрос идет от нас.

Если у вас подтвержденный SSL сертификат, то все что вам нужно, это открыть ваш URL в браузере:

1
https://api.telegram.org:443/bot[token]/setwebhook?url=[webhook]

Иначе необходимо сгенерировать самоподписанный сертификат. Вот пример команды в Linux для этого:

1
openssl req -newkey rsa:2048 -sha256 -nodes -keyout /path/to/certificate.key -x509 -days 365  -out /path/to/certificate.crt -subj "/C=IT/ST=state/L=location/O=description/CN=yourdomain.com"

И не забудьте открыть SSL порт:

1
sudo ufw allow 443/tcp

Чтобы подтвердить ваш сертификат и сделать ваш домен для webhook доверительным, необходимо загрузить свой публичный ключ:

1
curl \

2
  -F "url=https://yourdomain.com/path/to/script.php" \

3
  -F "certificate=/path/to/certificate.key" \

4
  "https://api.telegram.org/bot[token]/setwebhook"

В итоге вы получите следующий JSON ответ:

1
{"ok":true,"result":true,"description":"Webhook was set"}

В нем сказано что webhook был установлен и мы готовы начать работу с ботом.

Создаем базу данных

Теперь нам нужно создать базу данных для наших таймеров. Что мы собираемся в ней хранить? Когда пользователь дает команду секундомеру начать отсчет, мы будем брать ID чата и сохранять строку с ID чата и текущим временем Unix. Следовательно мы сохраним строку с отметкой времени и ID чата.

Чтобы показывать текущее время секундомера, мы будем брать сохраненную метку времени и сравнивать ее с текущим временем. Разница и будет текущее время в секундах. Если пользователь останавливает секундомер, то мы просто удаляем строчку с данным ID чата.

Итак давайте создадим базу данных и таблицу для хранения информации для секундомера:

1
CREATE TABLE IF NOT EXISTS `stopwatch` (
2
  `chat_id` int(10) unsigned NOT NULL,
3
  `timestamp` int(10) unsigned NOT NULL,
4
  PRIMARY KEY (`chat_id`)
5
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Создание класса Stopwatch

Наконец мы готовы начать писать код. Создадим класс для работы с базой данных в файле stopwatch.php и начнем с конструктора, который устанавливает два приватных свойства, где мы будем хранить ID чата и соединение с MySQL:

1
class Stopwatch
2
{
3
    /** @var mysqli */
4
    private $mysqli;
5
    /** @var int */
6
    private $stopwatch_id;
7
    /**

8
     * Stopwatch constructor

9
     * @param mysqli $mysqli

10
     * @param $stopwatch_id

11
     */
12
    public function __construct(\mysqli $mysqli, $stopwatch_id)
13
    {
14
        $this->mysqli = $mysqli;
15
        $this->stopwatch_id = intval($stopwatch_id);
16
    }
17
}

Когда пользователь запускает таймер, мы берем текущую временную метку Unix и сохраняем ее вместе с ID чата в методе start():

1
public function start()
2
{
3
    $timestamp = time();
4
    $query = "

5
        INSERT INTO  `stopwatch` (`chat_id`, `timestamp`)

6
        VALUES ('$this->stopwatch_id', '$timestamp')

7
        ON DUPLICATE KEY UPDATE timestamp = '$timestamp'        

8
    ";
9
    return $this->mysqli->query($query);
10
}

Если таймер останавливается, то нам нужно удалить строку из базы данных:

1
/**

2
 * Delete row with stopwatch id

3
 * @return bool|mysqli_result

4
 */
5
public function stop()
6
{
7
$query = "

8
    DELETE FROM `stopwatch`

9
    WHERE `chat_id` = $this->stopwatch_id

10
    ";
11
    return $this->mysqli->query($query);
12
}

И вот главная часть нашего класса. Когда пользователь запрашивает статус нашего таймера, нам нужно найти строку с секундомером из текущей беседы и подсчитать разницу в секундах между сохраненной временной меткой и текущим временем. К счастью временная метка Unix является целым числом, так что мы просто можем вычесть одно из другого. Чтобы отформатировать результирующее значение в виде времени, мы используем функцию gmdate.

1
/**

2
 * Find row with stopwatch id and return difference in seconds from saved Unix time and current time

3
 * @return string

4
 */
5
public function status()
6
{
7
    $query = "

8
        SELECT `timestamp` FROM  `stopwatch`

9
        WHERE `chat_id` = $this->stopwatch_id        

10
    ";
11
    $timestamp = $this->mysqli->query($query)->fetch_row();
12
    if (!empty($timestamp)) {
13
        return gmdate("H:i:s", time() - reset($timestamp));
14
    }
15
}

Как видите, если в базе данных нет значения, то метод status() ничего не вернет, и мы обработаем значение null как остановленный таймер.

Выбор библиотеки PHP

Есть много PHP библиотек для работы с Telegram API, но на момент написания этой статьи лишь одна поддерживала одновременно Telegram Bot API и трекинг Botan. Она называется PHP Telegram Bot API.

Для установки библиотеки используем Composer:

1
composer require telegram-bot/api

Если вам не нужна аналитика, то попробуйте Bot API PHP SDK с интеграцией в Laravel или PHP Telegram Bot.

Запуск Webhook скрипта

И вот мы переходим к главной части - мы создаем скрипт для обработки обратных вызовов от Telegram Bot API. Создадим файл index.php и включим в него автозагрузчик Composer и новый класс Stopwatch. Откроем соединение MySQL, создадим нового клиента Telegram API и запустим его:

1
require_once 'vendor/autoload.php';
2
require_once 'stopwatch.php';
3
4
// connect to database

5
$mysqli = new mysqli('database_host', 'database_user', 'database_password', 'database_name');
6
if (!empty($mysqli->connect_errno)) {
7
    throw new \Exception($mysqli->connect_error, $mysqli->connect_errno);
8
}
9
10
// create a bot

11
$bot = new \TelegramBot\Api\Client('bot_token', 'botanio_token');
12
// run, bot, run!

13
$bot->run();

Создание команд

Теперь нужно настроить ответ бота на команду /start. Эта команда используется для старта всех ботов Telegram, и пользователям будет показано наше сообщение с приветствием.

1
$bot->command('start', function ($message) use ($bot) {
2
    $answer = 'Howdy! Welcome to the stopwatch. Use bot commands or keyboard to control your time.';
3
    $bot->sendMessage($message->getChat()->getId(), $answer);
4
});

Здесь в методе command() мы определим замыкание для получения команды. Замыкание получает ID текущего чата и отправляет сообщение с приветствием. Так же все зарегистрированные команды автоматически по имени команды.

Для запуска секундомера, мы определим команду /go:

1
$bot->command('go', function ($message) use ($bot, $mysqli) {
2
    $stopwatch = new Stopwatch($mysqli, $message->getChat()->getId());
3
    $stopwatch->start();
4
    $bot->sendMessage($message->getChat()->getId(), 'Stopwatch started. Go!');
5
});

Она создаст объект класса Stopwatch и запустит таймер, вызывав метод start(), который мы определили ранее.

Чтобы определить команду /status, делаем аналогично. Просто вызываем метод status() и возврашаем результат. Если метод вернул null, сообщаем пользователю, что таймер не был запущен.

1
$bot->command('status', function ($message) use ($bot, $mysqli) {
2
    $stopwatch = new Stopwatch($mysqli, $message->getChat()->getId());
3
    $answer = $stopwatch->status();
4
    if (empty($answer)) {
5
        $answer = 'Timer is not started.';
6
    }
7
    $bot->sendMessage($message->getChat()->getId(), $answer);
8
});

А если пользователь останавливает таймер, то нам нужно сначала получить статус, показать результирующее время, а затем остановить таймер, используя метод stop().

1
$bot->command('stop', function ($message) use ($bot, $mysqli) {
2
    $stopwatch = new Stopwatch($mysqli, $message->getChat()->getId());
3
    $answer = $stopwatch->status();
4
    if (!empty($answer)) {
5
        $answer = 'Your time is ' . $answer . PHP_EOL;
6
    }
7
    $stopwatch->stop();
8
    $bot->sendMessage($message->getChat()->getId(), $answer . 'Stopwatch stopped. Enjoy your time!');
9
});

И это все! Теперь вы можете загрузить все необходимые файлы в вашу директорию для webhook и протестировать своего бота.

Добавление клавиатуры

Чтобы предложить пользователю, какие команды он может выполнять, мы можем к сообщению добавить клавиатуру. Наш таймер может быть в двух состояниях: запущен или остановлен. Для того чтобы показать пользователю клавиатуру, нам нужно просто переопределить метод sendMessage():

1
$keyboard = new \TelegramBot\Api\Types\ReplyKeyboardMarkup([['/go', '/status']], null, true);
2
3
$bot->sendMessage($message->getChat()->getId(), $answer, false, null, null, $keyboards);
4
});

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

Добавление бота в Store

Хорошо, теперь у нас есть работающий бот, и мы хотим показать его миру. Лучшим способом будет зарегистрировать бота в каталоге ботов. Сейчас у Telegram нет официального каталога, но есть несколько неофициальных, и самый большой из них - Storebot.me, в котором уже зарегистрированы тысячи ботов.

И в нем есть бот... для регистрации бота в каталоге ботов! Добавляем @storebot в свой контакт лист, пишем команду /add и следуем инструкциям. Вас попросят ввести имя бота, описание, выбрать одну из стандартных категорий, и подтвердить права на бота отправкой токена.

Через некоторое время ваш бот пройдет процесс подтверждения я появится в чартах Storebot. Теперь вы и ваши пользователи могу голосовать, находить и оценивать вашего бота в store, чтобы помочь ему подняться в каталоге.

Заключение

Мы прошли длинный путь, от создания простого бота до регистрации его в store, сделав его доступным для реальных пользователей. Как вы могли убедиться, существует много инструментов, которые сделают вашу жизнь проще при создании собственного бота, и не нужно писать много кода для создания простого бота. Теперь вы готовы к созданию собственного бота!

Если у вас есть какие-либо вопросы, не стесняйтесь задавать их в комментариях под статьей.

Для дальнейшего чтения