В данной статье мы рассмотрим применение речевых технологий, предоставленных компанией Яндекс в контексте распознавания аудиосообщений в Telegram – популярном мессенджере, объединяющем миллионы пользователей по всему миру.

Оглавление

  1. Регистрируемся и получаем API-ключ

  2. Получаем токен для чат-бота

  3. Пишем основу чат-бота

  4. Посылаем запросы в Яндекс.Облако

  5. Дорабатываем чат-бота и смотрим на результат

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

https://t.me/lifestyle_tracker_bot

Данный функционал нам великодушно и альтруистично (почти) предоставила компания Яндекс на их сервисе Яндекс.Облако (Yandex.Cloud). На первый взгляд, название может напоминать о неком облачном хранилище для бесконечных потоков фотографий с телефона, но на самом деле все куда интереснее:

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

На Яндекс.Облаке можно найти кучу различных программистских интересностей - распознавание/генерация аудио, машинный перевод, нейросети, базы данных и т.д. Полный список актуальных решений можно посмотреть здесь или посмотреть их список, взятый из википедии ниже:

https://ru.wikipedia.org/wiki/Yandex_Cloud

Да, каждый сервис платный, но их стоимость, в большинстве решений, не столь высока, да и Яндекс предоставляет тестовый период и грант для новых пользователей. Подробные условия их получения можно посмотреть здесь и здесь. Говоря кратко - если вы только зарегистрировались в Яндекс.Облаке, то нате 2 месяца бесплатного доступа, чтобы все затестить.

Театр начинается с вешалки

Переходим на Яндекс.Облако, авторизируемся и попадаем в консоль. После чего сразу создаем здесь платежный аккаунт. Если вы вошли сюда в первый раз, то получаем пробный период/грант или не заморачиваемся и пополняем счет на пару десятков рублей, привязав банковскую карту.

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

Для этого переходим на данную вкладку:

Далее в данном разделе жмем на кнопку "Создать сервисный аккаунт". Вводим название и выбираем роль. В данном примере мы будем использовать функционал по распознавания аудио - Speech-To-Text (STT), поэтому в качестве роли выберем "ai.speechkit-stt.user".

Жмем "Создать" и после недолгой прогрузки увидим новую строку в списке сервисных аккаунтов на этой же странице. Нажимаем на эту строчку и попадаем на новую страницу, где в правой верхней части экрана находим кнопку "Создать новый ключ", нажимаем и выбираем "API-ключ". Появится форма, в которой можно задать описание для данного ключа - нажимаем "Создать" и получаем свежий ключ:

Готово! Сохраняем себе данный ключ или не закрываем данную форму, чтобы потом его скопировать в программу.

Заключаем сделку с BotFather

Чтобы затестить функционал сервиса давайте соберем простейшего чат-бота для Telegram, который будет расшифровывать аудиосообщения и присылать нам текст.

Для начала пойдем к BotFather и, склонив колено, попросим токен для нового чат-бота:

  1. Запускаем бота BotFather

  2. Вызываем команду /newbot

  3. Вводим название для нового бота

  4. Вводим его username

  5. Получаем токен

Теперь перейдем к основной части представления и начнем писать код чат-бота.

Тык тык тык делаю по клавишам

Для создания чат-бота воспользуемся библиотекой telebot (или pyTelegramBotAPI), которую установим таким образом:

pip install pyTelegramBotAPI

Сперва создадим файл "config.py", где будем хранить все полученные ключи и экземпляр класса TeleBot (импортированный из библиотеки telebot), с помощью которого будут происходить все взаимодействия с чат-ботом. В конструктор данного класса передаем лишь только токен, который мы до этого получили и сохранили в переменную BOT_TOKEN.

from telebot import TeleBot

# Токен чат-бота
BOT_TOKEN = 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'
# Экземпляр класса TeleBot, через который будет происходить все взаимодействия с ботом
bot = TeleBot(BOT_TOKEN)

# API-ключ сервисного аккаунта из Yandex.Cloud
YC_STT_API_KEY = 'YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY'

Заменяем значения переменных BOT_TOKEN и YC_STT_API_KEY на свой токен и API-ключ соответственно. Далее создаем файл "main.py", где будет располагаться функционал самого чат-бота. В начале определим все импорты:

from config import bot
from telebot.types import Message

Здесь мы импортировали объект класса TeleBot из раннее созданного "config.py" и класс Message из подмодуля types библиотеки. Данный класс представляет собой сообщение, отправленное в чат и содержит всю исчерпывающую информацию о нем.

Первое, что должен уметь чат-бот - это реагировать на команду /start, которая как раз и вызывается при запуске бота:

# Используем декоратор из объекта класса TeleBot, 
# в который передаем параметр commands - список команд, 
# при вызове которых будет вызываться данная функция
@bot.message_handler(commands=['start']) 
# Определяем функцию для обработки команды /start, она принимает объект класса Message - сообщение
def start(message:Message):
    # Отправляем новое сообщение, указав ID чата с пользователем и сам текст сообщения
    bot.send_message(message.chat.id, "Йоу! Отправь мне аудиосообщение")

Здесь мы использовали декоратор из объекта bot, который даст нашей программе знать, что вот эту функцию start нужно вызывать только тогда, когда пользователь ввел команду /start.

Самая функция принимает лишь один аргумент - это экземпляр класса Message, то есть, в данном случае, это будет то самое сообщение с командой /start, которую юзер отправил сам или просто нажал "Запустить", войдя первый раз в нашего бота. В последнем случае она будет отправлена за него боту автоматически.

message_handler - это декоратор из библиотеки telebot, предназначенный для обработки входящих сообщений в чат-боте. Он позволяет задать функцию, которая будет вызываться автоматически, когда бот получает сообщение определенного типа или удовлетворяющее определенным условиям.

В самом конце файла размещаем следующий код, который запустит бот при запуске "main.py":

if __name__ == "__main__":
    bot.polling(non_stop=True)

Итак, теперь наш бот реагирует на его запуск пользователем и выпрашивает аудиосообщение:

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

Сперва определим функцию с другим декоратором, который будет реагировать уже не на команду, а тип сообщения - а именно голосовое:

@bot.message_handler(content_types=['voice'])
def handle_voice(message:Message):

Давайте импортируем еще один класс из библиотеки, который будет представлять аудиосообщение:

from telebot.types import Voice

Теперь внутри функции нам нужно получить отправленное/пересланное аудиосообщение. Так как оно уже хранится на серверах Telegram, то мы можем просто получить путь к нему.

# Определяем объект класса Voice, который находится внутри параметра message
# (он же объект класса Message)
voice:Voice = message.voice
# Получаем из него ID файла аудиосообщения
file_id = voice.file_id
# Получаем всю информацию о данном файле
voice_file = bot.get_file(file_id)
# А уже из нее достаем путь к файлу на сервере Телеграм в директории
# с файлами нашего бота
voice_path = voice_file.file_path

В данном случае переменная voice_path будет храниться в себе относительный путь к аудиофайлу, например: "voice/file_0.oga". То есть, есть сервер Telegram, а в нем директория со всеми файлами нашего бота - там есть папка voice где и лежит присланное аудиосообщение.

OGA - это расширение файла аудио, используемое на серверах Telegram. Этот формат, известный как Ogg Vorbis, обеспечивает хорошее качество звука и небольшие размеры файлов, что позволяет передавать голосовые сообщения в мессенджере с высокой четкостью и экономией трафика.

Однако, толку от такого пути для нас никакого. Давайте применим немного хитрости и получим абсолютный путь к сохраненному аудиосообщению. Для этого нам понадобится токен бота, который мы сохранили в "config.py":

from config import BOT_TOKEN

И относительный путь файла, хранящийся в переменной voice_path:

file_base_url = f"https://api.telegram.org/file/bot{BOT_TOKEN}/{voice_path}"

Таким образом мы получим абсолютный путь к аудиофайлу на сервере Telegram вида:

https://api.telegram.org/file/botXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/voice/file_0.oga

Теперь у нас есть ссылка на аудиофайл и мы можем отправить ее Яндекс.Облаку, который попытается получить из данного аудиофайла текст.

Путешествие аудиосообщения

Создаем еще один файл, где мы будем взаимодействовать с сервисом Yandex.Cloud. Назовем его, к примеру "yandex_cloud.py". Мы могли бы использовать какие-нибудь готовые библиотеки для данной задачи, но для такого простого функционала легче написать взаимодействие при помощи классического модуля requests. Импортируем его и API-ключ от Яндекс.Облака из конфига:

import requests
from config import YC_STT_API_KEY

Определим переменную с адресом, на который будет идти запрос:

# URL для отправки аудиофайла на распознавание
STT_URL = 'https://stt.api.cloud.yandex.net/speech/v1/stt:recognize'

И создаем функцию, которая будет принимать в качестве аргумента адрес аудиофайла на серверах Telegram и возвращать распознанный из него текст:

def get_text_from_speech(file_url):
    # Выполняем GET-запрос по ссылке на аудиофайл
    response = requests.get(file_url)

    # Если запрос к серверу Telegram не удался...
    if response.status_code != 200:
        return None

    # Получаем из ответа запроса наш аудиофайл
    audio_data = response.content
    
    # Создам заголовок с API-ключом для Яндекс.Облака, который пошлем в запросе
    headers = {
        'Authorization': f'Api-Key {YC_STT_API_KEY}'
    }
    
    # Отправляем POST-запрос на сервер Яндекс, который занимается расшифровкой аудио,
    # передав его URL, заголовок и сам файл аудиосообщения
    response = requests.post(STT_URL, headers=headers, data=audio_data)

    # Если запрос к Яндекс.Облаку не удался...
    if not response.ok:
        return None

    # Преобразуем JSON-ответ сервера в объект Python
    result = response.json()
    # Возвращаем текст аудиосообщения
    return result.get('result')

Осталось только доработать функцию handle_voice из "main.py"

Осталось совсем чуть-чуть

Вернемся к модулю main и импортируем созданную функцию:

from yandex_cloud import get_text_from_speech

Продолжим код функции handle_voice и добавим пару строчек:

# Сохраняем текст аудиосообщения в перменную
speech_text = get_text_from_speech(file_base_url)
# Посылаем его пользователю в виде нового собщения
bot.send_message(message.chat.id, speech_text)

Снова запускаем бота и, смотрим на результат:

Готово!

Полный код бота можно скачать на GitHub

Комментарии (4)


  1. MoonArsenii
    06.08.2023 20:26
    +1

    Что забавно, я делал подобную вещь примерно год назад. Делал на библиотеке SpeechDelete и отдельно ещё использовал первую попавшуюся нейронку для исправления орфография. SpeechDelete иногда делала большие буквы, когда не надо, не везде ставила запитые и т.д.


    1. Molot999 Автор
      06.08.2023 20:26

      Ахахах, прикол) Я сначала делал это через питоновскую библиотеку, вроде эту - работает круто, но не принимает формат аудиофайлов от телеги, нужно конвертировать в wav. Ок, сделал конвертацию через ffmpeg. Если запускать бота на винде то все ок, а вот на линуксовском хостинге уже ffmpeg начинает барагозить. По итогу подумал лучше платить Яндексу по паре десятков копеек и не мучиться.


      1. oleg-svs
        06.08.2023 20:26
        +1

        Так есть же speechkit от яндекса, пример с голосовыми и кругляшками из телеги: https://github.com/olegsvs/yepcock-size-bot/blob/main/stt.py

        Да и конвертация в wav работает на линуксе без проблем.


  1. svdik
    06.08.2023 20:26
    +1

    А мемы на сайте скоро будут?