Documentation

Powered by Algolia

How-To

How to get the right input from the user

intent("I want a coffee", async p => {
    p.play("What coffee do you prefer?");
    const coffee = await p.then(whatCoffee);
    p.play(`Your ${coffee} coffee will be ready in a moment`);
});

const whatCoffee = context(() => {
    follow("$(COFFEE Black|Americano|Latte|Cappuccino|Expresso|Macchiato)", p => p.resolve(p.COFFEE));
})

How to send data to a client application

You can send a JSON object to your application

intent("Change color to $(C red|green|blue)", p => {
    p.play({command: "setColor", color: p.C});
});

How to get arbitrary user inputs

Use regex variable (asterisk * follows variable) inside intent or follow to get user input by template

intent("I want to give a feedback", async p => {
    p.play("Thank you! What do you think about our product?");
    const feedback = await p.then(userInput);
    p.play({command: "feedback", value: feedback});
    p.play(`Your feedback is: ${feedback}`);
});

const userInput = context(() => {
    follow("$(I* .+)", p => p.resolve(p.I));
})

How to handle unexpected voice commands from the user

Use fallback function in the user input to get the right response from the user

intent("Check my math", async p => {
    const a = Math.floor(Math.random()*10)
    const b = Math.floor(Math.random()*10)
    p.play(`How much ${a} plus ${b}?`)
    const sum = await p.then(getNumber)
    p.play(`Your answer ${sum} is ${a+b==sum? 'correct': 'incorrect'}`)
});

const getNumber = context(() => {
    follow("$(NUMBER)", p => p.resolve(p.NUMBER.number))

    fallback("You have to say a number")
})

How to iterate over a list of objects (back and forward)

let fruits = ["apple", "orange", "lemon", "mango", "banana", "avocado", "kiwi"]

intent("(I want to|) (select|choose) fruits", async p => {
    p.play("Iterate over the list of fruits and select your favorite one. To select fruit say 'select', say 'forward' or 'back' to traverse over the list");
    p.play("Say 'finish' to complete your choice")
    p.userData.favorite = []
    p.play(`We have ${fruits.length} fruit in our list. First is ${fruits[0]}`)
    await p.then(selectFruits, {state: { index : 0}})
    if (p.userData.favorite.length > 0) {
        p.play(`Your favorite fruits are: ${p.userData.favorite.join(", ")}`)
    } else {
        p.play(`You have not chosen any fruit`)
    }
});

const selectFruits = context(() => {
    follow("(select|choice)",  p => {
        let selected = false
        if(!p.userData.favorite.includes(fruits[p.state.index])) {
            p.userData.favorite.push(fruits[p.state.index])
        }
        p.play(fruits[p.state.index] + " is selected")
        p.play(p.userData.favorite)
    })

    follow("(repeat|say again)", p => playFruit(p, p.state.index))

    follow("(forward|next)", p => {
        if(p.state.index < fruits.length-1) {
            p.state.index++;
            playFruit(p, p.state.index)
        }
        else {
            p.play("It's the last fruit in the list")
        }
    })

    follow("(back|previous)", p => {
        if(p.state.index > 0) {
            p.state.index--;
            playFruit(p, p.state.index)
        }
        else {
            p.play("It's the first fruit in the list")
        }
    })

    follow("(finish|stop|it's all)", p => p.resolve())

    fallback("Say select to choose fruit, say forward or back to navigate through the list or say 'finish' to return")
})

const playFruit = (p, index) => {
    p.play(`Fruit number ${p.state.index+1} is ${fruits[p.state.index]}`)
}

How to pass state object to a context

intent("I have friends", async p => {
    p.play("How many friends do you have?");
    let number = await p.then(numberContext)
    p.play("What is name of your friend number one?");
    let index = 0, friends = [];
    let list = await p.then(friendsContext, {state: {number, index, friends}})
    p.play({command: "friends", list})
})

const friendsContext = context(() => {
    follow("$(NAME)", p => {
        let {number, friends} = p.state;
        friends[p.state.index++] = p.NAME;
        if (p.state.index == number) {
            p.resolve(friends)
        }else {
            p.play(`What is name of your friend number ${p.state.index+1}?`);
        }
    })
})

const numberContext = context(() => {
    follow("$(NUMBER)", p => {
        if (p.NUMBER.number > 0) {
            p.resolve(p.NUMBER.number)
        } else {
            p.play("Number of friends must be one or more")
        }
    })
})

How to use intents with fuzzy parameters

const array = ["small", "medium", "large"];
intent(`$(ORD~ one~1|first~1|two~2|second~2|middle~2|three~3|third~3|last~3)`, p => {
    // one is value, 1 is label
    let index = parseInt(p.ORD.label);
    p.play(`You've selected ${array[index-1]}`);
})

How to use several variables of the same type in one intent

You can reference to the same variable name in one intent. In this case all variable values will be collected in an array with a special name which come from the variable name plus 's'. For example, if you have several variables named ITEM you can access object ITEMs with an array of size 2 and you can reference first item by ITEMs[0].

const items = ["burger", "cola", "taco"].join('|');
intent(`I want $(NUMBER) $(ITEM ${items}) and $(NUMBER) $(ITEM ${items})`, p => {
    let order = _.reduce(_.range(_.size(p.NUMBERs)), (acc, i) => {acc[p.ITEMs[i]] = p.NUMBERs[i].number; return acc}, {});
    p.play({command: "createOrder", order });
})

How to get data from external web services

Here is an example how to get data from a weather web service

const serviceURL = "http://api.openweathermap.org/data/2.5/weather?appid=4acdb6432d18884ebc890c13a9c3cc85"
intent("what is the weather in $(LOC)", p => {
    let location = p.LOC;
    api.request(serviceURL + `&q=${location}&units=imperial`, (error, res, body) => {
        if (error || res && res.statusCode !== 200) {
            p.play(`Could not get weather information`)
        } else {
            let data = JSON.parse(body);
            p.play(`The temperature in ${location} is ${Math.floor(data.main.temp)} degrees in Fahrenheit`);
        }
    });
})

How to use nouns regardless of single or plural form

  intent("I want $(NUMBER) ($P cake_)", p => {
      p.play(`Here is your ${p.NUMBER} ${p.P}`);
  })

Note. This syntax is applicable only to nouns whose plural forms are no exception.

How to use predefined variables

intent(`I meet $(NAME) from $(ORG) $(DATE) (in|at|near|) $(LOC) (at|) $(TIME)`, p => {
    p.play(`OK. I will meet ${p.NAME} from ${p.ORG} ${p.DATE} at ${p.TIME} in ${p.LOC}`)
})

I want the user to be able to say either the acronym of a product name or the product name itself. How do we do this?

It is often necessary to give the user the opportunity to say the same thing in different ways. For example, a particular product in the price list can be pronounced in several ways, but still we must recognize which product the user is talking about. For these purposes, you can compile a synonym dictionary for each element and use fuzzy parameters in intent / follow. Fuzzy parameters make it possible to associate a key with a value that was recognized as user input.

const products = [
    {id: "cola",  aliases: ["cola","coca-cola", "soda", "coca"]},
    {id: "juice", aliases: ["juice", "fresh", "orange juice", "orange fresh"]}
]
// We have to construct following fuzzy parameter with all value~label items concatenated with |
// cola~cola|cola-cola~cola|soda~cola|coca~cola|juice~juice|fresh~juice|orange juice~juice|orange fresh~juice

const PROD_INTENTS = _.flatten(products.map(p => p.aliases.map(a => `${a}~${p.id}`))).join('|')

intent(`(I want|give me) $(ITEM ${PROD_INTENTS})`, p => {
    p.play(`We added ${p.ITEM.label} to your order`)
})

How voice commands can respond differently to the user based on visual context

You can set the visual state object on the client side and it will be available in p object when user input matches intent or follow.
Check our documentation to see how to send visual state in Аndroid and iOS application.

intent('What can I do here?', p => {
    switch (p.visual.screen) {
       case "main":
           p.play("Here is main screen. Choose a category.");
           break;
       case "checkout":
           p.play("You are in your cart. You can change or finish your order.");
           break;
       case "product":
           p.play(`It's product detail page for ${p.visual.product.name}`);
           break;    
       default:
           p.play("Say open menu to start");
   }
})

You can also filter intents matching based on visual state value.
For example, this intent will never match if cart is empty.

intent(visual(v => !_.isEmpty(v.order)), `(What is|) my order (details|)`, p => {
    p.play("You have ordered:");
    for (let product in p.visual.order) {
        p.play(p.visual.order[product] + " " + product);
    }
});