Режим разработчика

Александр Пиндык

Senior Software Engineer в Wargaming.net

Я расскажу о создании Discord-бота на Python, который запускает сборку проекта в Unity Cloud Build и создаёт ссылку на скачивание для QA из внешней команды.

Настройка и поддержка полноценного CI занимает много времени и средств. При этом команде чаще всего нужна базовая функциональность. Так случилось и с нашим проектом. Потребовался простой способ сборки проекта в Unity Cloud Build и передачи его на внешний QA.

На начальном этапе были такие условия и ограничения:

  • репозиторий проекта — Bitbucket (есть поддержка GitHub и GitLab);
  • наличие лицензии Unity Teams Advanced, которая разблокирует доступ к Unity Cloud Build;
  • ограниченный доступ к Unity Cloud Build для части команды;
  • сборка версии проекта не после каждого коммита в ветку, а только после завершения работы над функциональностью или багом.

Для этих целей был создан Discord-бот на Python (этот язык выбрал, как самый быстрый и удобный для автоматизации). Он позволил команде запускать сборку версии в Unity Cloud Build, и после её успешного завершения формировал ссылку для скачивания QA, как внутри команды, так и вне её.

Боты для Discord на Python: проект для начинающих

Перед тем, как начать писать бот, необходимо подготовить окружение. Для этого настроим Unity Cloud Build и Discord-гильдию (сервер) на сборку проекта под разные платформы. После этого настроим отправку сообщений об успешном завершении сборки из Unity Cloud Build в канал. И создадим бота, работающего с Unity Cloud Build и Discord.

Настройка проекта в Unity Cloud Build

Переходим в Unity Dashboard: https://dashboard.unity3d.com/landing и выбираем Cloud Build:

Далее выбираем Create project и вводим имя нашего проекта (для примера, назовем его Dungeon Crawl Prototype):

После этого в списке проектов появится созданный проект. Переходим к нему и выбираем SET UP CLOUD BUILD:

На следующей странице выбираем с каким Git репозиторием будем интегрировать наш проект. В нашем случае это GitHub:

Подключаем GitHub и переходим к настройке сборки проекта:

Добавляем новую сборку SETUP NEW TARGET:

Выбираем платформу. В нашем примере было создано две сборки под Windows 64 и Android:

Вводим название Target Label, в Branch ветку в git репозитории. Для тестового примера был установлен флаг Auto Detect Version:

Для примера был выбран Debug ключ и тестовый Bundle ID:

Далее нажимаем NEXT: BUILD, после чего настройка сборки завершена. Те же шаги необходимо повторить и для других платформ. На этом заканчиваем настройку Unity Cloud Build. В будущем понадобится создать Discord-оповещения для старта построения сборки и результата сборки. Сделаем это после настройки сервера.

Создание и настройка Discord-гильдии (сервера)

Переходим по ссылке к Discord https://discord.com/, скачиваем приложение и создаем аккаунт. После того как приложение установлено и произведена авторизация в аккаунт, необходимо создать новый Discord-сервер (далее будем называть их гильдиями). Создаем новую гильдию нажав на  «‎+». Выбираем пункт Create My Own → For me and my friends и вводим название гильдии без пробелов.

После создания сервера необходимо добавить новые роли. Для этого переходим в настройки и создаем три:

  • bot для нашего бота, ci для пользователей:
  • ci для пользователей, которые смогут давать команды боту:
  • qa для тех, кому будет доступна ссылка на скачивания сборки.

Далее создадим три канала:

  • ci для отправки команд боту;
  • build для сообщений от Unity Cloud Build;
  • testing отправки ссылок QA.

На этом настройка гильдии закончена. Следующим шагом будет создание Discord-бота.

Создание и настройка Discord-бота

Боты для Discord создаются на Discord Developer Portal: https://discord.com/developers/applications. Необходимо перейти по ссылке и авторизоваться. Затем создаем новое приложение, нажав на New Application. Вводим имя приложения и нажимаем Create:

Далее создаем бота. Для этого переходим в созданное приложение, выбираем вкладку Bot и нажимаем Add Bot:

Далее необходимо настроить разрешения для бота. Переходим в пункт OAuth2 и выбираем следующие опции:

После выбора всех опций копируем url кнопкой Copy. Затем в браузере переходим по скопированной ссылке, выбираем Discord-гильдию, созданную ранее, и проверяем, выставленные разрешения:

Далее в настройках Discord-гильдии добавляем нашего бота в роль bot, которую создали ранее:

Осталось настроить Discord-оповещения для Unity Cloud Build (о начале и завершении сборки, и её результате):

Настройка Discord оповещения для Unity Cloud Build

Возвращаемся в Unity Cloud Build и переходим в раздел Notifications, в котором выбираем Integrations page:

Далее создаем новую интеграцию, нажав на NEW INTEGRATION:

Выбираем Discord и нажимаем NEXT:

В появившемся окне выбираем события, при которых будут отправляться сообщения в гильдию. Затем нажимаем NEXT:

В появившемся окне авторизации выбираем нашу гильдию и канал, куда приходить сообщения. Нажимаем Authorize:

На этом настройка оповещений и окружения завершена. На следующем этапе создадим Discord-бот на Python.

Создание Discord-бота на Python

Настройка проекта

Для создания бота будем использовать Python 3.9, а для работы с Discord — библиотеку discord.py: https://pypi.org/project/discord.py/. Также понадобится библиотека python-dotenv для получения переменных окружения из .env файла.

Middle и Senior Python developer в проект Clutch

, ,

tproger.ruВакансии на tproger.ru

Создаём новый проект и добавляем в него requirements.txt с таким содержанием:

aiohttp==3.7.3 python-dotenv==0.15.0 discord.py==1.6.0

Так же создаём файл .env с переменными:

DISCORD_TOKEN= DISCORD_GUILD= DISCORD_BOT_NAME= UNITY_API_KEY= UNITY_ORGANIZATION_ID= PROJECT_ID=PROJECT_NAME=

DISCORD_GUILD — название Discord гильдии.

DISCORD_BOT_NAME — имя бота.

DISCORD_TOKEN — можно получить на Discord Developer Portal в разделе Bot, скопировав токен:

UNITY_API_KEY можно получить в настройках Unity Cloud Build:

UNITY_ORGANIZATION_ID и PROJECT_ID так же можно получить в Unity Cloud Build. Для этого открываем конфигурацию сборки и нажимаем на EDIT BASIC INFO:

Далее в строке браузера можно увидеть UNITY_ORGANIZATION_ID и PROJECT_ID:

PROJECT_NAME — имя проекта, который собирается в Unity Cloud Build:

Написание бота

Далее я опишу основные моменты реализации Discord-бота. Создаём класс DiscordBot который наследуется от discord.Client. Он реализует API, позволяющее подключиться к Discord гильдии:

import discord   class DiscordBot(discord.Client):     def __init__(self):         super(DiscordBot, self).__init__()

Далее необходимо подключиться к Discord-гильдии:

await self.start('discord bot token', bot=True)

Метод on_ready, позволяет реагировать на событие подключения бота к Discord-гильдии:

async def on_ready(self):     for guild in self.guilds:         if guild.name == 'self guild name here':             print('Connected to the Discord guild')             return     print('Fails connect to the Discord guild')     await self.close()

Для обработки сообщений необходимо реализовать метод on_message:

async def on_message(self, message: discord.Message):     try:         # Do not process self messages or messages not from bot supported channels         message_channel = message.channel.name         if message.author == self.user or message_channel not in ['ci', 'build']:             return          if message_channel == 'build':             await self._process_build_event(message)         else:             # React only for messages that mention bot             for mention in message.mentions:                 if self.user == mention:                     await self._process_bot_command(message)                     return     except Exception as e:         print(f'Exception: {e}')

В первой проверке отсекаем все сообщения от самого бота, а также сообщения, пришедшие не из каналов, поддерживаемых ботом. Затем разделяем сообщения на два типа: команды боту и сообщения от Unity Cloud Build. В зависимости от результата передаём сообщение нужному обработчику.

Обработчик сообщений для выполнения команд ботом. Метод _get_command_for_bot читает сообщение, ищет в нем команду и параметры команды. После этого выполняется метод конкретной команды:

async def _process_bot_command(self, message: discord.Message):     command, params = self._get_command_for_bot(message)     if command == 'build':         await self._start_build(message, params)     elif command == 'build_target_info':         await self._build_target_info(message, params)     else:         print(f'Unsupported command for bot: {command}')

Для работы с Unity Cloud Build создаём класс UnityCloudBuildWorker:

from aiohttp import ClientSession   class UnityCloudBuildWorker(object):     def __init__(self, base_url: str, project_id: str, api_key: str):         self._base_url = base_url         self._project_id = project_id         self._cloud_build_targets = {             'qa_windows': 'qa-windows-64-bit',             'qa_android': 'qa-android'         }         self._supported_builds = list(self._cloud_build_targets.keys())         self._session = None         self._base_header = {             'Content-Type': 'application/json',             'Authorization': f'Basic {api_key}',         }      async def start_worker(self):         assert self._session is None         self._session = ClientSession()      async def stop_worker(self):         if self._session is not None:             await self._session.close()             self._session = None

Разберём пример отправки команды на сборку проекта. Для этого в канал «ci» боту отправляется команда @AleksPinGames_DEV_Bot build qa_windows. Она обозначает, что необходимо начать сборку QA Windows.

Обработчик в DiscordBot получает сообщение и набор параметров и отправляет в чат ответ о начале выполнения задачи. Далее в UnityCloudBuildWorker отправляется запрос на сборку проекта через Unity Cloud Build. После получения ответа бот сообщает о том, какая сборка начала строиться и какая ветка использовалась в Git репозитории:

async def _start_build(self, message: discord.Message, params: str):     await message.channel.send('Send start build command to Unity Cloud Build')     result = await self._unity_cloud_build_worker.cmd_start_build(params)     await message.channel.send(result)

UnityCloudBuildWorker получает команду и выполняет метод cmd_start_build. Затем формируется url запроса и выполняется запрос. Полное описание API для работы с Unity Cloud Build можно найти тут: https://build-api.cloud.unity3d.com/docs/1.0.0/index.html#intro:

async def cmd_start_build(self, build_target: str) -> str:     build_target_id = self._cloud_build_targets.get(build_target, None)     if build_target_id is None:         return f'Unsupported build target: {build_target}'      url = f'{self._base_url}/projects/{self._project_id}/buildtargets/{build_target_id}/builds'     headers = {'clean': 'true'}     response = await self._send(self._send_post, url, headers)     result = await response.json() if response is not None else {}     self._logger.info(f'{self._log_tag} cmd_start_build result: {result}')     if result and len(result) > 0:         result_msg = f"Start building {result[0]['buildTargetName']} | Branch: {result[0]['scmBranch']}"     else:         result_msg = f'Error starting build for target: {build_target}'     return result_msg  async def _send(self, send_method: callable, url: str, headers: dict = None):     try:         if headers is not None:             headers.update(self._base_header)         else:             headers = self._base_header         result = await send_method(url, headers)         return result     except Exception as e:         print(f'Exception: {e}')         return None  async def _send_get(self, url: str, headers: dict = None):     return await self._session.get(url=url, headers=headers, ssl=True)  async def _send_post(self, url: str, headers: dict = None):     return await self._session.post(url=url, headers=headers, ssl=True)

После сборки проекта Unity Cloud Build отправляет в канал build сообщение о результате сборки. На это сообщение реагирует обработчик сообщений от Unity Cloud Build.

Обработчик сообщений от Unity Cloud Build проверяет от кого пришло сообщение. Все сообщения от Unity с данными по сборке вычитываются и формируется ссылка для канала QA testing. Сообщение в канал QA попадёт только в случае build_success = True. Также в данном обработчике удаляется ссылка на скачивание собранного проекта непосредственно в Unity Cloud Build. Остаётся только внешняя ссылка, не требующая авторизации в Unity Team.

async def _process_build_event(self, message: discord.Message):         try:             if message.author.name != 'Unity' or len(message.embeds) <= 0:                 return              to_delete_idx = None             build_success = False             embed = message.embeds[0]             for idx, field in enumerate(embed.fields):                 field_name = field.name                 if field_name == 'Build success':                     build_success = True                 elif field_name == 'Download':                     to_delete_idx = idx              if build_success:                 if to_delete_idx is not None:                     embed.remove_field(to_delete_idx)                 channel = self._server_text_channels['testing']                 await channel.send(embed=embed)         except Exception as e:             print(f'Exception: {e}')

Запуск бота как сервис

Для удобства запуска бота на удаленном сервере мы добавили в репозиторий возможность собрать и запустить бота в качестве Docker-контейнера. Для этого были добавлены Dockerfile:

FROM python:3.9.2-buster  ENV APP_DIR /unity_cloud_build_discord_bot ENV PYTHONPATH $PYTHONPATH:$APP_DIR  WORKDIR $APP_DIR  COPY requirements.txt requirements.txt  RUN pip install -r requirements.txt RUN apt-get update && apt-get install -y --no-install-recommends locales locales-all  COPY . $APP_DIR  CMD python app/discord_bot.py

И Makefile, в котором описаны команды up и down для запуска и остановки контейнера с ботом:

TAG ?= latest CONTAINER_NAME ?= unity_cloud_build_discord_bot IMAGE_NAME ?= $(CONTAINER_NAME):$(TAG)  build:    docker build . -t $(IMAGE_NAME)  up: build    docker run -d     --name $(CONTAINER_NAME)     --env-file .env     $(IMAGE_NAME)  down:    docker stop $(CONTAINER_NAME)    docker rm $(CONTAINER_NAME)

Все инструкции и полная версия кода бота доступны в GitHub-репозитории: https://github.com/AleksandrPindyk/unity_cloud_build_discord_bot

Итог

На этом всё ? Мы запустили бот в качестве Docker-контейнера и настроили на Discord-гильдию, где идёт общение разработчиков и внешних QA. После нескольких дней тестирования мы пришли к выводу, что использовать бота удобнее, чем запускать сборку через веб-страницу Unity Cloud Build. В следующей версии добавим поддержку нескольких проектов и разворачивание WebGL-версии сразу на удаленном сервере в Docker-контейнере. Более детальную информацию о возможностях разработки Discord-ботов на Python, можно получить на сайте с официальной документацией: https://discord.com/developers/docs/intro.

Не смешно? А здесь смешно: @ithumor

Если вы собираетесь использовать Discord API, например, для создания бота, то режим разработчика Discord будет вам очень полезен.

Включение режима разработчика на рабочем столе

Включить режим разработчика очень просто. Откройте настройки Discord (рядом с вашим именем в левом нижнем углу шестеренка) и нажмите Внешний вид. Там вы найдете режим разработчика. Нажмите на переключатель, чтобы включить его.

Android

На Android вы можете включить режим разработчика, перейдя в настройки Discord ( рядом с вашим именем в левом нижнем углу). Там вы найдете режим разработчика. Нажмите на переключатель, чтобы включить его.

Использование

Режим разработчика добавляет простую опцию Copy ID в ваше контекстное меню. Щелкните правой кнопкой мыши или долгое нажатие на любом сервере, пользователе, сообщении или канале, чтобы получить его уникальный идентификатор.

Используемые источники:

  • https://tproger.ru/articles/discord-bot-na-python-dlja-avtomatizacii-raboty-s-unity-cloud-build-v-gamedev-komande/
  • https://discord.fandom.com/ru/wiki/режим_разработчика

Оцените статью
Рейтинг автора
5
Материал подготовил
Илья Коршунов
Наш эксперт
Написано статей
134
Добавить комментарий