Jenkins telegram bot настройка
В данной статье я расскажу о том как написать Telegram бота с нуля. При этом, делая это не на коленке, а достаточно “по-взрослому”: имея “на борту” Django (для управления контентом, настройками бота, расписанием выполнения задач и пр.), Celery, python-telegram-bot в качестве обертки над Telegram API, Redis, PostgreSQL для хранения всего, что нужно боту. При этом для разработки используя Docker, а деплой в продакшн реализуя через Dokku с python buildback-ом от Heroku.
В качестве примера разработае м простенького бота, который порадует всех любителей поэзии: раз в сутки он будет присылать случайный стих, который будет парсить с одного из сайтов со стихами. Кроме этого, пользователи бота смогут по кнопке получать сколько угодно случайных стихов здесь и сейчас. Понравившиеся стихи смогут добавлять в избранное с возможностью последующего просмотра и прочтения.
Первым делом оставлю ссылки на репозитории, где все можно посмотреть, потыкать, клонировать и применить в своих проектах:
Telegram поддерживает два режима работы:
Как легко догадаться, webhook-и намного более эффективны. Аналитики подсчитали, что, в среднем, 98.5% запросов в режиме polling-а происходит “вхолостую”, тогда как эффективность webhook-ов, само собой, равняется 100%, потому что обращение происходит тогда и только тогда, когда появляется событие для этого обращения.
Polling — быстрое идеальное решение для разработки и отладки, поэтому его мы и используем для этих целей.
Webhook — идеален для продакшена.
Первое с чего следует начать разработку бота — с его регистрации.
Для регистрации бота добавляем в Телеграм @BotFather — головного бота, где происходит регистрация новых ботов.
Пишем команду /newbot и задаем наименование нашего бота. В моем случае: “Поэтическая душа”.
После этого бот попросит ввести имя на латинском для никнейма бота, которое должно заканчиваться на _bot. В моем случае будет: poetry_soul_bot.
После этого бот выдаст информацию о том, по какой ссылке можно начать диалог с ботом и токен, который необходимо сохранить и добавить в .env файл проекта (об этом позже).
На этом — регистрация бота завершена. Можно переходить к разработке.
.env файлик содержит в себе переменные окружения, которые нужны нам для локальной разработки. При деплое на прод мы единожды при развертывании приложения пропишем несколько переменных окружения на сервере и более про это забудем. Основные переменные, которые нам понадобятся:
Dockerfile — докерфайл проекта, базовые директива для докера, применяться будет только для локальной разработки.
docker-compose.yml — тут описаны сервисы, которые будут подняты в контейнерах докером на локальной машине. По умолчанию это: db (PostgreSQL), redis, web, bot, celery, celery-beat (последние два нужны для периодических задач, которые, возможно, понадобится выполять нашему боту).
entrypoint.sh — последний файлик для локальной работы проекта, говорит докеру, что делать после того как поднимутся все контейнеры. Тут говорим, что надо сделать миграции, создать админа по кредам из .env файла, прогрузить различного рода фикстуры, если таковые имеются в проекте, собрать статику и запустить сервер.
Procfile — данный файл необходим для Dokku при деплое на прод. Он содержит одну или более строк описывающих типы процессов и ассоциированных им команд. При деплое приложения в Dokku будет создан Docker образ, затем Dokku извлечет файл Procfile и команды, которые в нем описаны, будут переданы в docker run. В нашем случае мы говорим, что необходимо сделать миграции, поднять gunicorn сервер, запустить celery воркера и celery beat.
requirements.txt — тут все понятно, список зависимостей нашего бота, которые будут ставится как при развертывании на локальной машине, так и при деплое на продашкн.
run_pooling.py — для кейса, когда приложение работает в pooling режиме (на локальном ПК).
runtime.txt — говорит Dokku, какую версию Python мы хотим использовать.
staticfiles — в данной папке вся статика проекта (админка).
media — по умолчанию используем данную папку для хранения медиа файлов. Предположим, мы из админки закачиваем какие-то картинки, которые надо будет показывать пользователям, вот они падают в данную папку потому, что в settings.py проекта прописано: MEDIA_ROOT = os.path.join(BASE_DIR, ‘media’)
logs — очень важная папка, потому что по логам мы будем отлавливать все, что происходит с ботом. В settings.py у нас задан логгер по умолчанию, который будет писать в файлик main.log все, что надо, ротируя его каждые 5 Мб.
dtb — головная папка Django проекта. Тут, скорее всего, ничего менять не придется, кроме settings.py.
tgbot — вся бизнес-логика бота находится в данной папке.
tgbot/admin.py — регистрирует модели приложения для использования в админке.
tgbot/models.py — содержит все модели, используемые ботом. Как минимум для базовой работы необходимы модели:
- Config — для задания различных параметров бота в формате “параметр” — “значение”;
- User — для пользователей бота;
- Location, Arcgis — для работы с геокодированием, если будет надо боту;
- UserActionLog — фиксация действий пользователя.
tgbot/tasks.py — celery задачи, которые можно устанавливать на исполнение по расписанию в очень удобной форме в админке проекта благодаря django-celery-beat.
tgbot/utils.p — различные функции-хелперы.
tgbot/fixtures — здесь могут быть различные фикстуры с данными для моделей проекта.
tgbot/migrations — тут все понятно, миграции проекта.
tgbot/templates — кастомные шаблоны для админки.
tgbot/handlers/admin.py — модуль-обработчик событий для пользователей с правами администратора. Наша модель пользователей, описанная в модуле tgbot/models.py содержит возможность задавать различные права пользователям. Так вот, если пользователь — администратор, то можно описать для него тот или иной функционал, который будет реализовываться при вызове им команд бота. Логика как раз описывается в данном модуле.
tgbot/handlers/files.py — модуль-обработчик событий, связанных с отправкой боту файлов. Опять же, по-умолчанию, заточен на администраторов. Когда мы посылаем какой-нибудь файл боту (фото, видео, документ и пр) — файл сохраняется и ему присваивается уникальные ID. В последующем, если мы хотим отослать файл пользователю, можно в соответствующий API метод передавать полный URL до этого файла (например, на внешнем хранилище), а можно передать просто Telegram ID этого файла. Вот как раз тут описана функция, которая возвращает ID файла отосланного боту, если его отослал пользователь с правами администратора.
tgbot/handlers/handlers.py — головной модуль-обработчик всех пользовательских событий, которые могут возникнуть у пользователей в процессе взаимодействия с ботом. Логика большинства функций довольно простая: прилетает в метод два параметра — update и context, из которых можно извлечь все: id чата, информацию о пользователе, что отослано и т.п. Далее происходит та или иная бизнес-логика, после чего, либо вызывается api метод edit_message, либо send_message, либо какая-то другая отправка, либо ничего, в зависимости от нужд логики.
tgbot/handlers/utils.py — функции-хелперы для работы обработчиков. Базовый набор: функция отсылки пользователя действия “печатаю”, декоратор для логирования и обертка над api методом send_message с обработками исключений.
tgbot/handlers/manage_data.py — в данном модуле определяются переменные для параметра callback_data, задаваемого при использовании клавиатуры. Т.е. в keyboard_utils модуле мы создаем клавиатуры, которые состоят из кнопок, а у кнопок есть два главных параметра: текст и обратный вызов, т.е. какая строка будет отправлена боту, когда пользователь нажмет на кнопку.
tgbot/handlers/static_text.py — в данный модуль выносим все текстовые данные, которые используем в боте.
Теперь, имея представление о структуре нашего проекта, мы можем немного углубиться в код, чтобы понять как и что происходит.
Прежде всего, небольшая вводная. Разворачивая проект в Docker-е на локальной машине, не забывайте, что после того как внесли правки в код, необходимо перезагрузить контейнер с ботом. Т.е. вы делаете:
docker-compose up -d — build — в результате поднимутся все контейнеры.
Далее просматриваете их:
И, найдя в списке контейнер с ботом пишите (когда это необходимо):
docker restart XXX , где XXX — ID контейнера.
Соответственно, если вы изменяете Celery-задачи, то не забывает перезагружать контейнер соответствующий для тестирования.
Для нашего проекта, не считая стандартных моделей из скелетона, необходимо добавить две новых: для хранения полученных стихов и для хранения списка избранных стихов пользователем.
Модуль /tgbot/poetry.py содержит в себе класс, который описывает головную логику проекта. Давайте рассмотрим его.
У класса есть два свойства — адрес источника и домен источника для последующей компоновки ссылок на стихи.
Инициализируя объект класса мы привязываем его к конкретному пользователю, который обратился за тем или иным функционалом.
Метод load_poem делает следующее: если в него передан id, то он просто выбирает из базы стих по id и возвращает его, если ничего не передано, то используя библиотеки requests и bs4, метод парсит источник со стихами выбирая случайный стих.
Метод add_to_fav получает id стиха, который пользователь хочет добавить в избранное и делает это.
Метод get_authors — нужен в нескольких местах нашего бота. Когда пользователь выбирает посмотреть содержимое избранное, то сначала бот показывает ему алфавитный список всех авторов, которые есть в избранном. Кликнув по букве, бот показывает уже полный список всех авторов на эту букву, наконец, кликнув на автора — пользователь получает список стихов. Так вот, данный метод либо просто возвращает всех авторов из избранного пользователя, либо список из первых букв фамилий авторов. В обоих случаях список отсортирован по алфавиту.
Метод get_poems — тут все просто, получает фамилию автора и возвращает все его стихи из избранного пользователя.
Метод get_poem_by_id — имеет красноречивое название.
Метод format_poem — статический, получает объект модели Poem и возвращает строку в разметке Markdown, которая будет красиво выводится ботом (название — жирным, автор — курсивом).
Центральный модуль нашей логики — /tgbot/handlers/dispatcher.py, а именно его метод setup_dispatcher.
Здесь происходит связка событий с нашими реакциями на события. Например, следующая строчка говорит о том, что мы хотим добавить в диспетчер обработчик команд (это, когда пользователь печатает слеш и что-то там), который при вводе команды /start вызовет соответствующую функцию command_start из модуля commands:
А следующая строчка говорит о том, что мы хотим добавить в диспетчер обработчик нажатия на кнопку пользователем CallbackQueryHandler, который должен вызывать функцию send_more из модуля hnd, когда бот получит callback-команду SEND_MORE, описанную в модуле md:
Теперь рассмотрим как бот реагирует на все это дело на примере функции send_more модуля hnd:
Как видим, функция получает два параметра — update и context.
Context — объект типа telegram.ext.callbackcontext.CallbackContext и он нам не особо интересен, т.к. в нашей функции все, что мы делаем, просто в конце с помощью этого объекта вызываем нужный нам мето: edit_message_text, send_message и т.п.
А вот update — интересный объект, ниже привожу все данные, которые прилетают в нем:
Как видно, в update-е содержится избыточная информация по всему, что нас может интересовать: кто написал, кому написал, что написал, какая была клавиатура, все даты и времена, какая кнопка была нажата и т.п.
В ряде случаев нам необходимо передать не просто некий callback от пользователя, но еще и тот или иной его выбор. Предположим, если речь идет о клавиатуре с алфавитом. В функции обработчике нам необходимо знать, какую буквы выбрал пользователь. Мы, конечно, можем для каждой буквы написать свой обработчик, но это будет ужасно. Поэтому, можно поступить проще. Посмотрим пример формирования клавиатуры для такого случая:
Как видно, мы указываем в callback_data не просто фиксированное значение, а указываем еще после символ решетки и текущую букву кнопки. После чего, в диспетчере мы определяем обработчик события как всегда, обычном способом:
А вот уже в функции обработчике получаем доступ к тому, что было передано после решетки вот так:
Как видно, мы получаем запрос из update.callback_query, а затем обращаемся к свойству data, которое и хранит нужную нам информацию, вычленяя из нее необходимую букву и производя необходимые манипуляции.
Что такое Dokku? Если вкратце и совсем просто Dokku — это open source Heroku, сделанный на базе Docker. Когда я прочел это определение, то следующим вопросом лично у меня был: “А что такое Heroku?”. Да, я слышал о Heroku неоднократно, но никогда не вдавался в подробности. Так вот, Heroku — это облачная PaaS (платформа как услуга)-платформа, поддерживающая множество языков программирования. Heroku, так же как и Dokku, обеспечивает быстрый и легкий деплой приложений “под ключ”: базы данных, логирования, мониторинг, контейниризация и т.д. и т.п. Heroku — модный и дорогой, Dokku — маленький, бесплатный и, возможно, не столь user friendly, но тем не менее, очень простой в изучении.
Можно сказать, что Dokku — это обертка над Docker, Heroku Buildpacks, Nginx и Git. Docker — обеспечивает Dokku контейнерами; Dokku использует свой собственный базовый образ Docker с ubuntu, всеми необходимыми пакетами, Heroku buildpack-ами и т.п. Heroku Buildpack — набор скриптов, задача которых определить, соответствует ли приложение заданному типу, скомпилировать и выпустить его. Билдпак, который Dokku запускает внутри контейнера, создает всю необходимую среду выполнения, устанавливает все зависимости и на выходе приложение готово к работе на 100%. Что касается Nginx, то Dokku передает внутрь приложения номер порта 5000, а для внешних использует порты 49200+, а трафик из/в контейнер проксирует Nginx, который нам не надо конфигурировать вообще никак, потому что Dokku все сделает сам. Наконец, в аспекте GIT-а тоже все очень удобно: Dokku использует git-hooks, отслеживает, когда код пушится в GIT и запускает скрипт, который и делает всю магию: создает docker образ из базового, запускает скрипт инициализации среды, запускает само приложение и рестартует Nginx.
Как я раньше разворачивал проекты, если не использовал Docker (хотя, с ним тоже были свои сложности)? Заход на чистый сервер, обновление пакетов, установка всех необходимых пакетов, создание пользователей, морока с правами, установка git, инициализация и пул проекта, установка virtual env, установка всех зависимостей проекта, установка python сервера и настройка, установка nginx и настройка для связи с Python сервером, установка БД и конфигурация приложения, установка redis, celery и их конфигурация, установка supervisor-а, который бы менеджил все это дело… В общем, очень много всяких муторных действий. Ниже я опишу как все это сделать буквально за пяток команд по 10–15 букв в каждой с использованием Dokku.
В рамках данного раздела с установкой и настройкой Dokku хотелось бы акцентировать еще на одном нюансе — работа со статикой. Если в нашем проекте все отлично и нам не о чем беспокоиться, потому что мы используем whitenoise , то в случае деплоя проекта, который не использует whotenoise, а хочет управлять статикой через nginx (хотя, даже официальная дока Heroku советует использовать whitenoise)— у нас возникнут небольшие проблемы. Дело в том, что хоть у нас и будет выполняться команда collectstatic, но nginx не будет знать, где находится наша статика — это проблема №1. Проблема №2 — статика будет внутри нашего контейнера с приложением, поэтому нам надо как-то смонтировать volume-ы, чтобы nginx имел доступ к статике проекта. Делается это все довольно просто. Ниже пара пунктов реализации:
Бонусом к очень удобной и беспроблемной настройке теперь идет прекрасный и очень удобный деплой: вносите правки в ваш код, делаете комит — пуш, и во время пуша в dokku происходит ребилд проекта, установка всего необходимого, прогоны тестов (если они у вас есть) и сборка проекта. Невероятно круто и очень удобно.
Ознакомившись и разобравшись с написанным, склонировав проект-скелетон, вы готовы начать писать своего бота. Мы изучили основы работы ботов, детально изучили структуру проекта, аспекты реализации функционала и удобные техники разворачивания решения на продакшне.
Ну а если вы не хотите со всем этим морочиться и хотите заказать разработку, то я, как член команды компании Webtronics готов вам в этом помочь. Заходите к нам на сайт, связывайтесь удобным образом и уверен, что мы сможем быть полезны друг другу :)
Cегодня я хотел бы поделиться своим опытом синхронизации уведомлений Continuous Integration серверов (Bamboo, Jenkins, TeamCity и тп.) с бесплатным мессенджером от Павла Дурова — Telegram.
Данную проблему захотелось решить из-за того, что программисты на нашем проекте не реагировали на уведомления, присылаемые на почту, а телеграмм у нас корпоративный мессенджер. Подопытным CI является наш корпоративный Bamboo, с которого и должны приходить эти самые уведомления.
Обязательно запоминаем эти данные — они нам ещё пригодятся!
Также необходимо будет поставить два сторонних python модуля:
Вам нужно положить файлик с client_secret.json в ту же папку, что и скрипт, и запустить выполнение, после чего откроется браузер и предложит вам разрешить доступ к gmail почте.
Я для нашего проекта создал новый gmail аккаунт, к которому и привязал уведомления с bamboo, но вы можете использовать уже созданный. Как душа пожелает!
Не забываем проверить, что уведомления с CI приходят на эту почту. Теперь в папке со скриптом, помимо client_secret.json, должна лежать папка credentials. Если все так, значит вы сделали верно.
Теперь нам нужно немного видоизменить скрипт, для этого меняем переменные TELEGRAM_BOT_CREDENTIALS и CHAT_ID на те, что мы узнали выше. Теперь пробуем выполнить скрипт, и, если все правильно, то в чат с вашим ботом должны упасть все непрочитанные письма.
В двух словах о том, что делает скрипт. Сначала он находит все непрочитанные письма в вашем почтовом ящике, затем он отправляет текст от них к вам в телеграмм, а далее — делает эти письма прочитанными.
Остался самый последний шаг: нам нужно организовать повторяющийся запуск этого скрипта. Я использовал cron на нашем ubuntu сервере. Грузим нашу папку со скриптами по ssh на сервер и настраиваем cron:
Для начала устанавливаем cron:
Затем запускаем редактор настроек cron:
Добавляем повторение нашего скрипта через каждую минуту:
Проверяем, что все сохранилось корректно:
Теперь наш скрипт запускается каждую минут. Если новых писем на почте нет, то он ничего не делает. А в ином случае — отправляет текст письма к вам в телеграмм и делает письмо прочитанным.
На этом все. Спасибо за внимание. Если будут вопросы — пишите, с радостью отвечу.
Одно из правил работы с CI — все должны видеть статус сборок, тоесть синхронизация. А что может быть лучше чем уведомление о неудачном, или напротив удачном, билде в чате.
Третий вариант более универсален, ну и с первым плагином я потратил очень много времени но таки и не смог его запустить.
Настраиваем бот
Создаем новый бот и канал для уведомлоений.
Находим @botfather и следуя инструкции создаем нового бота. Важно получить токен бота. Токен позволит любому управлять вашим ботом.
Получаем iD чата для отправки уведомлений
Через адресную строку браузера делаем запрос
Где меняем <TOKEN> на значение из пункта создания бота.
Ответ на запрос:
Нужно сохранить chat.id и сохранить знак - перед числом, это полный идентификатор.
Готовим Jenkins
Настраиваем переменные для авторизации API
- Manage Jenkins -> Manage Credentials
- Для уведомлений в телеграмм создаем новый домен.
- Добавляем переменную для ID чата и отдельно переменную для токена созданного бота. Указываем пронятные ID для дальнейшей подстановки переменных.
Настриваем пайплайн
Предположим уже настроен pipeline через Jenkinsfile. Осталось добавить к нему post-секцию
Post-секция выполняется независимо от результата стадий основного пайплайна. И именно в нем будут действия для отправки уведомлений.
Все указанные условия должны содержат выполняемые скрипты:
- строка начинающаяся с withCredentials декларирует переменные для подмены в запросе.
- текст отправляется в формате markdown .
Запускаем билд и при успехе получаем уведомление:
Дата изменения: October 19, 2020
Вам также может понравиться
Что такое хорошая [продуктовая] задача.
2 мин на чтение
Небольшое лирическое вступление. Когда-то я играл в баскетбол и тренер беспрестанно вбивал нам в голову — “если твой пас не приняли в этом твоя вина”, или ес.
Лицензии при разработке программных продуктов софта
1 мин на чтение
В разработке ПО часто является разумным использовать уже готовые фреймворки и библиотеки для решения тех или иных задач. Важно помнить, что несмотря на стату.
Интернет термины
15 мин на чтение
Часто бывает необходимо подобрать правильно определение для договора или элементарного соглашения, а иногда найти простые слова чтобы объяснить клиенту/партн.
Дизайн привычных вещей. Дональд Норман
3 мин на чтение
С одной стороны, книгу нужно бы прочитать каждому дизайнеру интерфейсов и промышленному дизайнеру, что бы понять какую боль могут приносить их разработки буд.
Делаем простого бота для публикации новостей в канал и автоматические ответы на вопросы за 6 шагов.
После бурных обсуждений в ИТ-прессе по поводу эффективности чат-ботов, они заняли свою нишу в экосистеме пользователей и компаний. Например, часто проекты внедряют ботов для оповещения о каких-либо событиях, а службы поддержки используют их для того, чтобы быстро отвечать на часто задаваемые вопросы клиентов.
В этой инструкции мы рассмотрим наиболее простой способ создать бота собственными руками и объясним, как он работает.
Бот в Telegram создается при помощи другого бота под названием BotFather. Отправляем ему команду /newbot, выбираем имя, которое будет отображаться в списке контактов, и адрес. Например, «Бот для DTF» с адресом «dtf_news_bot».
Через BotFather также можно добавить аватарку для бота, описание и прочее.Теперь создаём канал с любым названием и адресом, и переходим в его настройки. Всё, что требуется — добавить в список администраторов созданного нами бота — именно он будет публиковать заметки в канал.
Для поиска бота можно использовать его адрес. Например, «dtf_news_bot».Вместо RSS-ленты можно отслеживать появление новых записей в Twitter или Facebook-аккаунте — для каждой функции в IFTTT предусмотрены отдельные модули.
Теперь переходим ко второму шагу — выбираем действие, которое будет выполняться при обнаружении новой записи в RSS. Нажимаем на "That" и ищем Maker Webhook, "Make a web request" — с помощью этого модуля можно отправлять запросы к любым сервисам. В нашем случае — к боту в Telegram.
На этом этапе понадобится веб-хостинг и сертификат SSL, который можно получить бесплатно с помощью сервиса Let's Encrypt.
Если у вас хостинг Vscale, то можно создать для бота отдельный сервер. Это делается при помощи одного клика в панели управления.
В код бота нужно внести всего два изменения:
Теперь необходимо связать Telegram и файл на сервере, чтобы запросы, отправленные боту в мессенджере, обрабатывал наш скрипт.
Для этого понадобится консоль. У разных хостинг-провайдеров она может находиться в разных разделах интерфейса управления сайтом. Кроме того, можно воспользоваться программой Terminal на macOS, введя через неё команду ssh имя_пользователя@адрес_домена.
После ввода пароля, печатаем для нашего поддомена простую команду:
С помощью ботов можно упростить себе задачу коммуникации с пользователями, создав для них умного помощника. Боты понимают текстовые команды и могут обращаться к API вашего вебсайта, сервиса или быть самостоятельным продуктом с уникальными услугами. Обычно, для создания бота требуется сервер, но в этой статье мы разберём подход, позволяющий обойтись без него. Он идеален при резких колебаниях количества запросов и прекрасно выдерживает пиковые нагрузки.
Настроить Телеграм-бота на Облачных функциях, используя официальный API и заготовленный нами пример, можно в 3 этапа:
- Запрограммировать логику работы Телеграм-бота.
- Зарегистрировать нового бота в Telegram.
- Связать бота и его логику воедино.
Запрограммировать логику работы Телеграм-бота
Пройдите следующие шаги с нашим примером бота, чтобы освоиться:
Зарегистрировать нового бота в Telegram
Чтобы Telegram знал о существовании нашего бота, его нужно зарегистрировать. Для этого существует только один способ:
-
Найдите бота @BotFather в Телеграм. Это официальный бот, созданный специально для управления ботами.
Отправьте ему команду:
Запишите полученный токен от @BotFather — это ключ для работы с Telegram API.
Теперь у нас API, регистрация бота в Telegram и его токен. Осталось связать это вместе.
Как связать бота и его логику воедино?
На этом этапе надо сделать так, чтобы бот знал токен пользователя, а Telegram знал, где его API:
- Найдите переменную окружения «TOKEN» в настройках функции. Мы оставили её пустой на первом этапе.
- Укажите в этой переменной токен вашего бота, который получили от @BotFather.
- Нажмите Сохранить и развернуть.
Способ 1. Через бота @SelectelServerless_bot
Бот ответит тем, что получит из Telegram API. Если всё хорошо, это будет «true».
Чтобы удостовериться в том, что всё корректно, отправьте команду:
В ответ должна прийти информация о webhook, в том числе указанный вами URL.
Способ 2. Из заготовленной формы
Примечание: может понадобиться наличие VPN.
Готов для работы бот, доступный в Телеграме, отвечающий на команды и способный автоматически масштабироваться под нагрузкой и без сервера.
Читайте также: