Безопасное распознавание речи в Telegram

Безопасное распознавание речи в Telegram

Всем чмоки, с вами Резник и сегодня мы распознаем голосовые сообщения …

Давайте отвлечёмся ненадолго от партнёрских сетей и сотворим бота, который будет помогать нам в технической поддержке. Его задача — распознавать содержимое голосовых и видео-сообщений в общих чатах Telegram. В качестве языка разработки возьмём Python. Мы будем распознавать речь с помощью бесплатной открытой библиотеки vosk и расставлять знаки препинания с помощью модели от Silero. Тот же механизм реализован в нашем AlterCPA Talk Bot.

Такой бот имеет важнейшее преимущество: распознавание речи происходит локально и не требует работы с API Яндекса или Google. Он целиком безопасен и подходит для параноиков и секретной информации. Этого бота сможет запустить начинающий разработчик или DevOps без глубоких профильных знаний.

Шаг 1. Подготовка бота в Telegram

Первым делом нам необходимо завести бота и получить его токен для работы.

  1. Заходим в Telegram и находим там @BotFather
  2. Отправляем боту команду /newbot для создания нового бота
  3. Указываем название и будущий адрес нашего бота
  4. Бот создан, копируем полученный токен и записываем на листочек
  5. Отправляем боту команду /mybots для показа списка ботов
  6. Выбираем только что созданного бота в списке
  7. Нажимаем Bot Settings
  8. Нажимаем Allow groups и убеждаемся, что Groups are currently enabled for bot

Бот готов и допущен к работе с групповыми чатами. Так он сможет автоматически распознавать сообщения в группах.

Шаг 2. Подготовка сервера

Как вы уже догадались, нам потребуется виртуальный сервер. Мои боты крутятся на тарифе Scarlett и Danny от Timeweb. В качестве операционной системы используется чистый Debian 10. По сути, подойдёт любая ОС, но шаг 5 на других ОС может отличаться от указанного тут. Для установки софта потребуется около 1.5 ГБ места, языковые модели потребуют ещё около 2-2.5 ГБ.

Перед началом работы обязательно обновляем систему:

apt update && apt upgrade -y

Устанавливаем Python и другой необходимый софт:

apt install -y python3 python3-dev python3-pip unzip ffmpeg

Устанавливаем необходимые для работы модули: torch, vosk, PyTelegramBotAPI и их зависимости:

pip3 install PyTelegramBotAPI
pip3 install --no-cache-dir vosk
pip3 install --no-cache-dir wave
pip3 install --no-cache-dir numpy
pip3 install --no-cache-dir torch
pip3 cache purge

Обратите внимание, что некоторые модули — довольно увесистые, установка может затянуться.

Шаг 3. Скачивание моделей для распознавания речи

Модели для распознавания будут храниться локально. Сложим их в папку /home/ml. Им потребуется около 3 ГБ свободного пространства. Доступные языки и модели распознавания лежат на сайте vosk в разделе Models. На момент написания самая свежая модель: vosk-model-ru-0.22, её мы и будем использовать.

Модель расстановки знаков препинания добывается из Github-репозитория компании Silero,  в котором нам пригодится файл models.yml. В нём находим te_models и его package в latest. Это и есть искомая ссылка, моя вела вот сюда.

Лучше всего выполнять команды по одной — загрузка моделей может занять кучу времени.

mkdir /home/ml
cd /home/ml
wget https://alphacephei.com/vosk/models/vosk-model-ru-0.22.zip
unzip vosk-model-ru-0.22.zip
rm -f vosk-model-ru-0.22.zip
wget https://models.silero.ai/te_models/v2_4lang_q.pt

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

Шаг 4. Написание бота

Готовый файл бота voxy.py вы можете скачать в нашем репозитории на Gitlab. Просто закиньте его на сервер и наслаждайтесь процессом. Процесс описан в следующем шаге. А в этом разберём его код по пунктам.

В начале файла указываем версию Питона, с которой будем работать. Возможно, ваша будет гораздо свежее, проверьте её через which python3.

#!/usr/bin/python3
# coding: utf-8

Подключаем все модули, скачанные на втором шаге.

import telebot
import pathlib
import requests
import subprocess
import os
import json
import wave
import torch
from vosk import Model, KaldiRecognizer

Указываем токен нашего бота из первого шага.

TOKEN = '12345:AAAA-BBBBBB_CCC'

Указываем пути к моделям и используемый язык. Если вы действовали строго по инструкции, пути не поменяются.

MODEL = r"/home/ml/vosk-model-ru-0.22"
TEMODEL = "/home/ml/v2_4lang_q.pt"
LANG = 'ru'

Подготавливаем бота и модели.

WORKDIR = str(pathlib.Path(__file__).parent.absolute())
bot = telebot.TeleBot( TOKEN )
model = Model( MODEL )
voska = KaldiRecognizer( model, 16000 )
tmodel = torch.package.PackageImporter( TEMODEL ).load_pickle( "te_model", "model" )

Мы будем перехватывать все сообщения с типом «голос» и «видео-сообщение».

@bot.message_handler(content_types=["voice","video_note"])
def voice_decoder(message):

Далее пойдёт код вложенной функции, не забывайте об отступах. Первым делом проверим тип сообщения.

    if ( message.voice != None ):
        file =  message.voice
    elif ( message.video_note != None ):
        file =  message.video_note
    else:
        return False

Скачаем файл, прикреплённый к сообщению.

    finfo = bot.get_file(file.file_id)
    try:
        contents = requests.get( 'https://api.telegram.org/file/bot{0}/{1}'.format(TOKEN, finfo.file_path) )
    except Exception:
        return False

Сохраним файл прямо рядом с ботом. Путь WORKDIR мы отыскали при инициализации.

    downpath = WORKDIR + "/" + file.file_unique_id
    with open( downpath, 'wb' ) as dest:
        dest.write(contents.content)

Конвертируем файл волшебной командой, которую покажем ниже.

    path = audioconvert( downpath )
    if ( path == False ):
        return False

Преобразуем файл в текст простым вызовом модели распознавания. Если всё получилось — прогоняем текст по модели улучшения.

    text = speech2text( path )
    os.remove( path )
    if ( text == False or text == "" or text == " " ):
        return False
    else:
        text = tmodel.enhance_text( text, LANG )

Отсылаем сообщение в качестве ответа, на этом вся работа распознавания заканчивается.

    bot.reply_to(message, text)

Для конвертирования аудио будем использовать FFmpeg, потому что других вариантов я и не знаю. Нам потребуется файл с битрейтом в 16к, формат PCM, моно.

def audioconvert(path):
    out_path = path + ".wav"
    command = [
        r'/usr/bin/ffmpeg',
        '-i', path,
        '-acodec', 'pcm_s16le',
        '-ac', '1',
        '-ar', '16000',
        out_path
    ]
    result = subprocess.run(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL )
    os.remove( path )
    if ( result.returncode ):
        os.remove( out_path )
        return False
    else:
        return out_path

Для извлечения текста через vosk, воспользуемся готовой магией от разработчиков. Я не имею ни малейшего представления о том, как это работает и почему это именно так. Но оно работает.

def speech2text(path):
    wf = wave.open(path, "rb")
    result = ''
    last_n = False
    while True:
        data = wf.readframes(16000)
        if len(data) == 0:
            break
        if voska.AcceptWaveform(data):
            res = json.loads(voska.Result())
            if res['text'] != '':
                result += f" {res['text']}"
                last_n = False
            elif not last_n:
                result += 'n'
                last_n = True
    res = json.loads(voska.FinalResult())
    result += f" {res['text']}"
    return result

Последний шаг — запускаем бота в режиме активной работы.

if __name__ == '__main__':
    bot.infinity_polling()

Шаг 5. Запуск бота как сервиса

Разместим бота в папке /home/bot и будем там его запускать.

mkdir /home/bot
cd /home/bot
wget https://gitlab.com/altervision/altercpa-voxy/-/raw/main/voxy.py
chmod a+x voxy.py
nano voxy.py

Указываем токен бота из шага 1, сохраняем (Ctrl + O, Enter, Ctrl + X). Пробуем включить бота и проверить, запустится ли.

./voxy.py

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

Создадим файл, который будет отвечать за работу нашего сервиса. Сервис назовём voxybot.

nano /lib/systemd/system/voxybot.service

Содержимое файла будет примерно таким.

[Unit]
Description=VoxyBot

[Service]
Type=simple
Restart=on-failure
RestartSec=5s
ExecStart=/home/bot/voxy.py

[Install]
WantedBy=multi-user.target

Обновляем службы, включаем бота и запускаем сервис.

systemctl daemon-reload
systemctl enable voxybot.service
service voxybot start

Шаг 6. Проверка работы

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

TL;DR: простая установка

После получения токена бота зайдите на сервер и выполните следующие команды:

wget https://gitlab.com/altervision/altercpa-voxy/-/raw/main/setup-ru.sh
bash setup-ru.sh YOURBOTTOKEN

Где вместо YOURBOTTOKEN укажите токен, полученный от BotFather. Установка может затянуться на пару часов — скрипту необходимо скачать около 3.5 ГБ файлов. После установки бот заработает сам.

А с вами был Резник, чмоки!