@Elacov_top

Как сделать активацию по спискам?

Хочу сделать Голосовой ассистент. я добавил списки на имя и команды:
VA_ALIAS = ('маркус', 'марк', 'марик')

VA_CMD_LIST = {
    "help": ('список команд', 'команды', 'что ты умеешь', 'твои навыки', 'навыки'),
    "ctime": ('время', 'текущее время', 'сейчас времени', 'который час'),
    "open_browser": ('открой браузер', 'запусти браузер', 'открой гугл хром', 'гугл хром'),
    "vk": ('открой vk', 'запусти vk', 'vk', 'открой вк', 'запусти вк', 'вк'),
    "gta": ('открой gta 5 rp', 'запусти gta 5 rp', 'откройте  gta 5 rp', 'запусти gta 5p', 'открой gta v rp')
}

После я сделал условие:
if text in (VA_NAME, VA_CMD_LIST[help]):
            print('Я могу: Открывать браузер, говорить текущее время, открывать браузер, вк и гта 5 рп. Проще говоря - все что нужно моему создателю.')

Но я не могу сослаться к списку, ведь программа его не видит. Как сделать правильно?
  • Вопрос задан
  • 124 просмотра
Пригласить эксперта
Ответы на вопрос 2
Vindicar
@Vindicar
RTFM!
Давай-ка я тебе покажу как я бы эту проблему решил. А уж осилишь или нет...

Идеи:
1. Разные команды должны быть независимы друг от друга.
2. Проверка совпадения текста с командой и выполнение самой команды - разные задачи.
3. Список команд не должен быть захардкожен.
4. Код, нужный для реализации и добавления команды, должен быть по возможности сосредоточен в одном месте.
5. Следует избегать необходимости повторять один и тот же код в обработчиках и предикатах. Это позволит упростить их.

Следствия:
1. Каждая команда должна быть отдельной подпрограммой - обработчиком команды.
2. Для каждого обработчика команды должна существовать отдельная подрограмма - предикат команды. Она проверяет, подходит ли произнесённый текст под команду.
3. Нужно хранить коллекцию (например, список - list) пар "обработчик - предикат". При работе бот будет перебирать этот список, ища подходящую пару. Должен быть механизм внесения пары в этот список (регистрация обработчика).
4. Следует предусмотреть возможность регистрации с помощью декораторов. Это удобно и позволяет разместить описание обработчика и его регистрацию рядом, неотрывно друг от друга.
5а. Основной бот должен заниматься вопросами распознавания речи, проверки наличия ключевого слова (имени бота), вызова предикатов для определения обработчика и возврата ответа пользователю (через консоль или синтез речи).
5б. Стоит предусмотреть способ автоматического конструирования простых предикатов вроде "текст начинается с указанного слова/слов".

Отсюда мы получаем следующее...
Код

import typing as t
import speech_recognition as sr


# предикат принимает текстовую строку и возвращает истину или ложь
# истина значит "я понимаю эту строку, мой обработчик с ней справится".
PredicateFunc = t.Callable[[str], bool]
# обработчик принимает объект бота и текстовую строку команды. Он не возвращает ничего.
HandlerFunc = t.Callable[['VoiceBot', str], None]


class VoiceBot:
    """Основной класс бота."""
    
    def __init__(self, name: str):
        """Конструктор вызывается при создании экземпляра бота."""
        self._name: str = name.lower()  # любая команда должна начинаться с имени бота
        # список известных боту обработчиков
        self._handlers: t.List[t.Tuple[PredicateFunc, HandlerFunc]] = []
        # тут будут другие поля, нужные для работы класса бота
        # ты ведь знаешь, как работают классы в питоне?
        self._recognizer = sr.Recognizer()
    
    def say(self, text: str) -> None:
        """Обработчики могут вызвать say(), чтобы бот что-нибудь сказал или написал."""
        print(text)  # пока что бот просто пишет ответ в консоль
    
    def register(self, condition: t.Union[str, t.List[str], PredicateFunc]):  # принимает строку, список строк или готовую функцию-предикат
        """Этот метод используется как декоратор для регистрации обработчика.
        Он может принимать как готовую функцию-предикат, так и просто строку/строки, с которых должна начинаться команда.
        В этом случае он подготовит нужный предикат самостоятельно.
        """
        if isinstance(condition, str):  # получили строку? переделаем её в предикат
            s_lower = s.lower()
            
            def predicate(text: str) -> bool:  # объявления функций могут быть вложенными
                return text.lower().startswith(s_lower)  # если команда начинается с этой строки
            
        elif isinstance(condition, (list, tuple)):  # получили список строк
            strings_lower = [s.lower() for s in condition]  # делаем список строк в нижнем регистре
            
            def predicate(text: str) -> bool:  # объявления функций могут быть вложенными
                return any(text.lower().startswith(s) for s in strings_lower)  # если любая из строк подошла
            
        else:  # получили готовую функцию-предикат - используем её как есть
            predicate = condition
        
        def decorator(handler: HandlerFunc):  # фактический декоратор получит на вход функцию-обработчик
            self._handlers.append((predicate, handler))  # запоминаем пару предикта-обработчик
            return handler  # декоратор должен вернуть функцию
        
        return decorator
    
    def _find_correct_handler(self, command: str) -> t.Union[HandlerFunc, None]:
        """Этот метод определяет, какой из известных обработчиков подойдёт к команде command."""
        for predicate, handler in self._handlers:
            try:
                if predicate(command):  # предикат сказал "да"
                    return handler  # значит, этот обработчик и будем использовать
            except Exception as err:  # что-то пошло не так с предикатом
                print(f'Predicate error for handler {handler!r}', err)  # сообщаем об ошибке, ищем дальше
        # если дошли досюда, мы не нашли подходящий обработчик
        return None  # вернём None как признак неудачи
    
    def do_one_command(self):
        """Этот метод запишет и обработает одну команду."""
        try:
            with sr.Microphone(device_index = 1) as source:
                print('Настраиваюсь.')
                self._recognizer.adjust_for_ambient_noise(source, duration=0.6)
                print('Слушаю...')
                audio = self._recognizer.listen(source)
            print('Услышал.')
            query = self._recognizer.recognize_google(audio, language = 'ru-RU')
        except Exception as err:  # никогда не "глотай" ошибки - хотя бы выведи их в журнал ,если не знаешь, что с ними делать.
            self.say('Ошибка при записи/распознавании!')  # даём знать пользователю
            print(err)  # это сообщение об ошибке нужно будет проанализировать, чтобы понять где ошибка
            return
        text = query.lower()
        print(f'Вы сказали: {text}')
        if not text.startswith(self._name):  # текст начинается не с нашего ключевого слова (имени) - игнорируем
            return
        text = text[len(self._name):].strip()  # убираем имя из текста
        handler = self._find_correct_handler(text)  # ищем обработчик
        if handler is None:  # не нашли
            self.say("Я не понимаю, что вы хотите.")  # так и говорим пользователю
            return
        # если дошли досюда - значит нашли
        try:
            handler(self, text)  # вызываем обработчик команды
        except Exception as err:  # он может потерпеть неудачу
            self.say("Произошла ошибка")  # так и говорим пользователю
            print(err)  # это сообщение об ошибке нужно будет проанализировать, чтобы понять где ошибка



А вот пример использования:
if __name__ == '__main__':
    import webbrowser 
    
    bot = VoiceBot('ботва')  # "Ботва" - ключевое слово (имя) бота.
    
    # описываем и регистрируем обработчик команды
    @bot.register(['открой контакт', 'открой вк'])  # должен сработать на "ботва открой контакт", например
    def command_vk(bot, text):
        webbrowser.open('https://vk.com', new=1)
        bot.say('Готово.')
    
    # рабочий цикл бота
    while True:
        bot.do_one_command()

Как видишь, это очень похоже на код, который получается при использовании готовых библиотек для создания чат-ботов (текстовых). Потому что идеи, лежащие в основе, одни и те же.

Как работают декораторы?

Если коротко, то вот этот код:
@bot.register(['открой контакт', 'открой вк'])
def command_vk(bot, text):
    webbrowser.open('https://vk.com', new=1)
    bot.say('Готово.')

эквивалентен вот такому коду:
_decorator_func = bot.register(['открой контакт', 'открой вк'])  # готовим функцию-декоратор

def command_vk(bot, text):  # описываем декорируемую функцию
    webbrowser.open('https://vk.com', new=1)
    bot.say('Готово.')

_decorated_func = _decorator_func(command_vk)  # вызываем декоратор на этой функции
command_vk = _decorated_func  # заменяем декорируемую функцию на то, что вернул декоратор



Да, боты - это ни разу не просто.
Ответ написан
Комментировать
AlexNest
@AlexNest Куратор тега Python
Работаю с Python/Django
Открываете оф.документацию или любой учебник (Например, "Изучаем Python" Марка Лутца) и читаете.
В них подробно описано как работать со структурами данных.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы