Как создать голосового помощника на Python - Академия Selectel

Как создать голосового помощника на Python

В этой инструкции расскажем, как с помощью простых инструментов и библиотек разработать голосового помощника на Python, сделав его полезным и удобным в повседневной жизни.

Что будет уметь голосовой ассистент

Мы научим ассистента:

  • распознавать и синтезировать голос даже без доступа к интернету,
  • приветствовать в зависимости от времени суток,
  • искать страницы в Wikipedia,
  • выполнять поиск в Google и Яндекс,
  • открывать видео в YouTube и RuTube,
  • переводить фразы с русского на английский и наоборот,
  • открывать необходимые сайты,
  • сообщать прогноз погоды в разных городах,
  • подсказывать время,
  • запускать приложения,
  • воспроизводить музыку,
  • отправлять email,
  • проверять баланс в панели управления my.selectel,
  • работать в мультиязычном формате.

Эти функции — далеко не все, чему можно обучить ассистента. Даже с базовыми навыками программирования, а главное — большим желанием, можно создать такого помощника, который посоревнуется с Алисой или Siri.

Подготовка среды

Для написания данной статьи использованы ОС Windows 10, IDE PyCharm 2024.1  и следующие библиотеки. 

  • pyttsx3 — библиотека для синтеза речи (TTS) на различных платформах. Позволяет настроить голос, скорость речи и другие параметры.
  • datetime — встроенный модуль для работы с датой и временем.
  • speech_recognition библиотека для распознавания речи. Поддерживает различные API для онлайн и офлайн-распознавания, такие как Google Web Speech API, Wit.ai, Microsoft Bing Voice Recognition и другие.
  • vosk — библиотека для офлайн-распознавания речи. Использует модели, созданные с использованием Kaldi.
  • wave — встроенный модуль для работы с аудиофайлами формата .wav.
  • os — встроенный модуль для взаимодействия с операционной системой.
  • json встроенный модуль для работы с форматом данных JSON.
  • wikipediaapi — библиотека для работы с API Википедии.
  • webbrowser — встроенный модуль для открытия веб-страниц в браузере.
  • traceback — встроенный модуль для форматирования и вывода информации об исключениях и ошибках.
  • googletrans библиотека для перевода текста с помощью API Google Translate.
  • pyowm — библиотека для работы с API OpenWeatherMap, предоставляет доступ к текущим данным о погоде, прогнозам и историческим данным.
  • subprocess — встроенный модуль для запуска новых процессов и взаимодействия с ними.
  • random — встроенный модуль для генерации случайных чисел.
  • pathlib — встроенный модуль для работы с путями файловой системы.
  • smtplib — встроенный модуль для отправки электронной почты через SMTP.
  • email.mime.multipart и email.mime.text — модули для создания и отправки сложных сообщений электронной почты, включая текст, HTML и вложения.
  • requests — библиотека для отправки HTTP-запросов.

Если необходимый для работы модуль не является встроенным, его необходимо установить.

Установка выполняется с помощью команды pip install [module].

Первый код и настройка ассистента

Как создать голосового помощника на Python

Для начала создадим голосового ассистента с помощью класса bot и его объекта. Назовем нашего бота Тирексом (Trex).


    class bot:
    bot_name_ru = ""
    bot_name_en = ""
if __name__ == "__main__":
    trex = bot()
    trex.bot_name_ru = "Тирекс"
    trex.bot_name_en = "Trex"

Выбор голоса 

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

Набор голосов зависит от операционной системы, на которой живет ассистент. Чтобы узнать, какие из них доступны в вашей ОС, необходимо добавить библиотеку pyttsx3 и выполнить следующий код. Он воспроизводит голоса и выводит в консоль их названия.


    import pyttsx3

    def speak(my_bot, text):
    print(my_bot.bot_name_ru, " говорит: ", text)
    engine.say(text)
    engine.runAndWait()

if __name__ == "__main__":
    trex = bot()
    trex.bot_name_ru = "Тирекс"
    trex.bot_name_en = "Trex"
   
    engine = pyttsx3.init()
    voices = engine.getProperty('voices')
    # Прослушивание голосов, доступных в ОС
    i = 0
    for voice in voices:
        engine.setProperty('voice', voices[i].id)
        print('Имя: %s' % voice.name)
        print('ID: ', i)
        speak(trex, "1")
        print('--------------------')
        i = i + 1

В нашем случае доступно всего два голоса:


    Имя: Microsoft Irina Desktop - Russian
ID:  0
--------------------
Имя: Microsoft Zira Desktop - English (United States)
ID:  1

Выберите любой понравившийся голос, доступный на вашей ОС, и запомните его ID. После этого цикл с прослушиванием можно удалить, а на его место вставить следующий код. Он наделяет ассистента постоянным голосом:


    engine.setProperty('voice', voices[your_id].id)

Приветствие

Теперь сделаем так, чтобы при запуске программы ассистент приветствовал нас в соответствии с текущим временем суток.

Для этого воспользуемся библиотекой datetime, позволяющей работать со временем.


    import datetime

Напишем функцию, с помощью которой ассистент будет приветствовать нас.


    def greetings():
    hour = int(datetime.datetime.now().hour)
    if hour >= 0 and hour < 6:
        speak(trex, f"Доброй ночи! Я - {trex.bot_name_ru}, твой голосовой помощник. Чем могу помочь?")
    elif hour >= 6 and hour < 12:
        speak(trex, f"Доброе утро! Я - {trex.bot_name_ru}, твой голосовой помощник. Чем могу помочь?")
    elif hour >= 12 and hour < 18:
        speak(trex, f"Добрый день! Я - {trex.bot_name_ru}, твой голосовой помощник. Чем могу помочь?")
    else:
        speak(trex, f"Добрый вечер! Я - {trex.bot_name_ru}, твой голосовой помощник. Чем могу помочь?")

Для вызова функции необходимо указать ее название.


    greetings()

Обработка голоса

Чтобы ассистент умел распознавать голос онлайн и офлайн, потребуются дополнительные библиотеки:

  • speech_recognition,
  • vosk,
  • wave,
  • os,
  • json.

    import speech_recognition as sr
from vosk import Model, KaldiRecognizer
import waveimport osimport json

Общая логика работы:

  1. Мы произносим в микрофон команду.
  2. Аудио сохраняется во временный файл.
  3. Запускается функция online-распознавания речи.
  4. При отсутствии доступа к интернету запускается функция offline-распознавания речи.
  5. Распознанная речь сохраняется в переменную в виде текста.

Запись речи

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


    def record():
    query = ""
    with microphone:
        recognizer.adjust_for_ambient_noise(microphone, duration=1)
        try:
            print(f"{trex.bot_name_ru} слушает...")
            rec_audio = recognizer.listen(microphone, 5, 5)
            with open("recorded_speech.wav", "wb") as file:
                file.write(rec_audio.get_wav_data())
            query = online_recognition()
        except sr.WaitTimeoutError:
            speak(trex, "Извините, я вас не понял. Повторите, пожалуйста, запрос.")
    return query

recorded_speech.wav сохраняется в папке текущего проекта. 

Конструкция with упрощает управление ресурсами, так как забирает эту обязанность на себя. Ресурсы, занимаемые процессом, будут автоматически возвращены после окончания with.

Онлайн-распознавание речи

Для online-распознавания мы будем использовать библиотеку speech_recognition.

С помощью функции WavFile считываем уже записанный аудиофайл и даем команду на его распознавание.


    def online_recognition():
    query = ""
    try:
        rec_audio = sr.WavFile("recorded_speech.wav")
        with rec_audio as audio:
            content = recognizer.record(audio)
        print(f"{trex.bot_name_ru} распознает речь...")
        query = recognizer.recognize_google(content, language="ru-RU").lower()
    except sr.UnknownValueError:
        pass
    except sr.RequestError: # Если интернет недоступен — переключение на offline-распознавание
        print(f"{trex.bot_name_ru} не смог подключиться к Интернету. Попытка offline-распознавания...")
        query = offline_recognition()
    return query

При наличии подключения к интернету ассистент распознает речь и переводит ее в текст, сохраняя результат в переменную query.

В противном случае запускается функция offline-распознавания.

Офлайн-распознавание речи

Если интернет отсутствует, ассистент будет работать с библиотекой Vosk. Для этого нужно загрузить из репозитория необходимую модель и поместить ее в поддиректорию проекта /models. Полный путь до этой директории будет примерно таким: C:/PyCharmProjects/voice_assistant/models.

Мы используем модуль vosk-model-ru-0.42, его имя указывается в переменной vosk_model.Если модуль с таким названием не будет найден в директории /models, ассистент сообщит об этом и попросит загрузить его.


    def offline_recognition():
    vosk_model = "vosk-model-ru-0.42"
    query = ""
    try:
        if not os.path.exists(f"models/{vosk_model}"):
            print(f"ВНИМАНИЕ: Для offline-распознавания необходимо загрузить языковую модель \"{vosk_model}\" с сайта https://alphacephei.com/vosk/models и распаковать в директорию \"models\" проекта.")
            exit(1)
        audio_file = wave.open("recorded_speech.wav", "rb")
        model = Model(f"models/{vosk_model}")
        offline_recognizer = KaldiRecognizer(model, audio_file.getframerate())
        audio = audio_file.readframes(audio_file.getnframes())
        if len(audio) > 0:
            if offline_recognizer.AcceptWaveform(audio):
                query = offline_recognizer.Result()
                query = json.loads(query)
                query = query["text"]
    except:
        speak(trex, "Извините, я не смог распознать речь. Пожалуйста, повторите запрос.")
    return query

В функциях выше мы использовали такие объекты, как recognizer и microphone. Не забудьте объявить их.


    recognizer = sr.Recognizer()
microphone = sr.Microphone()

А для запуска процесса записи и распознавания речи требуется вызвать функцию record().


    query = record()
print(f"Вы сказали: {query}")

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


    os.remove("recorded_speech.wav")

Теперь ассистент умеет слушать и понимать нашу речь. Далее научим его выполнять наши просьбы.

Функции голосового ассистента

Подготовка логики

Ассистент должен уметь выполнять не только один вид запроса. Для этого нужно проработать логику, которая позволяет вызывать функции в соответствии с нашим запросом.

Один из вариантов — использовать блок if-else. А для обеспечения непрерывной работы ассистента мы поместим эту конструкцию в цикл while. Таким образом, помощник будет выполнять команды до тех пор, пока мы не завершим работу программы.


    while True:
  query = record()
  os.remove("recorded_speech.wav")
  print(f"Вы сказали: {query}")
  query = query.lower()
     
# Вызов функций в соответствии с распознанным запросом
if ("word") in query:
  function()
elseif("word2") in query:
  function2()

Поиск по Википедии

Для поиска по Википедии используется библиотека wikipediaapi. Это API Википедии, позволяющий искать статьи по определению. Также для взаимодействия с браузером потребуется библиотека webbrowser. А с обработкой ошибок нам поможет traceback.


    import wikipediaapi
import webbrowser
import traceback

Логика работы:

  1. Поиск страницы по запросу.
  2. Открытие найденной страницы в браузере.
  3. Чтение первых двух предложений.

    def search_wiki():
    speak(trex, "Произвожу поиск по Википедии.")
    wiki = wikipediaapi.Wikipedia(f"{trex.bot_name_en} ({trex.bot_name_en}@selectel.ru)", "ru")
    query = query.replace("википедия ", "")
    query = query.replace("википедии ", "")
    query = query.replace("wikipedia ", "")
    wiki_page = wiki.page(query)
    try:
        if wiki_page.exists():
            speak(f"Вот что мне удалось найти в Википедии по запросу \"{query}\".")
            webbrowser.get().open(wiki_page.fullURI)
            result = wiki_page.summary(query, sentences=2)
            speak(trex, result)
        else:
            speak(trex, f"К сожалению, я не смог ничего найти в Википедии по запросу \"{query}\"")
    except:
        speak(trex, "Произошла ошибка работы модуля \"Поиск по Википедии\". Подробности ошибки выведены в терминал.")
        traceback.print_exc()
        return

Таким образом, цикл с вариантами запросов будет выглядеть так:


    while True:
    query = record()
    os.remove("recorded_speech.wav")
    if (query == ""):
        print(f"{trex.bot_name_ru} не распознал речь. Возможно, вы ничего не сказали.")
        print("--------------------")
        continue
    print(f"Вы сказали: {query}.")
    query = query.lower()
    # Вызов функций в соответствии с распознанным запросом
    if ("википедия" in query) or ("википедии" in query) or ("wikipedia" in query):
        search_wiki(query)
    print("--------------------")

Для вызова функции, выполняющей поиск по Википедии, в голосовом запросе должно прозвучать слово «Википедия» или «Википедии». Например, «Википедия кошка».

Поиск в Google и Яндекс

Теперь научим нашего ассистента гуглить и «яндексить». Дополнительных библиотек нам не потребуется, так как задача довольно простая.

  1. Запоминаем фразу.
  2. Удаляем лишнее из запроса.
  3. Открываем браузер и вставляем подготовленную ссылку.

Google


    def search_google(query):
  speak(trex, "Произвожу поиск в Google")
  query = query.replace("гугл ", "")
  query = query.replace("гугле ", "")
  query = query.replace("google ", "")
  try:
      URI = "https://google.com/search?q=" + query
      webbrowser.open(URI)
      speak(trex, f"Вот что мне удалось найти в Google по запросу \"{query}\"")
  except:
      speak(trex, "Произошла ошибка работы модуля \"Поиск в Google\". Подробности ошибки выведены в терминал")
      traceback.print_exc()
      return

Яндекс


    def search_yandex(query):
  speak(trex, "Произвожу поиск в Яндексе")
  query = query.replace("яндекс ", "")
  query = query.replace("yandex ", "")
  try:
      URI = "https://ya.ru/search?text=" + query
      webbrowser.open(URI)
      speak(trex, f"Вот что мне удалось найти в Яндексе по запросу \"{query}\"")
  except:
      speak(trex, "Произошла ошибка работы модуля \"Поиск в Яндексе\". Подробности ошибки выведены в терминал")
      traceback.print_exc()
      return

Запросы такого типа можно производить в любой поисковой системе. Главное — знать правильный URI-адрес. Например:

  • Google: https://google.com/search?q=
  • Яндекс: https://ya.ru/search?text=
  • Yahoo: https://search.yahoo.com/search?p=

Вызов функций поиска в Google и Яндекс выглядит так:


    elif ("гугл" in query) or ("google" in query):
  search_google(query)
elif ("яндекс" in query) or ("yandex" in query):
  search_yandex(query)

Чтобы функции сработали, в голосовом запросе должно присутствовать любое из этих слов: гугл, google, яндекс или yandex. Например, можно сказать: «Яндекс ЦОД Selectel».

Поиск по Youtube и Rutube

Поиск по видеохостингам ничем не отличается от способа, представленного для поиска по Google и Яндекс, — необходимо знать лишь URI-адрес.

Для этого перейдем на интересующий видеохостинг, выполним какой-либо поиск и скопируем URI-адрес от начала до места, где начинается запрос.

  • YouTube: https://www.youtube.com/results?search_query=
  • RuTube: https://rutube.ru/search/?query=

YouTube


    def search_youtube(query):
  speak(trex, "Произвожу поиск на YouTube")
  query = query.replace("ютуб ", "")
  query = query.replace("youtube ", "")
  try:
      URI = "https://www.youtube.com/results?search_query=" + query
      webbrowser.open(URI)
      speak(trex, f"Вот что мне удалось найти на YouTube по запросу \"{query}\"")
  except:
      speak(trex, "Произошла ошибка работы модуля \"Поиск на YouTube\". Подробности ошибки выведены в терминал")
      traceback.print_exc()
      return

RuTube


    def search_rutube(query):
    speak(trex, "Произвожу поиск на RuTube")
    query = query.replace("рутуб ", "")
    query = query.replace("rutube ", "")
    try:
        URI = "https://rutube.ru/search/?query=" + query
        webbrowser.open(URI)
        speak(trex, f"Вот что мне удалось найти на RuTube по запросу \"{query}\"")
    except:
        speak(trex, "Произошла ошибка работы модуля \"Поиск на RuTube\". Подробности ошибки выведены в терминал")
        traceback.print_exc()
        return

Для поиска по YouTube и RuTube используйте голосовой запрос: 

  • youtube
  • rutube

    elif ("ютуб" in query) or ("youtube" in query):
  search_youtube(query)
elif ("рутуб" in query) or ("rutube" in query):
  search_rutube(query)

Открыть любой сайт

Находить информацию в Википедии, гуглить и искать видео ассистент уже умеет. Теперь давайте научим его открывать любой сайт. Но для этого, конечно, нужно знать его доменное имя.

Например, чтобы открыть сайт selectel.ru, необходимо сказать: «Открой selectel.ru». При выполнении такой команды помощник уберет из запроса слово «открой», а перед selectel.ru добавит https://www.


    def search_website(query):
    query = query.replace("открой ", "")
    query = query.replace("open ", "")
    speak(trex, f"Произвожу поиск сайта \"{query}\"")
    try:
        URI = "https://www." + query
        webbrowser.open(URI)
        speak(trex, f"Открываю \"{query}\"")
    except:
        speak(trex, "Произошла ошибка работы модуля \"Открыть website\". Подробности ошибки выведены в терминал")
        traceback.print_exc()
        return

Для поиска сайта в интернете используйте запрос «открой [site]».


    elif ("открой" in query) or ("open" in query):
  search_website(query)

Переводчик

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

Чтобы перевести фразу с английского языка на русский, необходимо сказать «переведи с английского ». Для перевода с русского языка на английский требуется произнести «переведи на английский ».

Для перевода помощник использует Google-переводчик.


    from googletrans import Translator

    def translate(query):
    gt = Translator()
    if ("с английского" in query):
        lang_src = "en"
        lang_dest = "ru"
    elif ("на английский" in query):
        lang_src = "ru"
        lang_dest = "en"
    else:
        speak(trex, "Извините, поддерживается только перевод с английского на русский и наоборот")
        return
    query = query.replace("переведи с английского ", "")
    query = query.replace("переведи на английский ", "")
    try:
        translate_query = gt.translate(query, src=lang_src, dest=lang_dest)
        speak(trex, translate_query.text)
  except:
      speak(trex, "Произошла ошибка работы модуля \"Переводчик\". Подробности ошибки выведены в терминал")
      traceback.print_exc()
      return

Функция срабатывает по описанной выше логике:


    elif ("переведи" in query) or ("перевод" in query) or ("translate" in query):
  translate(query)

При появлении проблем с актуальной версией библиотеки googletrans установите более раннюю версию с помощью команды pip install googletrans==4.0.0-rc1

Прогноз погоды

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

Для работы с OWM добавим в проект библиотеку pyowm.


    from pyowm import OWM
from pyowm.utils.config import get_default_config

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

  • Перейдите на сайт OWM.
  • Зарегистрируйтесь.
  • Перейдите в раздел My API keys.
  • Введите имя ключа (например, PythonAssistant) и нажмите Generate.

У функции следующая логика:

  • если в запросе встречается выражение «в городе», из запроса выбирается последнее слово — нужное нам названием города.
  • если в запросе данное выражение не встретилось, берется город, установленный в классе bot.

    class bot:
    bot_name_ru = ""
    bot_name_en = ""
    city = ""

    trex = bot()
trex.bot_name_ru = "Тирекс"
trex.bot_name_en = "Trex"
trex.city = "Москва"

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


    def get_weather(query):
    weather_api_key = "your_api_key"
    config_dict = get_default_config()
    config_dict['language'] = 'ru'
    try:
        open_weather_map = OWM(weather_api_key, config_dict)
        weather_manager = open_weather_map.weather_manager()
        if ("в городе" in query):
            city = query.split(" ")[-1:]
            city = str(city).strip("[]'")
        else:
            city = trex.city
        observation = weather_manager.weather_at_place(city)
        weather = observation.weather
    except:
        speak(trex, "Произошла ошибка работы модуля \"Погода\". Подробности ошибки выведены в терминал")
        traceback.print_exc()
        return
    status = weather.detailed_status
    temperature = weather.temperature('celsius')["temp"]
    wind_speed = weather.wind()["speed"]
    pressure = int(weather.pressure["press"] / 1.333)  # Из гПА в мм рт.ст.
    speak(trex, f"В городе {city} {status}")
    speak(trex, f"Температура {str(temperature)} градусов по Цельсию")
    speak(trex, f"Скорость ветра составляет {str(wind_speed)} метров в секунду")
    speak(trex, f"Давление составляет {str(pressure)} миллиметров ртутного столба")

Сама функция вызывается по слову «погода».


    elif ("погода" in query) or ("weather" in query):
  weather(query)

Например, чтобы узнать погоду в Санкт-Петербурге, необходимо сказать: «Погода в городе Санкт-Петербурге».

Узнать время

Для реализации данной функции воспользуемся уже знакомой библиотекой datetime. С ее помощью обратимся к своему устройству и получим текущее время.


    def time():
    time_time = datetime.datetime.now().strftime("%H:%M:%S")
    speak(trex, f"Текущее время: {time_time}")

Чтобы ассистент вызвал функцию, следует сказать слово «время».


    elif ("время" in query) or ("time" in query):
  time()

Запустить приложение

Хотите, чтобы ассистент запускал за вас приложения? Он может и такое. Нужно лишь  знать полный путь до исполняемого файла или то, какой командой запускается приложение из командной строки (CMD – command-line interface). Подойдет любой из этих методов.

Добавьте в проект дополнительную библиотеку.


    import subprocess

Для примера мы взяли три приложения:

  • Microsoft Edge — запуск .exe с указанием абсолютного пути;
  • Калькулятор — запуск с использованием команды CMD start;
  • Google Chrome — аналогично калькулятору.

Чтобы открыть, например, Google Chrome, необходимо сказать: «Запусти браузер». 


    def start_app(query):
    query = query.replace("запусти ", "")
    print(query)
    if ("edge" in query):
        subprocess.run("C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe")
    elif ("калькулятор" in query):
        subprocess.run(["start calc"])
    elif ("браузер" in query):
        subprocess.run(["start chrome"])

Не забудьте добавить новое условие:


    elif ("запуск" in query) or ("запусти" in query) or ("start" in query):
  start_app(query)

Воспроизвести музыку

Еще одна интересная функция — возможность воспроизведения музыки.

Предварительно создайте директорию и поместите в нее любимые музыкальные композиции. В переменной music_dir_name как раз и задается путь до музыкального хранилища.

Также понадобятся дополнительные библиотеки.


    import random
from pathlib import Path

    def play_music():
    music_dir_name = "C:\\MyMusic"
    music_dir = Path(music_dir_name)
    music = os.listdir(music_dir)
    print(f"В директории \"{music_dir}\" найдены следующие композиции: \n", music)
    music_count = len(list(music_dir.iterdir()))
    print(f"В папке {music_dir} есть {music_count} объектов")
    os.startfile(os.path.join(music_dir, music[random.randint(0, music_count-1)]))

В нашей конфигурации музыка начинает воспроизводиться в случайном порядке. Чтобы воспроизведение происходило с начала, следует заменить music[random.randint(0, music_count-1)] на music[0].

Вызов функции выглядит так:


    elif ("музыка" in query) or ("музыку" in query) or ("music" in query):
  play_music()

Отправка email

Добавим еще одну фичу — пусть помощник отправляет электронные письма. Для примера мы будем использовать почту mail.ru.

Стоит отметить, что mail.ru и другие сервисы запрещают сторонним приложениям отправку писем с почтовых адресов, используя стандартный пароль. Для создания «пароля внешних приложений» в mail.ru необходимо перейти в настройки своего аккаунта, а именно: НастройкиВсе настройкиБезопасностьПароля для внешних приложений.

Для такой функциональности нам потребуются следующие библиотеки:


    import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText

Логика работы функции.

  1. Подключение к SMTP-серверу.
  2. Аутентификация с паролем внешних приложений.
  3. Выбор получателя письма. В нашей реализации выбор получателя происходит только из заранее определенного списка. 
  4. Указание заголовка письма.
  5. Указание текста письма.
  6. Отправка.

    def send_email():
    try:
        server = smtplib.SMTP_SSL('smtp.mail.ru', 465)
        server.ehlo()
        server.login('email_from@mail.ru', 'password')
        msg = MIMEMultipart()
        msg["From"] = "email_from@mail.ru"
        speak(trex, "Скажите получателя письма")
        query = record()
        if ("руководитель" in query):
            msg["To"] = "email_to1@mail.ru"
        elif ("мама" in query):
            msg["To"] = "email_to2@gmail.com"
        print(f"Получатель письма: {msg["To"]}")
        speak(trex, "Скажите заголовок письма")
        msg["Subject"] = record()
        print(f"Заголовок письма: {msg["Subject"]}")
        speak(trex, "Скажите текст письма")
        text = record()
        print(f"Текст письма: {text}")
        msg.attach(MIMEText(text, "plain"))
        server.sendmail(msg["From"], msg["To"], msg.as_string())
        speak(trex, f"Письмо для {msg["To"]} успешно отправлено")
        server.close()
    except:
    speak(trex, "Произошла ошибка работы модуля \"Отправка электронного письма\". Подробности ошибки выведены в терминал")
    traceback.print_exc()
    return

Для запуска функции отправки письма следует сказать «письмо».

Получение информации о балансе Selectel

Если у вас уже есть личный кабинет Selectel, вы можете узнать баланс аккаунта и многое другое, даже не заходя в него! Потребуется лишь API-ключ, создать который можно в разделе Профили и настройкаКлючи API.

Мы будем использовать API: https://api.selectel.ru/v3/balances

Подробнее об API Selectel.

В переменной apikeys_selectel хранится API-ключ, созданный в личном кабинете, он передается в headers.

В случае успешного подключения ассистент сообщит нам баланс аккаунта:

  • общий, если подключен Единый баланс.
  • раздельный, если Единый баланс не подключен (основной, облачная платформа, хранилище и CDN, облако VMware).

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


    import requests

    def billing_selectel():
    apikey_selectel = "your_api_key"
    headers = {'X-Token': apikey_selectel}
    response = requests.get('https://api.selectel.ru/v3/balances', headers=headers)
    if response.status_code == 200:
        balance_info = response.json()
        # Проходим по всем типам биллинга и выводим их сумму
        for billing in balance_info['data']['billings']:
            billing_type = billing['billing_type']
            balances_values_sum = billing['balances_values_sum']
            billing_type = str(billing_type).replace("primary", "Основной")
            billing_type = str(billing_type).replace("vpc", "Облачная платформа")
            billing_type = str(billing_type).replace("storage", "Хранилище и CDN")
            billing_type = str(billing_type).replace("vmware", "Облако на базе VMware")
            speak(trex, f"Баланс для типа '{billing_type}': {balances_values_sum/100} рублей")
    else:
        speak(trex, "Не удалось получить информацию о балансе.")

Узнать баланс аккаунта Selectel можно с помощью фразы «Баланс Selectel».


    elif ("баланс selectel" in query):
  billing_selectel()

Мультиязычность

Мультиязычность — еще одна интересная функциональность ассистента. Мы покажем лишь одну из реализаций. Возможности для расширения этой функции у пользователя  безграничны.

Логика работы:

  • при запуске программы устанавливается язык ассистента по умолчанию;
  • при выполнении функции speak() сопоставляется установленный язык ассистента по умолчанию и фраза, которую помощник должен сказать;
  • если в словаре есть фраза на нужном языке, ассистент озвучивает ее. В противном случае помощник сообщает об отсутствии перевода.

Для реализации потребуются дополнительный класс и функция.


    class bot_translate:
    with open("translations.json", "r", encoding="UTF-8") as file:
        translations = json.load(file)

    def get(self, text: str):
        if text in self.translations:
            return self.translations[trex.language]
        else:
            print(f"Для фразы: {text} нет перевода.")
            return text

Не забудьте создать объект класса.


     translator = bot_translate()

В класс самого ассистента добавьте новую переменную и ее инициализацию.


    class bot:
    bot_name_ru = ""
    bot_name_en = ""
    city = ""
    language = ""

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


    if trex.language == "ru":
  engine.setProperty('voice', voices[0].id)
elif trex.language == "en":
  engine.setProperty('voice', voices[1].id)

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


    {
  "Извините, я вас не понял. Повторите, пожалуйста, запрос": {
    "ru": "Извините, я вас не понял. Повторите, пожалуйста, запрос",
    "en": "I'm sorry, I didn't understand you. Could you repeat the request, please?"
  }
}

Теперь, чтобы автоматический перевод заработал, необходимо немного видоизменить вызов функции, добавив обращение к функции get нового класса.


    speak(trex, translator.get("Извините, я вас не понял. Повторите, пожалуйста, запрос"))

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

Полный код голосового помощника — на GitHub.

Заключение

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

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

Для дальнейшего развития можно рассмотреть добавление новых опций. Например, подключить больше языков, расширить функциональность ассистента и улучшить интеграции с различными API и сервисами. Кроме того, голосового помощника можно разместить на облачном сервере Selectel, добавив в него поддержку устройств с различными ОС. 

Тем не менее, уже на этом этапе голосовой ассистент показывает, как современные технологии могут сделать нашу повседневную жизнь проще и удобнее.