Создаем радио-бота для рабочих каналов в Slack +6


Летом 2021 года в Slack появились новые голосовые легковесные чаты — huddles. Мне они очень приглянулись в каждодневной рутине, и достаточно быстро появилась идея использовать их для улучшения жизни на работе.

Дальше я расскажу о процессе создания бота и подробнее остановлюсь на моменте работы с виртуальными звуковыми устройствами для Ubuntu, потому что это заняло больше времени, чем ожидалось. Для людей практики весь опыт завернут в репозиторий, чтение опционально :)

В чем суть

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

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

В целом, сделать свой интернет-поток сейчас нетрудно, но кто будет заходить в него и целенаправленно слушать, вот честно? Huddle, мне показалось, идеально для этого подходит — все уже есть на канале, не нужно отвлекаться от рабочих инструментов. Просто заходишь в болталку, еще и можешь с кем-то сразу обсудить то, что услышал. "Выглядит достаточно круто, чтобы попробовать", — решил я.

Реализация идеи легко разбивается на задачи.

  1. Понять, как работать с huddle

  2. Сделать аудио-поток

  3. Завернуть этот поток в huddle

  4. Проверить, что все работает, и наслаждаться восторженными аплодисментами в свою честь

  5. Бесконечно докручивать фичи: текущая песня, добавление юзерами песен в плейлист, лайки и т.п.

Slack API

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

Попробовал быстро накидать скрипт на знакомой связке python + selenium, но клик по кнопке входа работал очень нестабильно. В итоге перешел на puppeteer, где все нормально заработало, и реализовал логику входа-выхода.

Сделал маленькую виртуалку на ubuntu 20.04, установил туда pulseaudio, google-chrome, node js и npm, подробнее опять же в репозитории.

Аудиопоток

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

Я решил использовать liquidsoap для генерации постоянного потока. Через пакетный менеджер установилась не самая свежая версия 1.4.1, но для наших целей хватит.

Пришло время разбираться со звуковыми устройствами и конкретно с PulseAudio.

PulseAudio. Настраиваем вход

По мере гугления обнаруживается много запросов на тему того, как примешать звук с приложения к микрофону и завернуть в тот же zoom, но мне они не помогли. После очередной вкладки stack overflow пришло осознание, что обычная работа программиста тут заканчивается и требуется реальное исследование. Поэтому здесь я остановлюсь подробнее и расскажу детали простым языком, но на объективность не претендую. Да, речь пойдет про семейство linux и безGUIвые разборки.

Есть ALSA. Это максимально приближенный к железу звуковой карты софт. А есть PulseAudio — это абстракция над драйверами, этакий микшер, с которым нам и стоит работать. Далее в словаре терминов встречаем Sinks и Sources, что везде переводят как выходы (колонки) и входы (микрофон). Окей, значит, мне нужно, вероятно, как-то сделать source, завести в него сигнал и использовать по умолчанию.

Для управления есть утилиты pactl и pacmd. Разница среди них есть, но можно пользоваться и тем, и тем. Например, убедимся, что у нас нет никаких входов

pactl list short sources

pacmd list-sources

Далее. У каждого выхода (sink) есть сущность monitor. Это как гнездо для подключения наушников у колонки, то есть вроде входа для чего-то еще. И кажется, это можно использовать как дефолтный микрофон! Разработчики PulseAudio предусмотрели как раз модуль для этого.

Пробуем:

pacmd load-module module-null-sink sink_name=Virtual_Sink sink_properties=device.description=Virtual_Sink

pacmd set-default-source Virtual_Sink.monitor

И говорю liquidsoft стримить плейлист на это устройство через конфиг:

output.pulseaudio(device="Virtual_Sink", radio)

Как проверить? Попробуем сделать запись звука в системе командой arecord -f cd test.wav (-f отвечает за качество записи). В моем случае записался кусок трека, который был в плейлисте, а значит, успех!

Запускаем бота npm start, ждем подключения, и … тишина.

Pulseaudio. Настраиваем выход

Варианта два. Либо звук всё-таки теряется где-то на подходе к браузеру, либо сам браузер почему-то не хочет его использовать. Не буду описывать процесс исследования запуска Chrome через puppeteer, важно обнаружение флага –use-file-for-fake-audio-capture и рядом --use-fake-ui-for-media-stream. С их помощью запускаю бота с записанным ранее test.wav и слышу наконец знакомый трек в Slack! Значит, Chrome сам по себе может передать звук, придется идти в настройки. Дописываю бота, делаю скриншоты.

Так и есть, не найден ни один микрофон. Параллельно с проверкой Chrome я выполнял команды проверки списка входов выходов и заметил кое-что.

Команда pacmd list-sources выводит стабильно 1 выход Virtual_Sink.monitor. А что за команда pacmd list-source-outputs? Что это за выходы входов? Как я в итоге понял, Virtual_Sink хоть и может быть источником за счет monitor, но создан как sink и находится только как устройство выхода. А нам нужно устройство ввода, которое создается модулем module-virtual-source. При этом оно должно забирать звук с нашего Virtual_Sink.monitor явно, потому что попытка создать простой source в надежде, что default-source в него попадет, у меня не сработала.

По непонятной мне причине описания модуля module-virtual-source нет на офф странице, через консоль аргументы создания я тоже не смог добыть. К счастью, гугл четвертой ссылкой привел меня к исходникам, где я нашел нужный мне параметр с именем master.

pacmd load-module module-virtual-source master=Virtual_Sink.monitor source_name=MicOut source_properties=device.description=MicOut format=s16le

pactl set-default-source MicOut

Проверка и итоги

В итоге бот заработал как надо, но то, что я услышал… Ровно так же звучит музыка, которую мы слышим в телефоне при ожидании абонента, или просто поднесенный к микрофону плеер. При этом запись с этого выхода в самой системе получается нормальной. Я проверил форматы потока при передаче между девайсами, там везде 16 бит 44100, этого должно быть достаточно. Звук в приложении явно хуже. Вопрос в техподдержку расставил все по своим местам.

Кроме того, в huddle невозможно говорить нескольким людям одновременно, так что идея с обсуждением на фоне музыки тоже оказалась сейчас нереализуемой. Остается лишь надежда, что Slack решит доработать huddles для качественного звука, и тогда этот проект может возродиться.




К сожалению, не доступен сервер mySQL