Xoxo, everyone — it’s Reznik, and today we’re going to recognize voice messages…
Let’s step away from affiliate networks for a bit and build a bot to help out with tech support. Its job is to recognize the content of voice messages and video messages in Telegram group chats. We’ll use Python as our language of choice. We’ll recognize speech using the free, open-source vosk library, and add punctuation using a model from Silero. The same mechanism is what powers our own AlterCPA Talk Bot.
A bot like this has one crucial advantage: speech recognition happens locally and doesn’t need to touch the Yandex or Google API. It’s completely secure and perfect for paranoids and classified information alike. Even a junior developer or DevOps engineer without deep specialized knowledge can get this bot running.
Step 1. Setting Up the Bot in Telegram
First, we need to create a bot and get its token so we can work with it.
- Open Telegram and find @BotFather
- Send the bot the
/newbotcommand to create a new bot - Give our bot a name and its future address
- The bot is created — copy the token you get and jot it down somewhere
- Send the bot the
/mybotscommand to show the list of bots - Pick the bot you just created from the list
- Click Bot Settings
- Click Allow groups and make sure it says Groups are currently enabled for bot
The bot is now ready and cleared to work in group chats. That way, it’ll be able to automatically recognize messages in groups.
Step 2. Setting Up the Server
As you’ve probably guessed, we’re going to need a virtual server. My bots run on the Scarlett and Danny plans from Timeweb. The OS of choice is plain Debian 10. Pretty much any OS will do, in theory, but step 5 might look different on a different one. Installing the software will take up about 1.5 GB of space, and the language models will need another 2-2.5 GB on top of that.
Before we start, make sure to update the system:
apt update && apt upgrade -y
Install Python and the other software we’ll need:
apt install -y python3 python3-dev python3-pip unzip ffmpeg
Install the modules we need: torch, vosk, PyTelegramBotAPI, and their dependencies:
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
Keep in mind that some of these modules are pretty hefty — the install might take a while.
Step 3. Downloading the Speech Recognition Models
The recognition models will be stored locally. Let’s put them in the /home/ml folder. They’ll need about 3 GB of free space. You can find the available languages and recognition models on the vosk site, in the Models section. As of this writing, the latest model is vosk-model-ru-0.22, and that’s the one we’ll be using.
The punctuation model comes from Silero’s GitHub repository, where we’ll need the file models.yml. In it, find te_models and its package under latest. That’s the link we’re after — mine led right here.
It’s best to run these commands one at a time — downloading the models can take a ton of time.
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
The models are downloaded and ready to go. Keep in mind that by the time you’re reading this, the links might have changed. Use the latest versions of the files.
Step 4. Writing the Bot
You can download the finished bot file, voxy.py, from our repository on GitLab. Just toss it onto your server and enjoy the process — that process is covered in the next step. In this one, let’s break down its code piece by piece.
At the top of the file, we specify the version of Python we’re working with. Yours is probably a lot newer — check it with which python3.
#!/usr/bin/python3 # coding: utf-8
Import all the modules we downloaded in step two.
import telebot import pathlib import requests import subprocess import os import json import wave import torch from vosk import Model, KaldiRecognizer
Specify our bot’s token from step one.
TOKEN = '12345:AAAA-BBBBBB_CCC'
Specify the paths to the models and the language we’re using. If you’ve been following the instructions exactly, these paths won’t change.
MODEL = r"/home/ml/vosk-model-ru-0.22" TEMODEL = "/home/ml/v2_4lang_q.pt" LANG = 'ru'
Set up the bot and the models.
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" )
We’re going to intercept every message of type “voice” and “video message”.
@bot.message_handler(content_types=["voice","video_note"]) def voice_decoder(message):
What follows is the code for the nested function — don’t forget about indentation. First, let’s check the message type.
if ( message.voice != None ):
file = message.voice
elif ( message.video_note != None ):
file = message.video_note
else:
return False
Download the file attached to the message.
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
Save the file right next to the bot. We already found the WORKDIR path during initialization.
downpath = WORKDIR + "/" + file.file_unique_id
with open( downpath, 'wb' ) as dest:
dest.write(contents.content)
Convert the file with a magic command we’ll show you below.
path = audioconvert( downpath )
if ( path == False ):
return False
Turn the file into text with a simple call to the recognition model. If it worked, run the text through the enhancement model.
text = speech2text( path )
os.remove( path )
if ( text == False or text == "" or text == " " ):
return False
else:
text = tmodel.enhance_text( text, LANG )
Send the message back as a reply — and that’s the end of the recognition work.
bot.reply_to(message, text)
We’ll use FFmpeg for audio conversion, because I honestly don’t know of any other options. We need a file at a 16k bitrate, PCM format, mono.
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
To extract the text through vosk, we’ll use some ready-made magic from its developers. I don’t have the faintest idea how it works or why it’s built this way. But it works.
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
Last step — start the bot up in active polling mode.
if __name__ == '__main__':
bot.infinity_polling()
Step 5. Running the Bot as a Service
Let’s place the bot in the /home/bot folder and run it from there.
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
Enter the bot’s token from step 1, save (Ctrl + O, Enter, Ctrl + X). Let’s try starting the bot up and see if it runs.
./voxy.py
You’ll see a few loading messages, and the bot will be ready within a few seconds. Send it a voice message and wait for a reply. In theory, nothing should go wrong at this step. If it does, go ahead and tear your hair out, throw a tantrum, and file your complaints into the void.
Let’s create a file that’ll be responsible for running our service. We’ll call the service voxybot.
nano /lib/systemd/system/voxybot.service
The file’s contents will look something like this.
[Unit] Description=VoxyBot [Service] Type=simple Restart=on-failure RestartSec=5s ExecStart=/home/bot/voxy.py [Install] WantedBy=multi-user.target
Reload the services, enable the bot, and start the service.
systemctl daemon-reload systemctl enable voxybot.service service voxybot start
Step 6. Testing It Out
Your bot is now running. Open a chat with it and send it a voice message. Then a video message. If there’s no reply, you know the drill: panic, denial, anger, bargaining, depression, and finally accepting the inevitable — contacting support, or learning Python. If you do get a reply — enjoy.
TL;DR: The Easy Install
Once you’ve got your bot’s token, log into your server and run the following commands:
wget https://gitlab.com/altervision/altercpa-voxy/-/raw/main/setup-ru.sh bash setup-ru.sh YOURBOTTOKEN
Replace YOURBOTTOKEN with the token you got from BotFather. The install might take a couple of hours — the script needs to download around 3.5 GB of files. Once it’s done, the bot will start working on its own.
That’s it from Reznik — xoxo, everyone!
