fletcherist / yandex-dialogs-sdk

☂️Build your skill for Yandex.Alice with ease. (nodejs/typescript)
MIT License
122 stars 16 forks source link

Confirm Method #29

Closed fletcherist closed 5 years ago

fletcherist commented 6 years ago

@popstas предложил реализовать что-то вроде такого метода

alice.welcome((ctx) => {
  ctx.confirm({
    reply: '18 есть?',
    onYes: (ctx) => ctx.confirm({
      reply: 'А вы уверены?',
      onYes: (ctx) => ctx.reply('добро пожаловать'),
    }),
    onNo: (ctx) => {ctx.reply('в другой раз'),
  })
})

Реализовать можно в качестве middleware. Давайте обсуждать!

popstas commented 6 years ago

Первая реализация confirm - https://github.com/popstas/yandex-dialogs-whatis/commit/490164ad666c8eb4b529bc51ec44bf30ca484c37

Это мидлварь + простой матчер + команда. Мидлварь создает функцию confirm, при вызове пишет флаг в сессию. Матчер проверяет сессию, если там есть данные { onYes, onNo }, вызывает соответствующее действие.

Какие я вижу проблемы:

  1. Функции предполагают хранение в памяти, а я надеюсь когда-нибудь сделать хранение ctx.session в БД и сделать возможным хостить навык на AWS Lambda, если начать сейчас хранить разные функции в ctx.session, этот момент может отдалиться.
  2. У меня уже есть несколько мест, где напрашивается необязательное подтверждение, например, когда юзер повторно заходит, можно говорить не "привет", а "привет, рассказать, что я умею?", хочется иметь возможность проигнорировать вопрос, но при желании ответить "да". Еще это очень в тему, когда навык примерно понял, что имел в виду юзер, но не уверен.
  3. Фразы, включающие "да/нет": с одной стороны будет круто, если не придется каждому их переизобретать, я думаю уже минут 10 и понимаю, что с ними не всё так просто, например, "конечно нет", "да нет наверное", "да зачем", "давай потом", то есть простой регуляркой можно поймать либо мало, либо не то, но перечислять все варианты мне тоже кажется неправильно. С одной стороны ложное срабатывание в подтверждении стремно, с другой хочется побольше вариантов принимать. А с другой стороны надо бы дать разработчику возможность переопределить встроенные матчеры внутри confirm.
  4. Уверен, что в навыках, которыми пользуешься часто и знаешь места, где запросят подтверждение, захочется отвечать так, чтобы не задавали лишних вопросов, типа не "забудь всё", а "забудь всё, точно", типа --force. Теоретически решение может быть таким: сделать мидлварь, которая будет искать такие маркеры, убирать их из ctx.message (чтобы разработчик не делал две alice.command) и ставить флаг, например ctx.session.confirmForce. Стремно в этом случае то, что подобное вырезание может легко сломать команды так, что разработчик долго будет искать проблему, например, он может сделать команду типа "отмерь точно", которая никогда не сработает (он же не должен по идее даже знать про эту магию). Поэтому эту крутую фичу если и делать, то опциональной мидлварью (а если надо переопределить слова, то проще ее скопипастить себе, она маленькая будет).

Итого: думаю команда будет выглядеть примерно так:

ctx.confirm(questionMiddleware, yesMiddleware, noMiddleware, options)

options:

{
  yesMatcher: ctx => ctx.message === 'да',
  noMatcher: ctx => ctx.message === 'нет',
  anyMiddleware: ctx => ctx.reply('ответь'),
}

Или ты думаешь, что лучше всё в options засунуть? Я вынес три мидлвари, т.к. предполагаю, что почти всегда будут использовать их и очень редко то, что в options.

Такой подход, по-моему, довольно красиво вписывается в концепции sdk, матчеры yes/no думаю пригодятся разработчикам сами по себе в других местах.

На disposable думаю забить пока, пусть функции в ctx.session лежат. Другой вариант, который я сначала обдумывал: хранить в yes и no ответ строкой, а потом внутренне проговаривать его навыку, но так вроде сейчас не сделать, да и сомнительный это способ, тогда придется делать разные alice.command на подтверждение и на действие.

Итак, если нет возражений, давай попробую сделать PR с решенными 3 и 4, с нерешенными 1 и 2.

Что насчет опциональных подтверждений, есть идеи, как их реализовать? Проблема в том, что на yes/no должны быть даны ответы, а вот на any надо возвращаться к command.search(), как бы это сделать? К этому времени матчер уже сработал, и надо почистить ctx.session.confirm и сделать вид, что подтверждения не было, снова найти подходящую команду.

fletcherist commented 5 years ago

Close, out of date. Feel free to reopen this, if you want to suggest something.