Читаем статьи с Хабра с помощью Slack-бота
![](https://habrastorage.org/getpro/habr/upload_files/a57/344/8c0/a573448c0c5db34c11251e744e8622fa.png)
Привет, Хабр! Сегодня расскажу, как на хакатоне для студентов SkillFactory я сделал Slack-бота, который оповещает студентов разных курсов о выходе статей на Хабре по интересующей их тематике. На КДПВ вы видите тестирование внедрённого бота; ссылку на его код вы найдёте в конце статьи.
Раньше наш редактор ежедневно заходил на Хабр, просматривал только что вышедшие статьи и вручную оповещал сотрудников SF о всех новых публикациях в специальном канале в Slack. Редактору это надоело, и мы придумали, как упростить ему жизнь и автоматизировать процесс.
Чтобы не утомлять, рассказываю о самых основных моментах, которые касаются объединения трёх основных частей проекта: Django, Celery и парсера Slack-бота. Опущены такие моменты, как получение токена Slack-бота на сайте, построение шаблона и заполнение URL проекта. Об этом уже есть много статей, а строение этого проекта ничем не отличается от большинства других. Я выполнял проект на Windows WSL 2, поэтому указаны команды для Linux. При этом время на выполнение проекта было ограничено, поэтому я старался упрощать все задачи. Поехали!
Вначале создадим виртуальное окружение и проект Django:
python3 -m venv venv
source venv/bin/activate
# В репозитории есть requirements.txt для установки нужных версий библиотек
pip install django
django-admin startproject slackbot
В корневом каталоге проекта создаём новое приложение:
cd slackbot
python3 manage.py startapp interface
Не забываем добавить interface
в settings.py
и в INSTALLED_APPS
.
Теперь продумаем модели нашего проекта. Напоминаю: программа должна парсить Хабр и отправлять новые статьи в каналы Slack, делать это будет Slack-бот, то есть потребуются две модели — модель бота и модель статьи. Построим модель бота:
![](https://habrastorage.org/getpro/habr/upload_files/a13/7f2/ec4/a137f2ec45bd6ac6fea1fdd39ed4897f.png)
name — имя бота;
token — токен бота;
channel — имя канала, куда отправляются сообщения;
task — по условиям есть 2 задачи. Чтобы не писать код автовыбора задачи, я решил прикреплять бота к определённой задаче в момент его создания;
editor_text — текст перед ссылкой на статью;
bot_tags — здесь пропишите теги, по которым бот будет искать статьи.
Теперь построим модель статей, чтобы хранить в базе данных то, что распарсил бот:
![](https://habrastorage.org/getpro/habr/upload_files/955/a59/cc5/955a59cc5b8d3c8e614282ca768b0c5f.png)
headline — заголовок статьи;
public_time — время публикации статьи;
link — ссылка на статью;
status — это поле-флаг определяет, в каком состоянии находится статья. Если она уже была отправлена ботом на канал Slack, статус нужно изменить на sended;
tags — теги статьи;
task — дополнительное поле принадлежности к статье на всякий случай.
Также я решил создать модель конфигурации бота. По условиям в задаче у пользователя должна быть возможность конфигурировать ботов отдельно по времени отправки сообщения и на задержку парсинга.
![](https://habrastorage.org/getpro/habr/upload_files/ba1/87f/311/ba187f31184320803aa72e709037df00.png)
task — у нас 2 задачи, поэтому нужно выбирать из них;
task_id — нужен для управления ботом. Бот будет запускаться асинхронно из под Celery, поэтому, чтобы запускать и останавливать конкретного бота, лучше сразу сохранить идентификатор его задачи;
mode — выбор режимов отправки нового контента в каналы Slack;
minute — если пользователь выбрал отправку каждый час, можно задать минуту часа;
hour — если раз в день, можно задать час отправки;
day — при отправке раз в неделю можно задать день недели отправки.
Модели проекта готовы, пора применить миграции, создать суперпользователя и приступить к работе над интерфейсом для пользователя.
python3 manage.py makemigrations
python3 manage.py migrate
python3 manage.py createsuperuser
python3 manage.py runserver
Самый быстрый способ сделать шаблон для проекта — взять готовый и доработать его. Я выбрал этот шаблон.
Создаём папку static/interface
и кладём туда скачанный шаблон. Все шаблоны будут наследоваться от этого шаблона. В нём есть готовая шапка, подвал, некоторые элементы js; остаётся вырезать лишнее и подправить section. Вьюха получилась довольно длинной, поэтому опишу только интересные моменты.
![](https://habrastorage.org/getpro/habr/upload_files/8ac/6f3/d7d/8ac6f3d7de92cd7f4807c4cf21c59789.png)
Это вьюха главной страницы, где можно создать и просмотреть список ботов, поэтому она одновременно наследуется от CreateView
и ListView
. Форма стандартная, а в конце вы найдёте ссылку на репозиторий. Функциональная часть страницы выглядит так:
![](https://habrastorage.org/getpro/habr/upload_files/974/962/2db/9749622dbbb11f26f34001171a2218d7.png)
Здесь можно зарегистрировать, запустить, остановить Slack-бота, а также сконфигурировать задачи и просмотреть список зарегистрированных ботов. Интерфейс между пользователем и ботом готов. Следующий этап — реализация бота.
Перед тем как писать код парсера, нужно прояснить одну вещь. Задача Django — принимать команды пользователя и передавать их боту, а бот должен работать сам по себе, не отвлекая Django от его задач; самым верным решением в этой ситуации будет асинхронность. Поэтому вначале нужно подключить к проекту Celery, а логику бота реализовывать как задачи Celery. Для работы Celery необходим Redis, запускайте его в отдельном окне терминала.
sudo apt-get update
sudo apt-get install redis
redis-server
После необходимо установить celery и redis в окружение Django:
pip install celery
pip install redis
Далее мы должны добавить некоторые настройки в конфигурацию проекта — settings.py
, дописав следующие строки:
CELERY_BROKER_URL = 'redis://localhost:6379'
CELERY_RESULT_BACKEND = 'redis://localhost:6379'
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
Затем согласно документации Celery переходим в директорию проекта и рядом с settings.py
добавляем файл celery.py
:
import os
from celery import Celery
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mcdonalds.settings')
app = Celery('mcdonalds')
app.config_from_object('django.conf:settings', namespace = 'CELERY')
app.autodiscover_tasks()
Также по рекомендациям в документации Celery мы должны добавить следующие строки в файл init.py
в той же папке, что и settings.py:
from .celery import app as celery_app
all = ('celery_app',)
Задачи Celery запускаются в отдельном окне из той же директории, что и runserver
. Запускаем:
celery -A slackbot worker -l INFO
![](https://habrastorage.org/getpro/habr/upload_files/847/8fb/263/8478fb263a44a0600ef90613a06cb5c4.png)
Готово! Celery настроен и может принимать задачи.
Наконец, переходим к самому интересному — разработке функций бота! Базовая структурная единица Celery — задача (task). По условиям проекта задачи две:
Парсить все статьи в блоге компании и оповещать о выходе новых статей через Slack.
Парсить все статьи в блоге компании, но оповещать только о статьях с определёнными тегами.
Значит у нас будут задачи parse
и parse2
. Создаём файл task.py
в каталоге приложения interface
. Чтобы зарегистрировать функцию как задачу Сelery, достаточно импортировать декоратор from celery import shared_task
и задекорировать функцию. Я создал новый файл tasks_extension.py
и вынес туда всю логику бота. В конце останется лишь импортировать основные функции триггеры для запуска задачи в tasks.py
.
В tasks_extension.py
пишем алгоритм работы парсера. Для этого нужны две библиотеки:
pip install requests
pip install beautifulsoup4
Берём html-код страницы Хабра.
![](https://habrastorage.org/getpro/habr/upload_files/adb/242/dbf/adb242dbf61525d493cd03ba9a4d9d60.png)
Функция print
здесь используется в качестве простой реализации логирования.
@onceEveryXSeconds(delay)
— это декоратор задержки, чтобы не собирать HTML Хабра каждую секунду.
![](https://habrastorage.org/getpro/habr/upload_files/ac6/746/3a5/ac67463a5bb6f188b4a435da9466c211.png)
Находим на странице статьи и записываем их в переменную items
:
![](https://habrastorage.org/getpro/habr/upload_files/5c7/726/7f0/5c77267f01069bf49499259873d03fb2.png)
Метод find_all
библиотеки beautifulsoup4
вернёт список, итерируя который мы заполним модель статей в базе данных. Итак, у нас есть логика парсера, а собранные данные лежат в базе, остаётся написать конфигурацию для рассылки статей по каналам.
![](https://habrastorage.org/getpro/habr/upload_files/b42/8d3/2ca/b428d32ca6eb32a85349b29fcc365bae.png)
Функция принимает на вход один из режимов работы и отправляет рассылку согласно настроенному времени. Интерфейс настроек выглядит так:
![](https://habrastorage.org/getpro/habr/upload_files/b7c/acb/6b3/b7cacb6b3f3fffcb0c8430f3a8742b4c.png)
И функция отправки сообщений:
![](https://habrastorage.org/getpro/habr/upload_files/a5a/314/1fd/a5a3141fde81fc4332699a3dfd52445d.png)
Теперь импортируем необходимые функции в файл tasks.py
и напишем основную задачу для Celery:
![](https://habrastorage.org/getpro/habr/upload_files/e4c/0dc/77a/e4c0dc77ae1c1bceca7d13dc42fba0bb.png)
Сама задача будет запускаться кнопкой на скрине выше; напишем функцию запуска бота:
![](https://habrastorage.org/getpro/habr/upload_files/e76/bc7/c0d/e76bc7c0d523ec1c8df8254f9434c390.png)
Главное — записать id задачи, чтобы в дальнейшем остановить её. Функция остановки выглядит так:
![](https://habrastorage.org/getpro/habr/upload_files/f54/cfc/b5d/f54cfcb5ddde253d88fc9993f3e33e9a.png)
Чтобы задача останавливалась корректно, её нужно декорировать в task.py
:
@app.task(bind=True, base=AbortableTask)
Cоздаём нового бота:
![](https://habrastorage.org/getpro/habr/upload_files/9a1/f37/f86/9a1f37f86b4f52164b087fdb230802e1.png)
И запускаем задачу в консоли Сelery:
![](https://habrastorage.org/getpro/habr/upload_files/3d7/6e2/f38/3d76e2f384036b68c9bdf4cb0ef35935.png)
![](https://habrastorage.org/getpro/habr/upload_files/e14/caa/909/e14caa90932d7b54ecce5e46e5df497d.png)
Готово! Бот работает:
![](https://habrastorage.org/getpro/habr/upload_files/54e/7e9/723/54e7e9723dd2a707fac567b1141b3a82.png)
Репозиторий проекта.
В пандемию, когда контакты с людьми сводятся к минимуму, боты незаменимы — и если вам интересна их разработка, то вы можете обратить внимание на наш курс о Fullstack-разработке на Python, а если хочется пойти дальше и создавать программы с ИИ, вы можете присмотреться к нашему курсу «Machine Learning и Deep Learning», который полностью подготовит вас к началу карьеры в области ИИ с нуля или прокачает ваши навыки. Также вы можете узнать, как начать карьеру или продолжить развитие в других направлениях:
![](https://habrastorage.org/getpro/habr/upload_files/182/a7d/d43/182a7dd43a5ab1be9fcd3197da76cee8.png)
Python, веб-разработка
Data Science и Machine Learning
Мобильная разработка
Java и C#
От основ — в глубину
А также: