Сьогодні кожен має Telegram. Усі ми користуємося цим месенджером щодня - він зручний, простий у використанні, інтуїтивно зрозумілий, безпечний і, звичайно, ми всі любимо стікери. Крім особистих повідомлень, ми часто використовуємо групові чати - з родиною, друзями та колегами. Крім звичайних користувачів, є також бот API Telegram. Вони створені для автоматизації відповідей, тобто API-бот Telegram відповідає на конкретні повідомлення - команди та виконує певні дії.
Дія може бути як простим привітанням у відповідь, так і ланцюжком певних питань і відповідей для виконання певних логічних операцій.
Особисто я, постійно користуюся чат-ботом @vkmusic_bot_news. Це простий бот для пошуку та прослуховування музики. Користувач надсилає повідомлення з назвою композиції або автора, а API-бот Telegram надає варіанти, які відповідають запиту.
У своєму прикладі я запросив пісню «show must go on» і отримав кілька варіантів на вибір. Вибравши перший варіант, я отримав трек у наступному повідомленні. Зручно, що слухати музику можна прямо в чаті з ботом. Таким чином бот може повноцінно замінити сторонній додаток, наприклад плеєр на телефоні. Ви просто шукаєте та одразу слухаєте музику.
Отже, бачимо, що API телеграм-ботів є досить зручною та багатофункціональною функцією месенджера. Тому ця стаття присвячена створенню простого чат-бота. Я вирішив зробити примітивний таймтрекер, щоб показати вам приклад. Це найпростіший випадок взаємодії з API телеграми.
Просте створення Telegram Bot API
Отже, почнемо.
Ви можете знайти документи тут: https://core.telegram.org/bots/api.
Для початку необхідно створити та зареєструвати бота в Telegram. Давайте знайдемо ботів Father @BotFather. Це бот для реєстрації ботів.
Повідомляємо йому, що хочемо створити власного чат-бота:
/newbot
Наступним запитом він просить створити назву для бота. Так як мій бот буде стежити за часом, я даю йому відповідне ім'я і пишу:
timeTrackerBot
Следующим шагом нужно задать юзернейм для бота (будет использоваться для поиска @username). Условие: username должен заканчиваться на bot или _bot. Пишу:
my_time_tracker_bot
Готово! У відповідь отримуємо токен, це означає, що ми зареєстрували нового чат-бота.
Розробка
Для початку я вирішив подивитися, які бібліотеки пропонує нам Інтернет. Варіантів безліч. З повним переліком запропонованих варіантів можна ознайомитися тут. Я вибрав TelegramBotApiBundle для Symfony. Встановлюємо:
composer require borsaco/telegram-bot-api-bundle
і продовжуйте.
Пакет підтримує роботу з декількома телеграм-ботами API одночасно, крім того, є можливість налаштування (відправка тільки розробнику) і робота через проксі. Для тестового прикладу нам багато не потрібно, тому прибираємо непотрібне.
config/packages/telegram.yaml
Я також переніс токен у змінну APP_TELEGRAM_TOKEN в .env
Трохи теорії
Telegram API, працює в двох режимах: ви можете отримувати оновлення з сервера через метод getUpdates() або налаштувати webHook. Перший варіант хороший, тому що він гранично простий, але його недолік в тому, що потрібно постійно запитувати сервер, чи є якісь оновлення. Варіант webHook дозволяє не думати про те, як отримати оновлення, а зосередитися на їх рендерингу. У цьому випадку Telegram сам надішле оновлення за вказаною нами URL-адресою.
Перевага другого підходу очевидна).
Щоб зареєструвати Webhook, ми зробимо наступне:
$bot=$botService->getBot('timeTracker');$bot->setWebhook(['url'=>'https://myWebSite.com/webhook']);
Webhook у значенні url - це маршрут, який ми зробимо трохи пізніше. $botService - це об’єкт сервісу Borsaco\TelegramBotApiBundle\Service\Bot, який можна вставити в будь-яке місце проекту.
Важливо: Telegram API підтримує лише надсилання на https!
Давайте перевіримо наш Webhook: якщо ви надішлете повідомлення нашому телеграм-боту, він надішле нам у відповідь такий набір даних:
{"update_id":21406673,"message":{"message_id":24,"from":{"id":701891111,"is_bot":false,"first_name":"aleksei","language_code":"ru"},"chat":{"id":701891111,"first_name":"aleksei","type":"private"},"date":1580672814,"text":"/help","entities":[{"offset":0,"length":5,"type":"bot_command"}]}}
Нас цікавить частина з текстом повідомлення. В даному випадку зрозуміло, що я надіслав бот «/help». Очевидно, він не відповідає.
Наявність суб'єктів у відповіді свідчить про те, що повідомлення було сприйнято ботом як команда (все, що починається зі слеша і написано латиницею, є командою для Telegram).
Давайте створимо контролер. Це буде точка входу для запитів від Telegram API, куди надходитимуть оновлення.
Для зручності я заношу всю логіку в сервіс MessageProcessor, куди передаю рядок, отриманий з Telegram.
Суть проста - є 4 команди: допомогти, запустити, зупинити, повідомити.
Кожна команда передбачає певну поведінку.
Також є 2 ситуації, коли відповідь скаже користувачеві, що ця дія на даний момент заборонена (поки таймтрекер не зупинено - запустити новий не можна. Відповідно, поки немає активності - зупинити нічого не можна).
Я додав такі суб'єкти:
- Користувач, який має поля name, telegramId і collection timelines,
- Часова шкала, яка має дату початку та дату завершення: startedAt, stopedAt.
Тобто користувач може мати декілька часових шкал (які мають час початку та час зупинки).
Ми отримуємо рядок json, і перше, що я роблю, це декодую рядок і отримую об’єкт.
$response= \json_decode((string)$telegramUpdate);
Далі перевіряю, чи є такий користувач у базі (дані про користувача, який написав боту, беруться з response-> message-> from). Якщо такого користувача немає, створюємо його.
Оскільки ми хочемо, щоб команди «help» і «/ help» сприймалися ботом однаково, ми перекладаємо текст повідомлення від користувача в нижній регістр і видаляємо звичайну та зворотну косу риску.
$messageText=mb_strtolower($response->message->text);$messageText=str_replace([’\\’, ’/’], ’’,$messageText);
Тепер перевіряємо отриману команду - чи є вона в списку команд, які ми можемо обробити. І тепер, виходячи з того, що маємо, ми надсилаємо відповідь.
$messageCommand=$this->isSupports($messageText) ? $messageText:false;
Якщо це команда «start» або «stop», ми створюємо часову шкалу для цього користувача або відповідно завершуємо поточну та інформуємо про це користувача.
Якщо це «report», ми обчислюємо час у всій шкалі часу на сьогодні та надсилаємо загальну кількість годин і хвилин користувачеві.
Ми надсилаємо повідомлення за допомогою методу sendMessage, який приймає масив параметрів. Обов’язкові параметри: «chat_id» і «text».
Оскільки це тестовий випадок, я обмежився простим switch / case для вибору команди.
switch($messageCommand){caseself::HELP_COMMAND:$this->bot->sendMessage(['chat_id'=>$user->getTelegramId(),'text'=>self::ANSWERS[self::HELP_COMMAND]]);break;caseself::START_COMMAND:if($this->timelineService->doesActiveExist($user)){$this->bot->sendMessage(['chat_id'=>$user->getTelegramId(),'text'=>self::BAD_ANSWERS['existNotStoppedTimeLine']]);break;}$this->telegramService->startTimeForUser($user);$this->bot->sendMessage(['chat_id'=>$user->getTelegramId(),'text'=>self::ANSWERS[self::START_COMMAND]]);break;caseself::STOP_COMMAND:if($this->timelineService->doesActiveExist($user)){$this->bot->sendMessage(['chat_id'=>$user->getTelegramId(),'text'=>self::ANSWERS[self::STOP_COMMAND]]);break;};$this->bot->sendMessage(['chat_id'=>$user->getTelegramId(),'text'=>self::BAD_ANSWERS['timeLineNotFound']]);break;caseself::REPORT_COMMAND:$timeForToday=$this->timelineService->getTodayTotalByUser($user);$this->bot->sendMessage(['chat_id'=>$user->getTelegramId(),'text'=> \sprintf(self::REPORT_COMMAND,$timeForToday)]);break;default:$this->bot->sendMessage(['chat_id'=>$user->getTelegramId(),'text'=>self::ANSWERS[self::HELP_COMMAND]]);}
Загальний вигляд сервісу виглядає так:
Таким чином, у нас є бот, який допоможе відстежувати час протягом дня.
Висновок
Підводячи підсумки, можна сміливо сказати, що бот Telegram API досить функціональний і простий в освоєнні. Великою перевагою є хороша документація та готові бібліотеки, доступні для використання.