How to deploy Python Telegram bot using Webhooks on Google Cloud Platform


Вместо предисловия


image

— Напиши телеграм-бота. Сейчас даже школьники пишут, — сказала она.
— А почему бы и нет, — подумал я тогда ( — Ну, ну, — сказал бы я сейчас).


Мы сидели в Бине и за чашкой кофе обсуждали возможности тестирования идей с моделями искусственного интеллекта на близком и не очень круге друзей. Лена, моя бывшая коллега, и во всех отношениях не блондинка, только что закончившая магистратуру, рассуждала так. Создав бота, можно сэкономить силы и время на интерфейсе, сосредоточившись на ядре с машинным обучением. Согласитесь, что устоять против такой логики “спортсменки, комсомолки и просто красавицы” в то прекрасное воскресное утро было невозможно. Решено. Телеграм-бот, значит телеграм-бот.


Первым делом я залез в гугл и нашел большое число ссылок “как сделать бот за 30 минут”. Это меня настолько воодушевило, что дальше названий я не пошел и занялся созданием ядра. В самом первом приближении мне предстояло написать систему обработки поисковых запросов с использованием NLP (natural language processing). Написание ядра заняло некоторое, вполне разумное, время (все же опыт кока-колой не пропить). И через несколько дней я был готов к тому, чтобы за пару часов обернуть первую тестовую версию ядра в пару другую команд send-receive, запустив все это в Телеграме на благо моим друзьям. Но не тут-то было.


Неожиданно возник целый клубок проблем. Потратив пару дней на поиски в интернете и общение с коллегами по цеху, я понял, что очевидное не очевидно, и еще одна “инструкция” точно не повредит. Так и появилась эта статья.


Вместо вступления. Библиотеки


Существует несколько библиотек на Python’е для телеграм-ботов. Сам Телеграм ссылается на три из них: python-telegram-bot, pyTelegramBotAPI, AIOGram. Ссылки на эти библиотеки и примеры на других языках можно посмотреть на сайте Телеграма здесь. Конечно, существует еще API самого Телеграма. В ходе своих экспериментов я попробовал два варианта API Телеграма и pyTelegramBotAPI. И пока остановился на втором.


В целом впечатления от библиотек следующие. Все очень похожи, что неудивительно. Python-telegram-bot, мне показалось, имеет лучшую документацию из всех, хотя работа над ней прекратилась (надеюсь, ошибаюсь). Видно, что над pyTelegramBotAPI работа продолжается, появляется новый функционал. AIOGram кажется моложе и зеленее. В последней не понравилось, что вместо документации по webhook’ам шла отсылка на сайт Телеграма. Но все течет, все меняется. И окончательный выбор еще предстоит сделать.


Больше к вопросу с библиотеками в этой статье мы возвращаться не будем.


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


  1. Телеграм-бот с использованием polling;
  2. Телеграм-бот с использованием webhook.

Самые неоднозначные темы с телеграм-ботами это ssl-сертификаты, webhook’и, диплоинг. Вокруг этого и сосредоточим наше внимание. Ниже я приведу пошаговую инструкцию, которая позволит наиболее простым и надежным способом за минимальное время разместить ваш телеграм-бот на одном из лучших облачных сервисов в мире, да еще и совершенно бесплатно (по крайней мере, в первый год). Создание ssl-сертификатов, их регистрация в Телеграме и другие смежные вопросы также будут освещены. При необходимости я буду давать пояснения, почему мы используем тот или иной вариант, те или иные команды.
 


Polling vs Webhook


image

Если бы не противостояние polling vs webhook и некоторые сложности (отчасти надуманные) с webhook’ом, в этой статье не было бы необходимости. Раз это имеет принципиальное значение, давайте разберемся подробнее.


Что такое бот и как он общается с Телеграмом? Очевидно, бот это программа, которая работает на вашем компьютере или сервере. А общение с Телеграмом происходит методами отправки и получения сообщений. И если с отправкой сообщений примерно все ясно, вариант один — послать (адрес “куда” мы знаем). То вариантов получения ботом сообщений от Телеграма два.


Первый — это опрос (буквальный перевод слова polling) сервера Телеграма на наличие сообщений для бота. Второй — это “почтовый ящик” с ip-адресом (webhook — можно перевести как веб-ловушка), на который приходят сообщения от сервера Телеграма.


Самая простая аналогия с реальной почтой. Пусть почта (почтовое отделение) — это сервер Телеграма, а вы — это ваш бот. Тогда, в первом случае (polling) вам приходится ходить на почту за корреспонденцией. И если хотите получать сообщения без задержек, то придется не ходить, а буквально бегать без передышек взад и вперед. Как понимаем, жить на почте в ожидании сообщений запрещено! Во втором случае вы сообщаете почтовому отделению свой домашний адрес и ждете корреспонденцию спокойно дома, попивая чай или покуривая бамбук.


Конечно, для человека первый вариант кажется самым суровым. Но, говоря между нами, если бегать за сообщениями мы посылаем железку с кодом, то нам должно быть все равно. И так бы и было, если бы не одна проблема. Время от времени, почта (сервер Телеграма) то закрывается на обед, то переезжает. И у вас в первом варианте (polling) происходит трагедия, которая в реальном мире ботов заканчивается их зависанием и сбоем.


Во втором же случае “почтового ящика” с адресом (webhook’ом) такого не происходит. Потому что ни вы, ни ваш бот, никуда не ходите, а просто ждете. И вам все равно, куда переехало почтовое отделение, потому что почту вам приносит почтальон.


Таким образом, по объему кода (см. ссылку выше) первый вариант кажется проще. А второй логичнее, но тяжелее. Для его реализации нужно завести адрес, подтвердить его достоверность и поднять веб-сервер, на который и будут приходить сообщения от Телеграма.


Конечно, для первого варианта есть возможность внести в код обработку исключений. Например, если почта закрыта, постоять и подождать. Но интернет утверждает, что все равно боты с polling’ом только для поиграться.


Я сначала не поверил, что проблема настолько серьезна. И сделал вариант с polling’ом, предполагая, что как-нибудь позже перепишу на webhook’и. Но это не сработало. На моем домашнем компе с macOS бот работал без проблем и час и два (конечно, с VPN) и не падал. Но стоило мне перенести его на облачный сервер на Linux’е он не мог проработать и 20 минут. Я пытался решить эту проблему разными путями и настройками, но получал лишь разный код ошибок. Селяви, это Телеграм. Потеряв день, пришлось взяться за webhook’и, не откладывая на потом. В конце концов, я же хотел запустить бот на сервере сейчас, а не через год.
 


Переписываем код на webhook


Не так страшен webhook как его малюют.


Если у вас уже есть код бота с polling’ом, то переписать его на webhook не составит почти никакого труда. Сравните коды из примеров библиотеки pyTelegramBotAPI. Вы обнаружите пересечение кода. Строки 13-25 из первого примера совпадают со строками 56-67 второго примера. Это логический блок, отвечающий за обработку команд, сообщений и ответов. В данном случае пример слишком простой, но главную суть он отражает.


# Handle '/start' and '/help'
@bot.message_handler(commands=['help', 'start'])
def send_welcome(message):
    bot.reply_to(message, """Hi there, I am EchoBot.
I am here to echo your kind words back to you. Just say anything nice and I'll say the exact same thing to you!""")

# Handle all other messages with content_type 'text' (content_types defaults to ['text'])
@bot.message_handler(func=lambda message: True)
def echo_message(message):
    bot.reply_to(message, message.text)

Таким образом, логический блок бота нужно оставить без изменений, а остальное поменять, используя банальный copy-past кода вне логического блока из второго примера (т.е. копируем строки 1-55 и 70-87). Если вы присмотритесь к коду, который копируете, то без труда выясните, что с помощью библиотеки aiohttp ваш бот устанавливает http-сервер и начинает прослушку выделенного для этого порта, а питоновский модуль ssl отвечает за шифрование и сертификаты. Помимо aiohttp существуют и другие аналогичные библиотеки, примеры с которыми можно посмотреть здесь.


Итак. Бот с webhook’ом у нас есть. Осталось выложить его на сервер и запустить, попутно получив данные для заполнения пустых значений в коде API_TOKEN, WEBHOOK_HOST, WEBHOOK_PORT, WEBHOOK_SSL_CERT, WEBHOOK_SSL_PRIV. За каждым из этих значений скрывается определенная процедура с тайным смыслом.
 


Регистрируем бот в Телеграме


Начнем с токена бота. Чтобы его получить заходим в Телеграм-мессенджер и подключаемся к боту @BotFather. Вводим команду “/newbot”. В ответ BotFather предлагает нам ввести name и username нашего бота и присылает вожделенный токен вида “712308912:DLGSteczdUnPdnNYLzNikaGOhome7l9q3vova” (конечно, у вас будет другой). Сохраняем токен, никому не показываем. В коде присваиваем его переменной API_TOKEN.
 


Где размещать? Google Cloud Platform vs Heroku


Несколько слов о муках выбора облачного сервиса. В интернете вы встретите много примеров, где телеграм-боты размещаются на Heroku. Heroku — это PaaS платформа (платформа как сервис). Вы заливаете код на GitHub, а оттуда на Heroku. И ваш бот начинает сразу же работать. Не надо устанавливать сервер, Python и библиотеки, все уже сделано за вас. По крайней мере так в идеале. Это решение мне также рекомендовал один из моих знакомых. Но я от него отказался.


Как я понял, основным посылом для рекомендаций Heroku во многих руководствах “бот за 30 минут” является наличие на Heroku бесплатного тарифного плана. Но, если вы посмотрите внимательнее, то окажется, что этот план весьма ограничен и не позволит вам развернуть бесплатно бот, который бы работал сутки напролет.


В отличие от Heroku Google Cloud Platform (GCP) предлагает целый год бесплатной работы на всех сервисах в пределах 300 долларов. За эту сумму за год много чего можно попробовать. На мой взгляд это шикарный подарок, особенно для тех, кто занимается датасайенсом. И, конечно, GCP тоже PaaS, полный список сервисов здесь. Есть и безсерверные решения, как и на Heroku. Я выбрал более консервативный путь для большего контроля в будущем, а именно серверное решение Compute Engine на GCP.
 


Google Cloud Platform


Project, instance, static ip, port


Создаем проект и сервер


Заходим на GCP, выбираем “Get started for free” и следуем инструкциям. Если все сделаем правильно, то у нас появится аккаунт с суммой в 300 долларов и даже первый созданный проект. Выбираем этот проект или создаем другой в верхнем меню, что не сложно. И затем идем на вкладку “Compute Engine/VM Instance” в боковом открывающемся меню.


image

Нажимаем “CREATE INSTANCE” и выбираем конфигурацию сервера, например, как показано на скрине ниже. Вы можете выбрать конфигурацию micro и тогда это будет стоить всего 4 доллара в месяц. Можно и шикануть, средства будут списываться из подарка от Google в 300 долларов.


image

В итоге получаем.


image

 


Делаем ip-адрес статичным


В колонке “External IP” мы видим адрес, который вбиваем в переменную WEBHOOK_HOST в нашем боте (в данном примере это 35.224.231.90). На этот адрес позже мы получим ssl-сертификат. Если вы собираетесь достаточно долго пользоваться ботом (даже, если будете менять сервер в пределах GCP), то рекомендую сделать этот ip-адрес статичным, чтобы сохранить его при переходе с одного instance на другой. Делается это в разделе “VPC/External IP addresses”.


image

Меняем поле “Type” с “Ephemeral” на “Static”, задав имя статичному адресу, чтобы потом не запутаться, если позже у вас будет десяток ip-адресов.


image

image

image

 


Открываем порт


Для корректной работы webhook’а нам потребуется открыть порт, на который будет стучаться Телеграм. В настоящее время (июль 2019 года) Телеграм поддерживает следующие порты: 443, 80, 88, 8443. Подробнее вы можете посмотреть полное руководство от Телеграма по webhook’ам здесь. Мы откроем порт 8443. В нашем боте это значение уже присвоено переменной WEBHOOK_PORT. Осталось настроить правило брэндмаура в GCP для нашего инстанса webhook-bot. Как это сделать? Идем на вкладку “VPC/Firewall rules” и нажимаем на “CREATE FIREWALL RULE”.


image

И создаем правило, как это показано на скрине ниже.


image

 


Подключаемся к серверу, устанавливаем библиотеки


На этом основная настройка сервера на Google Cloud Platform завершена. Мы завели аккаунт на GCP, создали проект, в рамках проекта создали сервер (инстанс) на базе Ubuntu 19.04, зарезервировали ip-адрес, открыли порт 8443.


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


Заходим в GCP на вкладку “Compute Engine/VM Instance” и в поле “Connect” нажимаем на “SSH”.


image

На вашем локальном компьютере должен открыться терминал с доступом к инстансу на GCP. Это обычная среда Ubuntu. Устанавливаем conda или virtualenv, создаем виртуальную среду и устанавливаем Python 3 с основными библиотеками. Дополнительно устанавливаем библиотеки, необходимые для работы нашего бота:
pip install pyTelegramBotAPI
pip install aiohttp


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


Получение самоподписанного сертификата SSL для IP


Надо сказать, что в интернете вокруг темы ssl сертификатов для Телеграма сплошной хоррор. Как сказал Михаил Лермонтов на этот счет:


“Смешались в кучу кони, люди,
И залпы тысячи орудий
Слились в протяжный вой…”


Если почитать, что пишут в интернете, то получится, что если бот на webhook’ах не работает, то во всем виноваты неправильно полученные сертификаты. А еще, их нельзя регистрировать на ip. И вообще самоподписанные сертификаты Телеграм не принимает. И даже, если он примет сертификат, то это не значит, что ваш бот будет работать, и Телеграм даже ошибку не пришлет.


Вот такие страшилки. К чему я это пишу? К тому, что эти страшилки обошлись мне в еще одни потерянные сутки, даже пришлось пропустить просмотр “Хоббита”. Мой бот первоначально отказывался работать на webhook’ах. Это произошло из-за моих ошибок при его размещении на сервере (тогда я этого не знал). Но из-за страшилок выше я искал проблему не там, сосредоточившись на решении проблем с ssl-сертификатами, которых не было. Зато я приобрел бесценный опыт. Я нарегистрировался сертификатов на годы вперед как на доменные имена, так и на ip, как самоподписанные, так и платные, как в виде двух файлов, так и в виде четырех. Я узнал, какие бывают сертификаты и как делать из них цепочки. Надеюсь, мне это когда-нибудь пригодится.


Одно хорошо, вам на эти грабли наступать не надо, я это уже сделал за вас. Вполне допускаю, что как не бывает “дыма без огня”, так и для таких страшилок был повод. Но на текущий день ситуация похоже устаканилась.


Прямо сейчас мы с вами получим ssl-сертификат самым удобным для нас путем, а именно на ip (а не на доменное имя), самоподписанные (т.е. бесплатно) и обойдясь всего одной строчкой без всяких сайтов.


В терминале (который мы еще не закрыли) подготовьте отдельный каталог, в который мы сохраним файл сертификата и приватный ключ. Зайдите в этот каталог и из него вызовите следующую команду.


$ openssl req -newkey rsa:2048 -sha256 -nodes -keyout url_private.key -x509 -days 3560 -out url_cert.pem

В ответ вы получите запросы на заполнение ряда простых полей. Заполните их по примеру ниже. Но, конечно, укажите верно код своей страны, область, город, название компаний (если они есть), подставьте свой ip и свой email.


image

В итоге вы получите два файла в каталоге, из которого запустили эту команду. Один файл url_cert.pem и есть ваш сертификат, а второй приватный ключ url_private.key. В коде бота для них выделены переменные WEBHOOK_SSL_CERT и WEBHOOK_SSL_PRIV соответственно. Не забудьте вписать пути доступа к этим файлам вместе с наименованиями. Скачайте копии и сохраните их у себя на будущее.


Не закрывайте терминал.
 


Регистрация webhook в Телеграме


Регистрация webhook с самоподписанным сертификатом


Пожалуй, это самая волнительная часть “А что скажет Телеграм?”. Без всяких преамбул просто вбивайте в свой терминал следующую команду. IP-адрес замените своим, порт 8443 не трогайте. Вставьте полученный от BotFather токен между “/bot” и “/setWebhook”) вместо YOUR-TOKEN. Команду запускайте из каталога, где хранятся сертификаты.


$ curl -F "url=https://35.224.231.90:8443" -F "certificate=@url_cert.pem" https://api.telegram.org/botYOUR-TOKEN/setWebhook

Если все верно, вы получите в терминале лаконичное сообщение от Телеграма, что webhook установлен.


Регистрация webhook с “правильным” сертификатом


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


https://api.telegram.org/botYOUR-TOKEN/setWebhook?url=https://YOUR.DOMAIN:8443/YOUR-TOKEN/

Обратите внимание, что ваш токен указывается два раза. Кроме того, важно, чтобы YOUR.DOMAIN был указан в таком виде, как это в сертификате. Например. Я зарегистрировал домен mydreem.com, регистратор оформил мне ssl-сертификат на домен www.mydreem.com. Вместо YOUR.DOMAIN указывать нужно последний www.mydreem.com.


Как проверить, установлен ли webhook?


Вы можете проверить установлен ли webhook из броузера следующей командой:


https://api.telegram.org/botYOUR-TOKEN/getWebhookInfo

Это работает для всех случаев. В ответ вы должны получить что-то типа этого в случае, если webhook установлен и бот запущен:


{"ok":true,"result":{"url":"https://35.224.231.90:8443/712308912:DLGSteczdUnPdnNYLzNikaGOhome7l9q3vova/","has_custom_certificate":true,"pending_update_count":0,"max_connections":40}}

Или типа этого, когда webhook установлен, но бот не работает (не запущен):


{"ok":true,"result":{"url":"https://35.224.231.90:8443/712308912:DLGSteczdUnPdnNYLzNikaGOhome7l9q3vova/","has_custom_certificate":true,"pending_update_count":2,"last_error_date":1564506964,"last_error_message":"Connection refused","max_connections":40}}

Как сбросить webhook?


Иногда вам может потребоваться сменить у бота сертификат, например, в случае переезда на другой сервер (домен). Тогда токен остается, а сертификат меняется (webhook устанавливается заново). Или возникает необходимость запустить бот не на webhook’е, а на polling’е (если webhook установлен, polling работать не будет). В обоих случаях полезна команда, которая “сбрасывает” webhook:


https://api.telegram.org/botYOUR-TOKEN/setWebhook

Теперь все готово для загрузки кода на сервер и запуска бота. Если вы мастер игры в командной строке в десять пальцев, то для вас это не составит труда. И через минуту ваш бот заработает. Если нет, то следующий раздел поможет несколько облегчить задачу загрузки/выгрузки файлов на ваш сервер и управления ими.
 


Cloud Shell or “Drop Dead Beautiful” (“Убийственно красива”)


Мне как человеку далекому от системного администрирования тяжело понять, почему в век торжества искусственного интеллекта мы все еще общаемся с серверами командной строкой как в былые годы на ЕС/СМ — ЭВМ. Предположим, что существуют причины, для простых смертных неведомые. Тогда такое явление как Cloud Shell в GCP необходимо принять с большой радостью. Это средство позволяет, пусть и с костылями, но несколько облегчить труд непосвященных. Хотя может и навредить, если не знать некоторые нюансы.


Cloud Shell предоставляет доступ к облачным ресурсам из командной строки прямо из броузера. Вы можете легко управлять своими проектами и ресурсами, не устанавливая Google Cloud SDK или другие инструменты в своей системе (последние два предложения взяты с их странички, “легко” зачеркнуто мной). Как управлять своими проектами из командной строки можно узнать здесь. Но основная для нас фишка не в этом. Запустим Cloud Shell (см. gif-ку ниже, взято с сайта google).


image

Вы зашли в Cloud Shell. Теперь, если нажать на кнопку в виде карандаша, то откроется “редактор текстов версии beta”.


image

В меню “Help/About” можно узнать, что это “theia-editor-for-cloudshell-preview 0.0.1”. Я не буду под конец статьи углубляться в особенности работы с этим редактором. Выделю только ключевые моменты. Это не только редактор для нескольких языков (проверял только с Python), но и файловый менеджер. Вы можете легко организовать обмен файлами между Cloud Shell и вашим локальным компьютером. Мышкой можно переносить файлы и каталоги в пределах пространства Cloud Shell.


Обратите внимание, я нигде выше не написал, что с помощью редактора можно редактировать и управлять файлами на вашем сервере (инстансе). Но даже этого вполне достаточно, чтобы облегчить нам жизнь. И об этом немного ниже. А сейчас я расскажу нечто важное о Cloud Shell. Это необходимо понимать, чтобы не попасть в просак, как это случилось со мной.


Cloud Shell — это сервер на базе Debian с 5Гб в виде диска и 1.7Гб оперативной памяти. На нем уже предустановлено некоторое ПО (включая Python). Cloud Shell легко можно принять за ваш инстанс, установить на него conda, создать env и запустить бота. И, если бот на polling, то он будет работать. Если на webhook, то работать не будет, потому что все порты у Cloud Shell закрыты! И сколько не открывай порты на инстансе, на Cloud Shell они не появятся.


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


Кстати, Cloud Shell существует вне пространства и времени (шутка близкая к истине). Если вы удалите у себя все инстансы и все проекты, ваш Cloud Shell продолжит существовать еще 120 дней со всем, что вы в него закачали. Не путайте это с резервным копирование ваших серверов.


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


Например, если вы хотите воспользоваться Cloud Shell как терминалом для доступа к своему серверу (например, webhook-bot), то необходимо в командной строке активировать свой инстанс:


$ gcloud compute ssh webhook-bot --zone us-central1-a

После этого вы оказываетесь в командной строке своего сервера. Вернуться в Cloud Shell можно командой “exit”. Предположим вы хотите скопировать какие-то файлы из Cloud Shell на инстанс. Для этого в командной строке Cloud Shell (не инстанса) набираем следующую команду:


$ gcloud compute scp --recurse ~/telebot/my_favorite_robot.py webhook-bot:~/telebot --zone us-central1-a

Если поменять источник с назначением местами, то произойдет копирование с инстанса в Cloud Shell. В примере ниже мы копируем файл “ex1.py” из каталога “/examples” сервера “webhook-bot” в каталог “/telebot2” Cloud Shell:


$ gcloud compute scp --recurse webhook-bot:~/examples/ex1.py --zone us-central1-a ~/telebot2

Эти и другие команды по обмену файлами можно найти здесь. Обмениваться можно не только файлами, но и целыми каталогами. В итоге мы получаем следующую схему, облегчающую нам жизнь. С локального компа мы копируем файлы нажатием пары кнопок на мышке в Cloud Shell через файловый менеджер редактора “theia-editor-for-cloudshell-preview 0.0.1”. А затем переправляем это на своей сервер (инстанс) командной строкой. Если нам требуется что-то оперативно отредактировать, мы это можем сделать в том же редакторе в Cloud Shell и забросить файлы той же командой на сервер. Получается достаточно оперативно.


Верю, что существуют более элегантные и удобные во всех отношениях пути обмена и управления файлами для облачного сервера на GCP (без установки дополнительных программ на локальный компьютер). Возможно, через Cloud Storage. Скажу откровенно, я потратил на поиски и эксперименты по этому вопросу всего несколько часов. Поэтому буду признателен, если вы поделитесь своими идеями или работающими решениями в комментариях.


Итак. Мы загрузили файлы на сервер (инстанс). И, если вы еще не запустили бот, то пора это сделать, зайдя в терминал сервера, активировав необходимое виртуальное окружение и набрав команду по типу “python my_webhook_bot.py”. Все должно заработать. Чтобы бот продолжил работать при закрытии терминала, его нужно запускать в фоновом режиме. Например, так “nohup python my_webhook_bot.py &”. Квест, как вывести процесс из фонового режима и завершить его, я оставляю вам, если, конечно, вы еще не знаете.
 


Заключение


Надеюсь, эта статья оказалась вам полезной, помогла сэкономить время и избежать ошибок, которые сделал я при написании телеграм-бота с webhook’ами и размещением его на сервере. Все, что описано в статье, я прошел сам, и на дату написания, июль-август 2019 года, работало именно так. Хочу выразить особую благодарность Михаилу Крутикову за совместный серфинг по просторам сервисов GCP и помощь в осознании ряда важных истин. Я открыт для вопросов, дискуссий и буду благодарен за советы в комментариях к этой статье. Или пишите смело мне в Телеграм @Eduard_Lanchev.


До последнего момента для меня был открыт вопрос, стоит ли давать ссылку на свой телеграм-бот. Друзья убедили меня, что стоит. Пообщаться с моим ботом можно по адресу @AelitaSoccerBot. Бот в самом начале пути, и над ним предстоит еще много работать. Поэтому пишите, если что-то не так, пишите, если хотите поделиться своим опытом или дать совет. Моя благодарность не будет иметь границ в разумных пределах.


И в завершении хочется пожелать успеха всем ботоводам и моим коллегам датасайентистам!
 


Полезные ссылки


  1. Telegram Bot API
  2. Bot Code Examples
  3. python-telegram-bot
  4. pyTelegramBotAPI
  5. AIOGram
  6. ssl — TLS/SSL wrapper for socket objects
  7. Heroku
  8. Google Cloud Platform
  9. Google Compute Engine
  10. Marvin's Marvellous Guide to All Things Webhook
  11. Conda
  12. Virtualenv
  13. Cloud Shell
  14. The gcloud compute command-line tool
  15. Transferring files using the gcloud command-line tool
  16. Google Cloud Storage

Комментарии (32)


  1. lgorSL
    05.08.2019 16:12
    +2

    Мне показалось удобным через sshfs файлы перекидывать. Монтируешь папку с проектом на сервере как папку на своём компе и спокойно редактируешь/добавляешь файлики.


    1. EddyLan Автор
      05.08.2019 16:33
      +1

      Спасибо! Речь об этом ru.wikipedia.org/wiki/SSHFS? Что-то типа папок dropbox получается?


      1. lgorSL
        05.08.2019 16:59
        +2

        Да, оно. Тоже писал бота) Ещё через ssh можно интерфейс прокидывать — например, чтобы на сервере открывался редактор текста как окно на моём компе. (Линукс и там и там).
        P.s. Ещё я регал двух ботов — один для отладки у себя, второй на сервере, а файлик с токеном для бота добавлял в .gitignore


        1. EddyLan Автор
          05.08.2019 17:43
          +1

          Спасибо! Интересно. Я вот сейчас серьезно задумался над папкой дропбокса :) Кажется, самое простое решение :)


    1. kvlsky
      06.08.2019 13:54

      Если пользуетесь VS Code, то в новой stable версии (1.36) выкатили классную фичю для работы по ssh — Visual Studio Code Remote — SSH. Есть драг н дроп, терминал и т.д. Буквально вчера тестил — работает на ура.


  1. uoziod
    05.08.2019 17:03
    +2

    А можно поподробнее про webhooks лучше чем polling? Мне всегда выбор одного подхода против другого виделся больше как следствие предпочтительного языка.
    PHP? Удобнее webhook.
    NodeJS? Удобнее polling.


    1. EddyLan Автор
      05.08.2019 17:37
      +1

      Длительное исследование по сравнению этих решений не проводил. Но polling на сервере начал отрубаться сразу. Решить проблему не получилось. Пришлось идти к вебхукам (как изначально советовал интернет :) ) С вебхуками возникла другая проблема, корни которой, я подозреваю, все те же самые, что и с polling, но бот на webhook не падает. Проблема вот в чем. После некоторого простоя, бот как бы зависает, залипает (это с webhook). Как результат, идет системная ошибка 104, сообщение в телеграм от бота не уходит (то есть запрос пользователя в боте остается без ответа). Но если отправить запрос второй раз, то сообщение отправится. Я искал причину ошибки. Оказалось, что ошибка не такая уж и редкая. На гитхабе pyTelegramBotAPI нашел намек, что можно обработать ее исключением и отправить еще раз :) Поставил эту заплатку, то есть ловлю ошибку и отправляю сообщение еще раз. Сейчас все ок. Реальная причина ошибки не ясна. То ли это особенность GCP (GCE), то ли pyTelegramBotAPI, то ли их связки… Одно точно скажу, замена библиотек, устанавливающих веб-сервер (при том, что pyTelegramBotAPI остается) на это никак не повлияла. Думаю, снова переписать бот на чистом API Telegram'а и посмотреть. Если все будет работать, то виновата pyTelegramBotAPI, если нет, то виноват GCE или некая его особая настройка, возможно, сервер нужно отдельно от бота поднять будет. Но это все имеет смысл только в случае коммерческого продукта и для успокоения души ;) Поэтому, отвечая на ваш вопрос, лично у меня оснований утверждать об особых преимуществах webhook над polling ровно столько, что на webhook в моем случае бот работает, а на polling нет. Возможно, на polling сейчас (после написания статьи) я смогу бот на сервере настроить, появились идеи. Но логика webhook правильнее.


    1. tmin10
      05.08.2019 17:52
      +1

      Если на сервере для обработки каждого запроса отводится новый поток, тот вебхук позволяет нативно получить многопоточность. Тогда как для пулинга нужно эту многопоточность реализовывать через средства языка/фреймворка.


  1. tmin10
    05.08.2019 17:55
    +1

    Если к боту на heroku обращаются довльно часто, то система не затушит его и он будет работать постоянно. Если же к нему нет обращений, то система выключит его и перестанет уменьшать лимит тарифа, а первый же запрос от сервера телеграма снова разбудит (будет небольшая задержка между запросом и ответом).
    Также можно придумать WA, когда эндпоинт бота автоматически пингуется каким-то сервисом по крону и не даёт системе убить скрипт.


    1. donkeystep
      05.08.2019 20:02
      +1

      На Heroku можно выбрать тип воркера:
      — web (засыпает через 30 минут неактивности)
      — service (не засыпает)
      Бесплатных часов как раз хватает на один сервис 24/7. В Procfile пишешь «service:...»
      Мой бот на java уже год работает, ни разу не засыпал сам по себе.
      И Long Polling в Java telegrambots lib не отваливался никогда.


    1. au_vlad
      06.08.2019 16:18
      -1

      Да, насколько я понимаю, обращение к боту должно будить его, если настроен webhook. Но когда я делал бота — хотел настроить webhook и, после изучения документации, пришёл к выводу, что это сделать не получится, так как heroku не предоставляет статичесикй IP-адрес (по крайней мере — на бесплатном аккаунте), а без него не получить сертификат. Сделал бота на pooling, работало всё нормально, но даже сообщение боту не будило его после засыпания, так как он сам обращается за новыми сообщениями к telegram, а не они приходят к нему. Решилась проблема действительно настройкой стороннего сервиса, который мониторит приложение и таким образом периодически будит его своим обращением. Для этих целей на heroku нашёлся очень удобный аддон:
      elements.heroku.com/addons/newrelic
      Уже полгода так всё и работает.


      1. mayorovp
        06.08.2019 16:24

        Вот только, на самом деле, с веб-хуками проблем не будет. Самоподписанный сертификат же, как выяснилось, телеграм прекрасно принимает — а динамический IP можно сообщить ему через API при старте бота.


        newrelic же выглядит диким оверкилом: это точно проще чем сменить тип воркера на service ?


        1. au_vlad
          06.08.2019 16:46

          newrelic весьма прост. А насчёт сменить тип на service — почему-то когда я искал решение для засыпающего бота, такой вариант мне не попался — ни в документации heroku, ни где-либо ещё. Если можно просто изменить одно слово в Procfile то, конечно, это проще всего.


      1. tmin10
        06.08.2019 17:28
        +1

        А зачем вешать вебхуку на ip адрес, есть heroku предоставляет удобный https endpoint для веб приложений? Есть мануал от авторов python-telegram-bot: https://github.com/python-telegram-bot/python-telegram-bot/wiki/Webhooks#heroku.


        1. au_vlad
          06.08.2019 17:55

          Как я себе представлял на момент создания бота — статический IP нужен для получения сертификата.


  1. bougakov
    05.08.2019 18:08
    +2

    SSL-сертификат на IP можно получить куда как проще. Есть чудесный сервис SSLip.io, который резольвит X.X.X.X.sslip.io на X.X.X.X

    На X.X.X.X.sslip.io отлично выписывается сертификат Let's encrypt, команда на его продление закидывается в crontab.


    1. EddyLan Автор
      05.08.2019 18:12
      +1

      А чем не устраивает командная строчка из статьи? Можно установить 10 лет и продлять не надо.


      1. bougakov
        05.08.2019 18:26
        +1

        нормальный сертификат всегда лучше самоподписанного.
        Let's encrypt предоставляет прекрасную инфраструктуру, зачем себе забивать голову, если можно пользоваться готовым и бесплатно?


        1. EddyLan Автор
          05.08.2019 18:30
          +1

          Спасибо! Логику понял.


  1. Taraflex
    05.08.2019 19:48
    +1

    При выборе между хуками и пулинга вспоминается аксиома Эскобара.
    Почему-то нигде не видел туториала, где используется tglib (ядро телеграм клиентов) напрямую, который лишен минусов обоих способов + доступен более обширный апи.
    А еще, если в вашей сети телеграм клиент может пробиться через блокировки РКН, то и tglib сможет — на проде такой плюс не особо важен, но разработку иногда может сделать чуть проще.

    От питона я далек, под nodejs использую эту штуку github.com/Bannerets/tdl#login-as-a-bot наверняка и под python есть адекватные обертки.


    1. EddyLan Автор
      05.08.2019 20:19
      +1

      Спасибо! Размещая на GCE о блокировках уже не думаешь, сервер не в России. Я рассматривал варианты для Python, tglib, как вижу, это либа под nodejs (эта github.com/nodegin/tglib ?). Я могу использовать еще API Telegrama, что также пробовал. А как tglib взаимодействует с Телеграмом, если она лишена всех недостатков?


      1. Taraflex
        05.08.2019 20:26
        +1

        Я опечатался (2 раза)
        Речь о github.com/tdlib/td
        Официальная кроссплатформенная нативная шаред библиотека.
        Оно подключается и взаимодействует с серверами телеги так же как и обычный телеграм клиент. Не нужен ни веб сервер, ни пулинг.


        1. EddyLan Автор
          05.08.2019 21:23
          +1

          Спасибо, уже смотрю. Вопрос вот в чем. Стоит ли поднимать клиента ради скромного бота? Но интересно, что вариантов реализации одного и того же решения много. Это не может не радовать.


          1. Taraflex
            05.08.2019 21:42
            +1

            Если уже есть готовые хуки то имхо переписывать не стоит, тем более на GCE. Я не в курсе как вообще на GCE свои бинари накатывать.
            + Если собирать tdlib не в том линукс окружении, где она будет работать (на win проще там 4 dll руками можно скопировать), то вероятно будет ругаться на не те версии системных библиотек.

            Также обратите внимание что для сборки tdlib требуется >8Гб памяти через gcc (нужен свежий) и чуть менее 4Гб памяти через clang/msvc

            Поэтому я собрал tdlib один раз под конкретную версию debian и накатываю её на vps клиентам.

            Сейчас наверняка снова кто-нибудь будет писать про докер и прочие контейнеры, конкретно в моем случае просто — нет (лень объяснять).


            1. EddyLan Автор
              05.08.2019 23:49
              +1

              Я тоже заметил требовательность к ресурсам. В целом понял. Спасибо!


            1. yaroslavche
              06.08.2019 08:52

              Также обратите внимание что для сборки tdlib требуется >8Гб памяти через gcc (нужен свежий) и чуть менее 4Гб памяти через clang/msvc

              tdlib/td вполне реально собрать с 2Gb RAM (+swap) с gcc. В 1.4.0 оптимизирована сборка и теперь она вообще без проблем собирается примерно за 20 минут на относительно слабом железе


    1. tmin10
      05.08.2019 21:17
      +1

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


      1. Taraflex
        05.08.2019 21:21
        +2

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


  1. karabas_b
    06.08.2019 14:48

    да уж, сэкономил силы на интерфейсе. кэп подсказывает, что для функционала «вопрос-ответ» для друзей было бы вполне достаточно генерировать простую хтмл-страничку с текстом, полем для ввода и кнопкой «отправить». и просто давать друзьям урл.

    но в целом статья познавательная, спасибо.


    1. EddyLan Автор
      06.08.2019 14:55

      Спасибо за отзыв. Возможно и так :) Но в моем случае все же нужно не только отправлять, но и получать, да и вообще чат может быть весьма оживленным :) Обычной странички в html не хватит, нужно чат встраивать. С тем опытом, который у меня теперь есть, мне подключить бот к логическому ядру, сделать сервер, сертификаты, все залить и запустить займет примерно 30-60 минут. Так что в этом смысле все же решение с ботом в мессенджере правильная идея. К тому же, ядро я собираюсь развивать, поэтому опыт с ботом полезен и на будущее времени будет уходить по минимуму.


      1. mayorovp
        06.08.2019 15:08

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


        Если что, гуглить по ключевым словам "python flask example chat"


        1. EddyLan Автор
          06.08.2019 15:58

          А почему бы и нет. Будет время, можно попробовать. У меня помнится и книжка по Flask'у есть :)