![](https://habrastorage.org/webt/ex/rg/qc/exrgqcvsrvilgaxihlqtzyqsov4.jpeg)
Всем привет! Меня зовут Макарий, я DevOps-инженер в команде кросс-платформенной инфраструктуры корпоративного супераппа VK Teams. Сегодня я продолжу рассказ о том, как мы применяем практики ChatOps в наших рабочих процессах. Первую часть о самих практиках и мини-аппах для их реализации можно прочитать здесь.
Напомню, ChatOps — модель организации работы и коммуникации внутри команды через общение, то есть через мессенджер. Такой подход объединяет разработчиков, DevOps-инженеров, QA-специалистов, инженеров поддержки, продуктовых менеджеров, аналитиков и других участников процесса в единую коммуникационную платформу. Мы рассмотрим открытый кросс-платформенный фреймворк OpsDroid, его возможности, напишем коннектор между OpsDroid и VK Teams, а также реализуем бота. Уверен, этот опыт откроет для ваc новые возможности, которые предоставляет ChatOps.
ChatOps и корпоративные мессенджеры
В предыдущей части мы рассмотрели основные принципы построения ChatOps:
![](https://habrastorage.org/webt/0j/cy/lz/0jcylzlxwnvs6hiefqrhusqflf4.png)
Также мы поговорили о том, как работает автоматизация процессов с использованием корпоративного мессенджера:
![](https://habrastorage.org/webt/ja/wf/hh/jawfhhxkgfatj_avgjjo5ad6rzi.png)
Здесь нам на помощь приходит VK Teams — корпоративный суперапп от VK, который объединяет в себе мессенджер для общения с коллегами, ботов для автоматизации, свой таск-трекер, видеоконференции на 100 пользователей и платформу мини-приложений.
Успешная реализация ChatOps требует не только выбора подходящих инструментов, но и создания культуры, при которой команды готовы активно использовать и вкладываться в принципы ChatOps.
Open-Source-фреймворк OpsDroid
OpsDroid — это кросс-платформенный фреймворк для разработки ChatOps-решений. Его основная идея заключается в создании единого центра управления ботом, который может работать одновременно на нескольких платформах, таких как Slack, MS Teams, Webex Teams, Facebook Messenger, Telegram, Matrix, Discord и других. Это позволяет командам использовать существующую инфраструктуру мессенджеров для управления операциями и взаимодействия с OpsDroid.
Взаимодействие между OpsDroid и платформой мессенджера осуществляется через коннекторы. Они представляют собой абстракцию над API и протоколами, используемыми различными мессенджерами и сервисами уведомлений. Коннекторы обеспечивают унифицированный интерфейс для взаимодействия с платформами и позволяют:
- принимать и отправлять сообщения,
- управлять каналами и пользователями,
- получать уведомления о событиях и реагировать.
Например, для связи OpsDroid и VK Teams потребуется соответствующий коннектор, который свяжет Bot API VK Teams с функциональностью OpsDroid.
![](https://habrastorage.org/webt/fr/_a/gl/fr_aglgaar96hh5ttdecs3mvymi.png)
OpsDroid также предоставляет удобный интерфейс для создания ботов: в виде навыков (skills) — то есть модулей, которые обрабатывают входящие события (events), анализируют их, взаимодействуют с внешними сервисами, возвращают результат для обработки.
Установить OpsDroid можно удобным для вас способом на личный компьютер или сервер. Для запуска бота необходимо настроить OpsDroid в соответствии с инструкциями:
Linux, MacOS — используйте Docker (руководство). Вы также можете установить OpsDroid без использования Docker, просто с помощью:
pip3 install opsdroid[common]
Руководство для Windows.
Коннектор OpsDroid-VK Teams
Для связи VK Teams и OpsDroid можно написать простой коннектор.
Вот его код
import aiohttp
import asyncio
from opsdroid.connector import Connector
from opsdroid.events import Message
class ConnectorVKTeams(Connector):
"""A connector for VK Teams"""
def __init__(self, config, opsdroid=None):
# Инициализация коннектора
super().__init__(config, opsdroid=opsdroid)
self.name = "vkteams"
self.opsdroid = opsdroid
self.latest_update = None
self.listening = True
self.default_target = self.default_user
self.session = None
self._closing = asyncio.Event()
self.loop = asyncio.get_event_loop()
self.base_url = config.get("base-url", None)
self.default_user = config.get("default-user", None)
self.whitelisted_users = config.get("whitelisted-users", None)
self.update_interval = config.get("update-interval", 1)
# без токена бот работать не сможет
self.token = config["token"]
async def _get_messages(self):
# Получение сообщений из ивентов. Ивенты получаем: events/get из Bot API
data = {
'token': self.token,
'pollTime': 30,
'lastEventId': 1
}
if self.latest_update is not None:
data["lastEventId"] = self.latest_update
await asyncio.sleep(self.update_interval)
resp = await self.session.get(self.build_url("events/get"), params=data)
if 200 == resp.status:
json = await resp.json()
await self._parse_message(json)
async def _parse_message(self, response):
# Отдаем сообщения, которые нашли в json-е ивента
for event in response["events"]:
user = self.get_user(event)
target = self.get_target(event)
parsed_event = await self.handle_messages(
user=user,
target=target,
event_id=event.get("eventId"),
raw_event=event
)
if parsed_event:
if self.handle_user_permission(event, user):
await self.opsdroid.parse(parsed_event)
else:
block_message = Message(
text="У вас нет доступа к взаимодействию с ботом",
user=user,
user_id=user,
target=target,
connector=self
)
await self.send(block_message)
self.latest_update = event["eventId"]
elif "eventId" in event:
# игнорируем
self.latest_update = event["eventId"]
Также в коннекторе необходимо реализовать методы для отправки сообщений, согласуя с Bot API.
Вот код реализации
import aiohttp
from opsdroid.connector import register_event
from opsdroid.events import Message, File
...
@register_event(Message)
async def send_message(self, message):
data = dict()
data["token"] = self.token
data["chatId"] = message.target
data["text"] = message.text
resp = await self.session.post(self.build_url("messages/sendText"), data=data)
@register_event(File)
async def send_file(self, file_event):
data = aiohttp.FormData()
data.add_field(
"token", str(self.token))
data.add_field(
"chatId", str(file_event.target), content_type="multipart/form-data")
data.add_field(
"file",
await file_event.get_file_bytes(),
content_type="multipart/form-data")
async with aiohttp.ClientSession() as session:
resp = await session.post(self.build_url("messages/sendFile"), data=data)
Подробнее пример коннектора смотрите в репозитории. Коннектор позволяет OpsDroid понимать, когда приходит новое сообщение в VK Teams, и получать его содержимое, а также самому отправлять сообщения. Коннектор можно дополнительно расширить. Например, добавить поддержку всех возможных ивентов от OpsDroid:
from opsdroid.events import JoinGroup
...
if raw_event.get("type") == "newChatMembers":
return JoinGroup(
user=f"@[{user}]",
user_id=user,
event_id=event_id,
target=target,
connector=self,
raw_event=raw_event,
)
Или реализовать поддержку всех типов событий, которые доступны на платформе (для VK Teams см. /events/get).
Код реализации
from opsdroid.events import Message
from . import vkt_events
...
if raw_event['payload']['parts'][0].get("type") == "forward":
return vkt_events.Forward(
user=f"@[{user}]",
user_id=user,
event_id=event_id,
target=target,
message=Message(
text=raw_event['payload']['text'],
user=f"@[{first_part['payload']['message']['from']['userId']}]",
user_id=first_part['payload']['message']['from']['userId'],
connector=self,
raw_event=first_part,
),
connector=self,
raw_event=raw_event
)
Расширение коннектора увеличивает возможности навыков, которыми будут обладать ваши боты. В нашем случае добавим поддержку следующих ивентов: Reply, Forward, File, Voice, Sticker, Mention для нового сообщения, а также Edited Message, Deleted Message, Pinned/Unpinned Message, Join/Left chat для других событий в чате.
Еще OpsDroid предоставляет широкие возможности для тестирования. Принято полностью покрывать новый коннектор тестами.
Пример бота с OpsDroid
Давайте реализуем такого же бота, который отправляет на указанный имейл сообщение, если на него был дан ответ с ключевым словом «send_email». Мы будем использовать OpsDroid для создания кросс-платформенного бота, который будет работать как в VK Teams, так и в Slack и, например, в Matrix. Код не претерпит значительных изменений, мы обернем его в OpsDroid. Действия, которые мы ранее выполняли с распаковкой payload вложенного сообщения, перенесены в код коннектора, разобранный выше, в парсинг ивента типа Reply. В данном случае нам останется лишь получить содержимое ивента, которое привязано к основному:
message.linked_event.text
Навык, который будет использоваться в нашем кросс-платформенном боте в файле configuration.yaml, выглядит следующим образом:
from opsdroid.skill import Skill
from opsdroid.matchers import match_regex
from opsdroid.constraints import constrain_connectors
class SendEmailSkill(Skill):
@match_regex(r'send_email', matching_condition="fullmatch")
async def send_email(self, message):
if not isinstance(message, Reply):
await message.respond("Сделайте реплай на сообщение, "
"которое хотите переслать на имейл")
return
message_for_email = f"{message.linked_event.user_id}:\n\n" \
f"{message.linked_event.text}\n\n"
send_email(to=["your.real.email@mail.ru"],
subject=f"Сообщение из {message.connector.name}(opsdroid)",
text=message_for_email)
await message.respond(f"Сообщение переслано на имейл")
@match_regex(r'send_email', matching_condition="fullmatch")
@constrain_connectors(['slack'])
async def send_email_slack(self, message):
slack = self.opsdroid.get_connector('slack')
thread_timestamp = message.raw_event.get('thread_ts', None)
replies = await slack.slack_web_client.conversations_replies(
channel=message.target, ts=thread_timestamp
)
messages = replies['messages']
thread_head = messages[0]
users_info = await slack.slack_web_client.users_info(user=thread_head['user'])
message_for_email += f"{users_info.get('user').get('real_name')}:" \
f"\n\n{thread_start['text']}\n\n"
send_email(to=["your.real.email@mail.ru"],
subject=f"Сообщение из Slack (opsdroid)",
text=remove_emoji(message_for_email))
await message.respond(f"Сообщение переслано на имейл")
Код этого навыка для Slack отличается, так как коннектор Slack не поддерживает тип ивента Reply. Здесь мы используем разбиение с помощью constrain_connectors, чтобы в случае, если бот принял ивент в Slack, он распарсил его одним способом, а если принял в VK Teams или в Matrix — то другим, более подходящим. Коды навыка для VK Teams и Matrix идентичны.
Подробнее пример навыка смотрите в репозитории. Для более глубокого погружения используйте полную документацию OpsDroid.
Затем нам необходимо организовать структуру проекта следующим образом:
.
├── Dockerfile*
├── configuration.yaml
└── skills
└── email_sender (имя кастомного скилла)
└── __init__.py
* Dockerfile — если вы запускаете OpsDroid с помощью docker.
Для этого выполним следующие команды:
cd /home/username/Projects
mkdir -p my_opsdroid_project/skills/email_sender
cd my_opsdroid_project
vim __init__.py <- сюда положим код навыка
Соберем нашего кросс-платформенного бота:
configuration.yaml
logging:
level: debug
timestamp: true
welcome-message: false
# Web server
web:
host: '0.0.0.0'
port: 8080
## Parsers
parsers: []
## Connectors modules
connectors:
vkteams:
token: "VKT_BOT_TOKEN" # <- токен, который получили у Метабота
bot-name: "email_sender_bot" # <- имя бота, которое задали Метаботу при создании
base-url: "api.my-company.myteam.mail.ru/bot/v1/" # <- base-url бот-апи
repo: https://github.com/mboriskin/connector-vkteams # <- код коннектора
# как настроить slack - https://docs.opsdroid.dev/en/stable/connectors/slack.html
slack:
bot-token: "SLACK_BOT_TOKEN"
bot-name: "email_sender_bot"
socket-mode: true
app-token: "SLACK_APP_TOKEN"
# как настроить matrix - https://docs.opsdroid.dev/en/stable/connectors/matrix.html
matrix:
mxid: "@bot_account_username:matrix.org"
password: "bot_account_password"
rooms:
'main': '#my-corporate-bots:matrix.org'
nick: "Botty EmailSender"
## Skill modules
skills:
## Hello (https://github.com/opsdroid/skill-hello)
hello: {} # <- как пример, навык по умолчанию
## Отправить сообщение на имейл
email_sender:
path: /opt/opsdroid/skills/email_sender # <- там лежит навык (если через docker)
no-cache: true
app-name: my_opsdroid_vkt
Соберем бота:
opsdroid config -f configuration.yaml build
Запустим в работу:
opsdroid start
Для запуска через Docker стяните скрипт startup.sh и Dockerfile из репозитория с примерами. Затем запустите скрипт для сборки бота:
IMAGE_NAME="my_opsdroid_vkt" ./startup.sh --build
Запустить скрипт можем как напрямую (для отладки и наблюдения за логами):
./startup.sh --run
Так и в фоне:
./startup.sh --runbg
И когда придет время, остановить бота:
./startup.sh --stop
Также можем удалить отработавший образ:
./startup.sh --remove
После запуска бота вы сможете написать ему личное сообщение или сообщение в групповом чате, куда он добавлен, и он выполнит нужную операцию, отправив сообщение на указанный имейл.
Например, в Slack:
![](https://habrastorage.org/webt/kz/vg/ep/kzvgepclez1asy7uicieq6f2p8s.png)
![](https://habrastorage.org/webt/fk/lt/lh/fkltlhppnfyg8ztcukm8msiznlu.png)
![](https://habrastorage.org/webt/6w/uq/lx/6wuqlxwwwgmluffbhsk5hg6uu3e.png)
В VK Teams:
![](https://habrastorage.org/webt/eo/zm/0z/eozm0zhcfnaqxznnw7i7iugbuie.png)
![](https://habrastorage.org/webt/fk/lt/lh/fkltlhppnfyg8ztcukm8msiznlu.png)
![](https://habrastorage.org/webt/r0/tq/gj/r0tqgjlwnm63iir6jh_2lbj7fsm.png)
И в Matrix:
![](https://habrastorage.org/webt/f1/xz/xf/f1xzxfpmrg3hx_5xbar6c2coade.png)
![](https://habrastorage.org/webt/fk/lt/lh/fkltlhppnfyg8ztcukm8msiznlu.png)
![](https://habrastorage.org/webt/v-/jc/v9/v-jcv9xizehrtwfn0lkvkuehcpq.png)
OpsDroid представляет собой мощный кросс-платформенный фреймворк для разработки ChatOps-решений. Благодаря способности работать одновременно на нескольких платформах, таких как Slack, MS Teams, VK Teams и других, OpsDroid обеспечивает командам гибкость и удобство при управлении операциями и взаимодействии с ботами. Концепция коннекторов упрощает интеграцию с различными сервисами, обеспечивая единый интерфейс для взаимодействия. Навыки в OpsDroid позволяют разработчикам создавать функциональность ботов, обрабатывать события и взаимодействовать с внешними сервисами. Благодаря простой установке и настройке OpsDroid является эффективным инструментом для разработки кросс-платформенных ботов, способных оперативно выполнять задачи и обеспечивать эффективное взаимодействие с пользователями.
Готовые решения OpsDroid можно шарить между мессенджерами.
Можно найти множество уже готовых решений для OpsDroid и затем интегрировать их для использования в VK Teams или другом сервисе.
Список готовых решений
- skill-ssh — навык для взаимодействия с Linux-серверами с помощью SSH-команд;
- skill-devops — навык для взаимодействия с Docker и Gitlab;
- skill-sysanalytics — мониторинг системы при запущенном экземпляре OpsDroid;
- skill-nginx-rtmp — мониторинг запуска, остановки nginx-rtmp и состояния в целом;
- skill-github-linker — предоставление ссылок на GitHub Issues и PR при упоминании;
- skill-jenkins — навык для взаимодействия с Jenkins;
- skill-repohook — предоставление ссылок на события в Git{Hub|Lab};
- skill-docker-daemon — навык для управления Docker;
- skill-k8s — навык для работы с Kubernetes;
- skill-shell — навык для запуска shell-скриптов;
- skill-yourextip — получение своего внешнего IP-адреса в чате;
- skill taginfo — навык для получения данных из OpenStreetMap;
- skill-awx — навык для взаимодействия с AWX;
- skill-grafana-annotation — навык для создания и просмотра аннотаций Grafana;
- skill-prometheus-scrape — навык для сбора метрик через Prometheus прямо из чата;
- skill-github — навык для взаимодействия с GitHub;
- skill-cloudhealth — навык для взаимодействия с CloudHealth;
- skill-reminders — для установки напоминаний;
- skill-minecraft — навык для отслеживания и публикации логов сервера Minecraft;
- skill-google-it — для ответа ссылкой на поиск в Google;
- skill-iss — навык для определения местоположения МКС;
- skill-formula1-schedule — обработка календаря «Формулы-1» и отправка напоминаний;
- skill-random — навык для примера: отображение случайных событий;
- skill-words — навык, использующий модуль NLTK для игры в слова;
- skill-word-of-the-day — прислать слово дня из Оксфордского словаря английского языка.
Заключение
ChatOps представляет собой модель организации работы и коммуникации внутри команды через мессенджеры, объединяя различные роли и функции в единую коммуникационную платформу.
VK Teams как корпоративный суперапп предоставляет широкий набор инструментов для общения, автоматизации, управления задачами и видеоконференций. Успешная реализация ChatOps требует создания соответствующей культуры в команде, готовности использовать инструменты и принципы ChatOps.
OpsDroid является кросс-платформенным фреймворком для разработки ChatOps-решений. Он позволяет создать единую централизованную систему управления ботом, который может работать на различных платформах, таких как Slack, MS Teams, VK Teams, Matrix и других. Взаимодействие с мессенджерами осуществляется через коннекторы, которые предоставляют унифицированный интерфейс для обмена сообщениями, управления пользователями и получения уведомлений о событиях.
В качестве примера использования OpsDroid был рассмотрен простой бот, способный отправлять сообщения на указанный имейл при получении определенного ключевого слова. Благодаря гибкости OpsDroid и его возможности работать на различных платформах такой бот может быть использован как в VK Teams, так и в других мессенджерах, например Slack или Matrix.
Мы отметили возможность использования готовых решений OpsDroid, которые могут быть адаптированы и интегрированы для использования в VK Teams или других мессенджерах. Это позволяет разработчикам использовать уже существующие возможности и навыки для управления операциями и взаимодействия с ботами.
В целом благодаря OpsDroid и интеграции с VK Teams команды могут создавать эффективные и гибкие ChatOps-решения, улучшая коммуникацию, автоматизируя рутинные задачи.