123 lines
3.1 KiB
Python
123 lines
3.1 KiB
Python
"""
|
||
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"\bзамолч\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достаточно\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
|