Створення простого бота Telegram Time Tracker
Сьогодні кожен має 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) { case self::HELP_COMMAND : $this->bot->sendMessage(['chat_id' => $user->getTelegramId(), 'text' => self::ANSWERS[self::HELP_COMMAND]]); break; case self::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; case self::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; case self::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 досить функціональний і простий в освоєнні. Великою перевагою є хороша документація та готові бібліотеки, доступні для використання.