You've already forked Irene-Voice-Assistant
mirror of
https://github.com/janvarev/Irene-Voice-Assistant.git
synced 2025-11-26 22:50:58 +02:00
v4.0
- поддержки работы с контекстом (см. справку) - демо-игра работы с контекстом Больше-меньше и Больше-меньше альтернативная (два стиля) - базовые реализации используют работы с контекстом - runva_webapi.py - теперь вызовы core._update_timers делаются через периодичпеские вызовы HTTP, и через таймер. Webapi стало значительно устойчивей - справка содержит информацию о контексте
This commit is contained in:
@@ -26,6 +26,8 @@
|
|||||||
**plugin_yandex_rasp.py** - расписание ближайших электричек через Яндекс.Расписания. Пример: "электричка, электрички".
|
**plugin_yandex_rasp.py** - расписание ближайших электричек через Яндекс.Расписания. Пример: "электричка, электрички".
|
||||||
Требует установки в конфиге бесплатного API-ключа для личных нужд (до 500 запросов в сутки) с https://yandex.ru/dev/rasp/raspapi/ , а также станций отправления и назначения
|
Требует установки в конфиге бесплатного API-ключа для личных нужд (до 500 запросов в сутки) с https://yandex.ru/dev/rasp/raspapi/ , а также станций отправления и назначения
|
||||||
|
|
||||||
|
**plugin_gamemoreless.py** - игра Больше-меньше. Команда "игра больше меньше". Является примером работы с контекстом в движке Ирины.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**plugin_tts_pyttsx.py** - (оффлайн) позволяет делать TTS (Text-To-Speech, озвучку текста) через pyttsx движок. Используется по умолчанию.
|
**plugin_tts_pyttsx.py** - (оффлайн) позволяет делать TTS (Text-To-Speech, озвучку текста) через pyttsx движок. Используется по умолчанию.
|
||||||
@@ -61,6 +63,9 @@
|
|||||||
|
|
||||||
**plugin_youtubesearch.py** - поиск на Ютубе. Пример: "ютуб остров сокровищ". Открывает страницу в браузере с поиском. (Inspired by @EnjiRouz)
|
**plugin_youtubesearch.py** - поиск на Ютубе. Пример: "ютуб остров сокровищ". Открывает страницу в браузере с поиском. (Inspired by @EnjiRouz)
|
||||||
|
|
||||||
|
**plugin_gamemoreless_alt.py** - альтернативная реализация игры Больше-меньше. Команда "игра меньше больше".
|
||||||
|
Является примером работы с контекстом альтернативным способом (**рекомендуется**) в движке Ирины.
|
||||||
|
|
||||||
**plugin_tts_silero.py** - (оффлайн) TTS через Silero. При первом запуске требует онлайна, чтобы скачать файл с нейросетью.
|
**plugin_tts_silero.py** - (оффлайн) TTS через Silero. При первом запуске требует онлайна, чтобы скачать файл с нейросетью.
|
||||||
Требует pytorch 1.9+. На мой взгляд, работает немного медленно + не озвучивает числа, требуя их перевода в числительные + шипит. Тем не менее, очень крут!
|
Требует pytorch 1.9+. На мой взгляд, работает немного медленно + не озвучивает числа, требуя их перевода в числительные + шипит. Тем не менее, очень крут!
|
||||||
Голос задается в конфиге.
|
Голос задается в конфиге.
|
||||||
|
|||||||
58
README.md
58
README.md
@@ -44,6 +44,64 @@
|
|||||||
|
|
||||||
Полная информация: [PLUGINS.md](/PLUGINS.md)
|
Полная информация: [PLUGINS.md](/PLUGINS.md)
|
||||||
|
|
||||||
|
### Поддержка контекста (с версии 4.0)
|
||||||
|
|
||||||
|
Обычно запуск любой команды требует префикса имени ассистента
|
||||||
|
(напр, "Ирина, погода"). Но иногда это избыточно - например, если мы пишем игру, и команды идут непрерывно.
|
||||||
|
|
||||||
|
С версии 4.0 поддерживается установление **контекста**. В контексте
|
||||||
|
движок:
|
||||||
|
* принимает фразы без префикса Ирина (имени ассистента)
|
||||||
|
* сразу передает их в функцию обработки контекста
|
||||||
|
|
||||||
|
**Как работает**
|
||||||
|
|
||||||
|
При вызове функции обработки можно вызвать функцию
|
||||||
|
```
|
||||||
|
core.context_set(new_context)
|
||||||
|
```
|
||||||
|
|
||||||
|
Контекст будет держаться 10 секунд, потом будет сброшен
|
||||||
|
(нужно, чтобы Ирина не зависала в состоянии, если пользователь ушел)
|
||||||
|
|
||||||
|
Пока контекст установлен, любой непустой вход (фраза) будет сразу
|
||||||
|
подана на вход функции контекста, после чего **контекст будет сброшен!**
|
||||||
|
|
||||||
|
Таким образом, если вы хотите продолжать взаимодействовать с контекстом,
|
||||||
|
нужно после каждого вызова функции его устанавливать повторно.
|
||||||
|
|
||||||
|
**Пример**
|
||||||
|
|
||||||
|
Для примера доступна игра "Больше-меньше" (плагин plugins/plugin_gamemoreless.py).
|
||||||
|
Запустите её с помощью команды "ирина игра больше меньше"
|
||||||
|
|
||||||
|
Также доступен второй тип задания контекста в альтернативной реализации игры
|
||||||
|
plugins_inactive/plugin_gamemoreless_alt.py.
|
||||||
|
**Альтернативный вариант рекомендуется больше, чем основной**
|
||||||
|
|
||||||
|
**Типы контекста**
|
||||||
|
|
||||||
|
Контекст может задаваться двумя способами:
|
||||||
|
1. Как функция, принимающая текстовый ввод (пример в игре больше меньше plugins/plugin_gamemoreless.py)
|
||||||
|
|
||||||
|
2. Как словарь команд внутри контекста - такой же, как при задании команд Ирины в манифесте плагина. В этом
|
||||||
|
случае движок будет пытаться распознать команду внутри контекста,
|
||||||
|
и в случае нераспознавания контекст будет переустановлен.
|
||||||
|
|
||||||
|
Пример диалога во втором варианте:
|
||||||
|
```
|
||||||
|
В: ирина игра больше меньше
|
||||||
|
О: скажи правила или начать
|
||||||
|
В: тест
|
||||||
|
О: не поняла (контекст сохранился, по-прежнему можно сказать правила)
|
||||||
|
```
|
||||||
|
|
||||||
|
Пример реализации игры Больше-меньше в альтернативном варианте есть в файле
|
||||||
|
plugins_inactive/plugin_gamemoreless_alt.py (нужно перенести в plugins_active,
|
||||||
|
а затем запустить командой "игра меньше больше")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Сторонние плагины
|
### Сторонние плагины
|
||||||
|
|
||||||
Если вы хотите узнать:
|
Если вы хотите узнать:
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ def start(core:VACore):
|
|||||||
"logPolicy": "cmd", # all | cmd | none
|
"logPolicy": "cmd", # all | cmd | none
|
||||||
|
|
||||||
"replyNoCommandFound": "Извини, я не поняла",
|
"replyNoCommandFound": "Извини, я не поняла",
|
||||||
|
"replyNoCommandFoundInContext": "Не поняла...",
|
||||||
"replyOnlineRequired": "Для этой команды необходим онлайн",
|
"replyOnlineRequired": "Для этой команды необходим онлайн",
|
||||||
|
|
||||||
"tempDir": "temp",
|
"tempDir": "temp",
|
||||||
|
|||||||
84
plugins/plugin_gamemoreless.py
Normal file
84
plugins/plugin_gamemoreless.py
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
# Игра больше меньше
|
||||||
|
# author: Vladislav Janvarev
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from vacore import VACore
|
||||||
|
import random
|
||||||
|
|
||||||
|
# функция на старте
|
||||||
|
def start(core:VACore):
|
||||||
|
manifest = { # возвращаем настройки плагина - словарь
|
||||||
|
"name": "Игра больше меньше", # имя
|
||||||
|
"version": "1.0", # версия
|
||||||
|
"require_online": False, # требует ли онлайн?
|
||||||
|
|
||||||
|
"commands": { # набор скиллов. Фразы скилла разделены | . Если найдены - вызывается функция
|
||||||
|
"игра больше меньше": play_game_start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return manifest
|
||||||
|
|
||||||
|
questNumber = -1
|
||||||
|
tries = 0
|
||||||
|
|
||||||
|
def play_game_start(core:VACore, phrase: str): # в phrase находится остаток фразы после названия скилла,
|
||||||
|
# если юзер сказал больше
|
||||||
|
# в этом плагине не используется
|
||||||
|
core.play_voice_assistant_speech("Скажи правила или начать")
|
||||||
|
core.context_set(play_1)
|
||||||
|
|
||||||
|
def play_1(core:VACore, phrase: str):
|
||||||
|
if phrase == "правила":
|
||||||
|
core.play_voice_assistant_speech("Правила игры. Я загадываю число от одного до тридцати. "
|
||||||
|
"Ты называешь число, а я говорю, загаданное число больше названного, или меньше. "
|
||||||
|
"Твоя задача - отгадать число за пять попыток. Скажи начать для начала игры.")
|
||||||
|
core.context_set(play_1)
|
||||||
|
return
|
||||||
|
if phrase == "начать" or phrase == "скачать" or phrase == "повторить":
|
||||||
|
global questNumber, tries
|
||||||
|
questNumber = random.randint(1,30)
|
||||||
|
#print(questNumber)
|
||||||
|
tries = 0
|
||||||
|
core.play_voice_assistant_speech("Число от одного до тридцати загадано. Начинаем!")
|
||||||
|
#play_game_start(core,"")
|
||||||
|
core.context_set(play_2)
|
||||||
|
return
|
||||||
|
|
||||||
|
if phrase == "отмена":
|
||||||
|
core.say("Поняла, играть не будем")
|
||||||
|
return
|
||||||
|
|
||||||
|
core.play_voice_assistant_speech("Не поняла...")
|
||||||
|
core.context_set(play_1)
|
||||||
|
|
||||||
|
def play_2(core:VACore, phrase: str):
|
||||||
|
from utils.num_to_text_ru import num2text
|
||||||
|
for i in range(1,31):
|
||||||
|
if phrase == num2text(i):
|
||||||
|
global tries
|
||||||
|
tries += 1
|
||||||
|
if i == questNumber:
|
||||||
|
core.say("Да, ты угадал. Поздравляю с победой! Скажи повторить, если хочешь сыграть еще раз.")
|
||||||
|
core.context_set(play_1)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
txtsay = ""
|
||||||
|
if i < questNumber:
|
||||||
|
txtsay += "Больше. "
|
||||||
|
else:
|
||||||
|
txtsay += "Меньше. "
|
||||||
|
|
||||||
|
if tries >= 5:
|
||||||
|
txtsay += "Пять попыток прошло, к сожалению, ты проиграл. А я загадала число "+num2text(questNumber)
|
||||||
|
txtsay += ". Скажи повторить, если хочешь сыграть еще раз."
|
||||||
|
core.say(txtsay)
|
||||||
|
core.context_set(play_1)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
core.say(txtsay)
|
||||||
|
core.context_set(play_2)
|
||||||
|
return
|
||||||
|
|
||||||
|
core.play_voice_assistant_speech("Не поняла число, скажи еще раз!")
|
||||||
|
core.context_set(play_2)
|
||||||
91
plugins_inactive/plugin_gamemoreless_alt.py
Normal file
91
plugins_inactive/plugin_gamemoreless_alt.py
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# Игра больше меньше (альтернативная реализация на меню)
|
||||||
|
# author: Vladislav Janvarev
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
from vacore import VACore
|
||||||
|
import random
|
||||||
|
|
||||||
|
# функция на старте
|
||||||
|
def start(core:VACore):
|
||||||
|
manifest = { # возвращаем настройки плагина - словарь
|
||||||
|
"name": "Игра больше меньше (альтернативная реализация)", # имя
|
||||||
|
"version": "1.0", # версия
|
||||||
|
"require_online": False, # требует ли онлайн?
|
||||||
|
|
||||||
|
"commands": { # набор скиллов. Фразы скилла разделены | . Если найдены - вызывается функция
|
||||||
|
"игра меньше больше": play_game_start,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return manifest
|
||||||
|
|
||||||
|
questNumber = -1
|
||||||
|
tries = 0
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def play_game_start(core:VACore, phrase: str): # в phrase находится остаток фразы после названия скилла,
|
||||||
|
# если юзер сказал больше
|
||||||
|
# в этом плагине не используется
|
||||||
|
core.play_voice_assistant_speech("Скажи правила или начать")
|
||||||
|
core.context_set(menu_main) # меню - набор фраз и правил, в конце файла
|
||||||
|
|
||||||
|
def play_cancel(core:VACore, phrase: str):
|
||||||
|
core.say("Поняла, играть не будем")
|
||||||
|
return
|
||||||
|
|
||||||
|
def play_rules(core:VACore, phrase: str):
|
||||||
|
core.play_voice_assistant_speech("Правила игры. Я загадываю число от одного до тридцати. "
|
||||||
|
"Ты называешь число, а я говорю, загаданное число больше названного, или меньше. "
|
||||||
|
"Твоя задача - отгадать число за пять попыток. Скажи начать для начала игры.")
|
||||||
|
core.context_set(menu_main)
|
||||||
|
|
||||||
|
|
||||||
|
def play_start(core:VACore, phrase: str):
|
||||||
|
global questNumber, tries
|
||||||
|
questNumber = random.randint(1,30)
|
||||||
|
#print(questNumber)
|
||||||
|
tries = 0
|
||||||
|
core.play_voice_assistant_speech("Число от одного до тридцати загадано. Начинаем!")
|
||||||
|
core.context_set(menu_in_game)
|
||||||
|
return
|
||||||
|
|
||||||
|
def play_game(core:VACore, phrase: str, i: int):
|
||||||
|
if phrase != "":
|
||||||
|
# что-то не распозналось или было добавлено. Просим повторить еще раз
|
||||||
|
core.say("Извини, не поняла число")
|
||||||
|
core.context_set(menu_in_game)
|
||||||
|
return
|
||||||
|
|
||||||
|
global tries
|
||||||
|
tries += 1
|
||||||
|
if i == questNumber:
|
||||||
|
core.say("Да, ты угадал. Поздравляю с победой! Скажи повторить, если хочешь сыграть еще раз.")
|
||||||
|
core.context_set(menu_main)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
txtsay = ""
|
||||||
|
if i < questNumber:
|
||||||
|
txtsay += "Больше. "
|
||||||
|
else:
|
||||||
|
txtsay += "Меньше. "
|
||||||
|
|
||||||
|
if tries >= 5:
|
||||||
|
txtsay += "Пять попыток прошло, к сожалению, ты проиграл. А я загадала число "+num2text(questNumber)
|
||||||
|
txtsay += ". Скажи повторить, если хочешь сыграть еще раз."
|
||||||
|
core.say(txtsay)
|
||||||
|
core.context_set(menu_main)
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
core.say(txtsay)
|
||||||
|
core.context_set(menu_in_game)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
# game menus
|
||||||
|
menu_main = {"правила":play_rules,"начать|скачать|повторить":play_start,"отмена":play_cancel}
|
||||||
|
|
||||||
|
menu_in_game = {}
|
||||||
|
from utils.num_to_text_ru import num2text
|
||||||
|
for i in range(1,31):
|
||||||
|
menu_in_game[num2text(i)] = (play_game, i)
|
||||||
@@ -62,23 +62,7 @@ if __name__ == "__main__":
|
|||||||
voice_input_str = record_and_recognize_audio()
|
voice_input_str = record_and_recognize_audio()
|
||||||
|
|
||||||
if voice_input_str != "":
|
if voice_input_str != "":
|
||||||
#print("Input: ",voice_input)
|
core.run_input_str(voice_input_str)
|
||||||
if core.logPolicy == "all":
|
|
||||||
print("Input: ",voice_input_str)
|
|
||||||
|
|
||||||
try:
|
|
||||||
voice_input = voice_input_str.split(" ")
|
|
||||||
#callname = voice_input[0]
|
|
||||||
for ind in range(len(voice_input)):
|
|
||||||
callname = voice_input[ind]
|
|
||||||
if callname in core.voiceAssNames: # найдено имя ассистента
|
|
||||||
if core.logPolicy == "cmd":
|
|
||||||
print("Input (cmd): ",voice_input_str)
|
|
||||||
|
|
||||||
command_options = " ".join([str(input_part) for input_part in voice_input[(ind+1):len(voice_input)]])
|
|
||||||
core.execute_next(command_options, None)
|
|
||||||
break
|
|
||||||
except Exception as err:
|
|
||||||
print(traceback.format_exc())
|
|
||||||
|
|
||||||
core._update_timers()
|
core._update_timers()
|
||||||
@@ -11,6 +11,11 @@ from vacore import VACore
|
|||||||
|
|
||||||
mic_blocked = False
|
mic_blocked = False
|
||||||
|
|
||||||
|
def block_mic():
|
||||||
|
global mic_blocked
|
||||||
|
#print("Blocking microphone...")
|
||||||
|
mic_blocked = True
|
||||||
|
|
||||||
# ------------------- vosk ------------------
|
# ------------------- vosk ------------------
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
q = queue.Queue()
|
q = queue.Queue()
|
||||||
@@ -109,28 +114,11 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
|
|
||||||
if voice_input_str != "":
|
if voice_input_str != "":
|
||||||
#print("Input: ",voice_input)
|
core.run_input_str(voice_input_str,block_mic)
|
||||||
if core.logPolicy == "all":
|
|
||||||
print("Input: ",voice_input_str)
|
|
||||||
|
|
||||||
try:
|
|
||||||
voice_input = voice_input_str.split(" ")
|
|
||||||
#callname = voice_input[0]
|
|
||||||
for ind in range(len(voice_input)):
|
|
||||||
callname = voice_input[ind]
|
|
||||||
if callname in core.voiceAssNames: # найдено имя ассистента
|
|
||||||
if core.logPolicy == "cmd":
|
|
||||||
print("Input (cmd): ",voice_input_str)
|
|
||||||
|
|
||||||
mic_blocked = True
|
|
||||||
command_options = " ".join([str(input_part) for input_part in voice_input[(ind+1):len(voice_input)]])
|
|
||||||
core.execute_next(command_options, None)
|
|
||||||
break
|
|
||||||
except Exception as err:
|
|
||||||
print(traceback.format_exc())
|
|
||||||
|
|
||||||
|
|
||||||
mic_blocked = False
|
mic_blocked = False
|
||||||
|
#print("UNBlocking microphone...")
|
||||||
else:
|
else:
|
||||||
#print("2",rec.PartialResult())
|
#print("2",rec.PartialResult())
|
||||||
pass
|
pass
|
||||||
@@ -144,4 +132,6 @@ if __name__ == "__main__":
|
|||||||
print('\nDone')
|
print('\nDone')
|
||||||
parser.exit(0)
|
parser.exit(0)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
parser.exit(type(e).__name__ + ': ' + str(e))
|
parser.exit(type(e).__name__ + ': ' + str(e))
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def runCmd(cmd:str,returnFormat:str):
|
|||||||
core.remoteTTS = returnFormat
|
core.remoteTTS = returnFormat
|
||||||
core.remoteTTSResult = ""
|
core.remoteTTSResult = ""
|
||||||
core.lastSay = ""
|
core.lastSay = ""
|
||||||
core.execute_next(cmd,None)
|
core.execute_next(cmd,core.context)
|
||||||
core.remoteTTS = tmpformat
|
core.remoteTTS = tmpformat
|
||||||
|
|
||||||
app = FastAPI()
|
app = FastAPI()
|
||||||
@@ -63,45 +63,40 @@ async def sendSimpleTxtCmd(cmd:str,returnFormat:str = "none"):
|
|||||||
# Пример: ирина погода, раз два
|
# Пример: ирина погода, раз два
|
||||||
@app.get("/sendRawTxt")
|
@app.get("/sendRawTxt")
|
||||||
async def sendRawTxt(rawtxt:str,returnFormat:str = "none"):
|
async def sendRawTxt(rawtxt:str,returnFormat:str = "none"):
|
||||||
voice_input = rawtxt.split(" ")
|
tmpformat = core.remoteTTS
|
||||||
|
core.remoteTTS = returnFormat
|
||||||
isFound = False
|
core.remoteTTSResult = ""
|
||||||
for ind in range(len(voice_input)):
|
core.lastSay = ""
|
||||||
callname = voice_input[ind]
|
isFound = core.run_input_str(rawtxt)
|
||||||
if callname in core.voiceAssNames: # найдено имя ассистента
|
core.remoteTTS = tmpformat
|
||||||
isFound = True
|
|
||||||
if core.logPolicy == "cmd":
|
|
||||||
print("Input (cmd): ",rawtxt)
|
|
||||||
|
|
||||||
command_options = " ".join([str(input_part) for input_part in voice_input[(ind+1):len(voice_input)]])
|
|
||||||
runCmd(command_options, returnFormat)
|
|
||||||
break
|
|
||||||
|
|
||||||
if isFound:
|
if isFound:
|
||||||
return core.remoteTTSResult
|
return core.remoteTTSResult
|
||||||
else:
|
else:
|
||||||
return "NO_VA_NAME"
|
return "NO_VA_NAME"
|
||||||
|
|
||||||
|
# Запускает внутреннюю процедуру проверки таймеров. Должна запускаться периодически
|
||||||
|
@app.get("/updTimers")
|
||||||
|
async def updTimers():
|
||||||
|
#core.say("аа")
|
||||||
|
#print("upd timers")
|
||||||
|
core._update_timers()
|
||||||
|
return ""
|
||||||
|
|
||||||
# simple threading for timer
|
def core_update_timers_http(runReq=True):
|
||||||
from threading import Thread, Event
|
from threading import Timer
|
||||||
|
if runReq:
|
||||||
class MyThread(Thread):
|
try:
|
||||||
def __init__(self, event):
|
import requests
|
||||||
Thread.__init__(self)
|
reqstr = "http://{0}:{1}/updTimers".format(webapi_options["host"],webapi_options["port"])
|
||||||
self.stopped = event
|
#print(reqstr)
|
||||||
|
r = requests.get(reqstr)
|
||||||
def run(self):
|
except Exception:
|
||||||
while not self.stopped.wait(0.5):
|
pass
|
||||||
core._update_timers()
|
t = Timer(2, core_update_timers_http)
|
||||||
|
t.start()
|
||||||
if __name__ != "__main__": # must run only in web
|
|
||||||
stopFlag = Event()
|
|
||||||
thread = MyThread(stopFlag)
|
|
||||||
thread.start()
|
|
||||||
# this will stop the timer
|
|
||||||
#stopFlag.set()
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
core_update_timers_http(False)
|
||||||
uvicorn.run("runva_webapi:app", host=webapi_options["host"], port=webapi_options["port"],
|
uvicorn.run("runva_webapi:app", host=webapi_options["host"], port=webapi_options["port"],
|
||||||
log_level=webapi_options["log_level"])
|
log_level=webapi_options["log_level"])
|
||||||
119
vacore.py
119
vacore.py
@@ -2,13 +2,16 @@ import os
|
|||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
import time
|
import time
|
||||||
|
from threading import Timer
|
||||||
|
|
||||||
import sounddevice as sound_device
|
import sounddevice as sound_device
|
||||||
import soundfile as sound_file
|
import soundfile as sound_file
|
||||||
|
|
||||||
from jaa import JaaCore
|
from jaa import JaaCore
|
||||||
|
|
||||||
version = "3.3"
|
version = "4.0"
|
||||||
|
|
||||||
|
# main VACore class
|
||||||
|
|
||||||
class VACore(JaaCore):
|
class VACore(JaaCore):
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
@@ -45,6 +48,10 @@ class VACore(JaaCore):
|
|||||||
self.remoteTTS = "none"
|
self.remoteTTS = "none"
|
||||||
self.remoteTTSResult = None
|
self.remoteTTSResult = None
|
||||||
|
|
||||||
|
self.context = None
|
||||||
|
self.contextTimer = None
|
||||||
|
self.contextTimerLastDuration = 0
|
||||||
|
|
||||||
import mpcapi.core
|
import mpcapi.core
|
||||||
self.mpchc = mpcapi.core.MpcAPI()
|
self.mpchc = mpcapi.core.MpcAPI()
|
||||||
|
|
||||||
@@ -150,10 +157,22 @@ class VACore(JaaCore):
|
|||||||
else:
|
else:
|
||||||
# it is function to call!
|
# it is function to call!
|
||||||
#context(self,command)
|
#context(self,command)
|
||||||
|
self.context_clear()
|
||||||
self.call_ext_func_phrase(command,context)
|
self.call_ext_func_phrase(command,context)
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# первый проход - ищем полное совпадение
|
||||||
|
for keyall in context.keys():
|
||||||
|
keys = keyall.split("|")
|
||||||
|
for key in keys:
|
||||||
|
if command == key:
|
||||||
|
rest_phrase = ""
|
||||||
|
next_context = context[keyall]
|
||||||
|
self.execute_next(rest_phrase,next_context)
|
||||||
|
return
|
||||||
|
|
||||||
|
# второй проход - ищем частичное совпадение
|
||||||
for keyall in context.keys():
|
for keyall in context.keys():
|
||||||
keys = keyall.split("|")
|
keys = keyall.split("|")
|
||||||
for key in keys:
|
for key in keys:
|
||||||
@@ -161,21 +180,19 @@ class VACore(JaaCore):
|
|||||||
rest_phrase = command[(len(key)+1):]
|
rest_phrase = command[(len(key)+1):]
|
||||||
next_context = context[keyall]
|
next_context = context[keyall]
|
||||||
self.execute_next(rest_phrase,next_context)
|
self.execute_next(rest_phrase,next_context)
|
||||||
#print(next_context)
|
|
||||||
#print(rest_phrase)
|
|
||||||
|
|
||||||
#if isinstance(next_context,dict):
|
|
||||||
|
|
||||||
|
|
||||||
#commands[key](*args)
|
|
||||||
#print
|
|
||||||
return
|
return
|
||||||
else:
|
|
||||||
#print("Command not found", command_name)
|
|
||||||
pass
|
|
||||||
|
|
||||||
# if not founded
|
# if not founded
|
||||||
self.play_voice_assistant_speech(self.plugin_options("core")["replyNoCommandFound"])
|
if self.context == None:
|
||||||
|
# no context
|
||||||
|
self.say(self.plugin_options("core")["replyNoCommandFound"])
|
||||||
|
else:
|
||||||
|
# in context
|
||||||
|
self.say(self.plugin_options("core")["replyNoCommandFoundInContext"])
|
||||||
|
# restart timer for context
|
||||||
|
if self.contextTimer != None:
|
||||||
|
self.context_set(self.context,self.contextTimerLastDuration)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
print(traceback.format_exc())
|
print(traceback.format_exc())
|
||||||
|
|
||||||
@@ -238,3 +255,79 @@ class VACore(JaaCore):
|
|||||||
# Wait until file is done playing
|
# Wait until file is done playing
|
||||||
status = sound_device.wait()
|
status = sound_device.wait()
|
||||||
|
|
||||||
|
# -------- raw txt running -----------------
|
||||||
|
def run_input_str(self,voice_input_str,func_before_run_cmd = None): # voice_input_str - строка распознавания голоса, разделенная пробелами
|
||||||
|
# пример: "ирина таймер пять"
|
||||||
|
if self.logPolicy == "all":
|
||||||
|
if self.context == None:
|
||||||
|
print("Input: ",voice_input_str)
|
||||||
|
else:
|
||||||
|
print("Input (in context): ",voice_input_str)
|
||||||
|
|
||||||
|
try:
|
||||||
|
voice_input = voice_input_str.split(" ")
|
||||||
|
#callname = voice_input[0]
|
||||||
|
haveRun = False
|
||||||
|
if self.context == None:
|
||||||
|
for ind in range(len(voice_input)):
|
||||||
|
callname = voice_input[ind]
|
||||||
|
|
||||||
|
if callname in self.voiceAssNames: # найдено имя ассистента
|
||||||
|
if self.logPolicy == "cmd":
|
||||||
|
print("Input (cmd): ",voice_input_str)
|
||||||
|
|
||||||
|
|
||||||
|
command_options = " ".join([str(input_part) for input_part in voice_input[(ind+1):len(voice_input)]])
|
||||||
|
|
||||||
|
# running some cmd before run cmd
|
||||||
|
if func_before_run_cmd != None:
|
||||||
|
func_before_run_cmd()
|
||||||
|
|
||||||
|
|
||||||
|
#context = self.context
|
||||||
|
#self.context_clear()
|
||||||
|
self.execute_next(command_options, None)
|
||||||
|
haveRun = True
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
if self.logPolicy == "cmd":
|
||||||
|
print("Input (cmd in context): ",voice_input_str)
|
||||||
|
|
||||||
|
# running some cmd before run cmd
|
||||||
|
if func_before_run_cmd != None:
|
||||||
|
func_before_run_cmd()
|
||||||
|
|
||||||
|
self.execute_next(voice_input_str, self.context)
|
||||||
|
haveRun = True
|
||||||
|
|
||||||
|
except Exception as err:
|
||||||
|
print(traceback.format_exc())
|
||||||
|
|
||||||
|
return haveRun
|
||||||
|
|
||||||
|
# ------------ context handling functions ----------------
|
||||||
|
|
||||||
|
def context_set(self,context,duration = None):
|
||||||
|
if duration == None:
|
||||||
|
duration = 10
|
||||||
|
|
||||||
|
self.context_clear()
|
||||||
|
|
||||||
|
self.context = context
|
||||||
|
self.contextTimerLastDuration = duration
|
||||||
|
self.contextTimer = Timer(duration,self._context_clear_timer)
|
||||||
|
self.contextTimer.start()
|
||||||
|
|
||||||
|
#def _timer_context
|
||||||
|
def _context_clear_timer(self):
|
||||||
|
print("Context cleared after timeout")
|
||||||
|
self.contextTimer = None
|
||||||
|
self.context_clear()
|
||||||
|
|
||||||
|
def context_clear(self):
|
||||||
|
self.context = None
|
||||||
|
if self.contextTimer != None:
|
||||||
|
self.contextTimer.cancel()
|
||||||
|
self.contextTimer = None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user