Files
smart-speaker/app/core/commands.py
2026-04-09 21:03:02 +03:00

123 lines
3.1 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
Command parsing helpers.
"""
import re
from .config import WAKE_WORD, WAKE_WORD_ALIASES
from ..audio.sound_level import is_volume_command, parse_volume_text
_STOP_WORDS_STRICT = {
"стоп",
"хватит",
"перестань",
"замолчи",
"прекрати",
"тихо",
"stop",
}
_STOP_PATTERNS_LENIENT = [
r"\bстоп\w*\b",
r"\bstop\b",
r"\bхватит\b",
r"\bперестан\w*\b",
r"\bпрекрат\w*\b",
r"\амолч\w*\b",
r"\bтише\b",
r"\bтихо\b",
r"\bвыключ\w*\b",
r"\bотключ\w*\b",
r"\bостанов\w*\b",
r"\bотмен\w*\b",
r"\bпауза\b",
r"\остаточно\b",
]
_STOP_PATTERNS_LENIENT_COMPILED = [re.compile(p) for p in _STOP_PATTERNS_LENIENT]
_FAST_WEATHER_PHRASES = {
"какая погода",
"какая погода на улице",
"какая сейчас погода",
"какая сейчас погода на улице",
"что по погоде",
"погода",
"погода на улице",
"что на улице",
"что там на улице",
"че там на улице",
}
_FAST_MUSIC_PHRASES = {
"включи музыку",
"поставь музыку",
"играй музыку",
"play music",
}
_WAKEWORD_PREFIX_RE = re.compile(
rf"^(?:{'|'.join(re.escape(alias) for alias in sorted({WAKE_WORD.lower(), *WAKE_WORD_ALIASES}, key=len, reverse=True))})(?:\s+|$)",
re.IGNORECASE,
)
def _normalize_text(text: str) -> str:
text = text.lower().replace("ё", "е")
text = re.sub(r"[^\w\s]+", " ", text, flags=re.UNICODE)
text = re.sub(r"\s+", " ", text, flags=re.UNICODE).strip()
return text
def normalize_command_text(text: str) -> str:
normalized = _normalize_text(text)
if not normalized:
return ""
return _WAKEWORD_PREFIX_RE.sub("", normalized, count=1).strip()
def is_stop_command(text: str, mode: str = "strict") -> bool:
"""
Detect stop commands in text.
mode:
- "strict": only exact stop words.
- "lenient": broader patterns for noisy recognition.
"""
if not text:
return False
normalized = _normalize_text(text)
if not normalized:
return False
if mode == "strict":
words = normalized.split()
return any(word in _STOP_WORDS_STRICT for word in words)
for pattern in _STOP_PATTERNS_LENIENT_COMPILED:
if pattern.search(normalized):
return True
return False
def is_fast_command(text: str) -> bool:
"""
Detect short commands that can stop STT early without waiting
for full utterance finalization.
"""
normalized = normalize_command_text(text)
if not normalized:
return False
if is_stop_command(normalized, mode="strict"):
return True
if normalized in _FAST_WEATHER_PHRASES:
return True
if normalized in _FAST_MUSIC_PHRASES:
return True
if is_volume_command(normalized) and parse_volume_text(normalized) is not None:
return True
return False