Smooch: How to do postback dependent state transit

2019-02-25 09:17发布

I am trying to transition the script from one state to another based on Smooch postback payloads; but getting error code H12.

Consider the example https://github.com/smooch/smooch-bot-example

Say I modify the script https://github.com/smooch/smooch-bot-example/blob/master/script.js as follows

start: {
    receive: (bot) => {
        return bot.say('Hi! I\'m Smooch Bot! Continue? %[Yes](postback:askName) %[No](postback:bye) );
    }
},
bye: {
    prompt: (bot) => bot.say('Pleasure meeting you'),
    receive: () => 'processing'
},

The intention is that the's bot's state would transition depending on the postback payload.

Question is, how do I make that happen?

My approach was add

stateMachine.setState(postback.action.payload)

to the handlePostback method of github.com/smooch/smooch-bot-example/blob/master/heroku/index.js

However, that threw an error code H12. I also experimented with

stateMachine.transition(postback.action,postback.action.payload)

to no avail.

3条回答
Ridiculous、
2楼-- · 2019-02-25 09:55

If you want to advance the conversation based on a postback you'll have to first output the buttons from the bot's prompt (so you can handle the button click in the receive), modify the handlePostback function in index.js, then handle the user's "reply" in your receive method - try this - modify script.js like so:

start: {
    prompt: (bot) => bot.say(`Hi! I'm Smooch Bot! Continue? %[Yes](postback:askName) %[No](postback:bye)`),
    receive: (bot, message) => {

      switch(message.text) {
        case 'Yes':
          return bot.say(`Ok, great!`)
            .then(() => 'hi')
          break;
        case 'No':
          return bot.say(`Ok, no prob!`)
            .then(() => 'bye')
          break;
        default:
          return bot.say(`hmm...`)
            .then(() => 'processing')
          break;          
      }
    }
},

hi: {
    prompt: (bot) => bot.say('Pleasure meeting you'),
    receive: () => 'processing'
},

bye: {
    prompt: (bot) => bot.say('Pleasure meeting you'),
    receive: () => 'processing'
},

Then modify the handlePostback function in index.js so that it treats a postback like a regular message:

function handlePostback(req, res) {

    const postback = req.body.postbacks[0];

    if (!postback || !postback.action)
        res.end();

    const stateMachine = new StateMachine({
        script,
        bot: createBot(req.body.appUser)
    });

    const msg = postback;

    // if you want the payload instead just do msg.action.paylod
    msg.text = msg.action.text;

    stateMachine.receiveMessage(msg)
      .then(() => res.end())
      .catch((err) => {
        console.error('SmoochBot error:', err);
        res.end();
      });
}

Now when a user clicks your button it will be pushed to the stateMachine and handled like a reply.

查看更多
Explosion°爆炸
3楼-- · 2019-02-25 10:02

I got the same issue with the [object Object] instead of a string. This is because the state you get or set with a function is contained in an object, not a string... I fixed it with this code inside index.js, replacing the existing handlePostback function in the smooch-bot-example GitHub repo:

function handlePostback(req, res) {

const stateMachine = new StateMachine({
    script,
    bot: createBot(req.body.appUser)
});

const postback = req.body.postbacks[0];
if (!postback || !postback.action) {
    res.end();
};

const smoochPayload = postback.action.payload;

// Change conversation state according to postback clicked
switch (smoochPayload) {
    case "POSTBACK-PAYLOAD":
        Promise.all([
            stateMachine.bot.releaseLock(),
            stateMachine.setState(smoochPayload), // set new state
            stateMachine.prompt(smoochPayload) // call state prompt() if any
        ]);
        res.end();
    break;

    default:
        stateMachine.bot.say("POSTBACK ISN'T RECOGNIZED") // for testing purposes
            .then(() => res.end());
};
}

Then inside script.js all you need to do is define states corresponding to the exact postback payloads. If you have multiple postbacks that should take the user to other states, just add them to the case list like so :

case "POSTBACK-PAYLOAD-1":
case "POSTBACK-PAYLOAD-2":
case "POSTBACK-PAYLOAD-3":
case "POSTBACK-PAYLOAD-4":
Promise.all([
        stateMachine.bot.releaseLock(),
        stateMachine.setState(smoochPayload), // set new state
        stateMachine.prompt(smoochPayload) // call state prompt() if any
    ]);
    res.end();
break;

Note that you should not write break; at the end of each case if the outcome you want is the same (here : setting the state and prompting the corresponding message).

If you want to handle other postbacks differently, you can add cases after the break; statement and do other stuff instead.

Hope this helps!

查看更多
一夜七次
4楼-- · 2019-02-25 10:05

Postbacks won't automatically transition your conversation from one state to the next, you have to write that logic yourself. Luckily the smooch-bot-example you're using already has a postback handler defined here:

https://github.com/smooch/smooch-bot-example/blob/30d2fc6/heroku/index.js#L115

So whatever transition logic you want should go in there. You can do this by creating a stateMachine and calling receiveMessage() on it the same way handleMessages() already works. For example:

const stateMachine = new StateMachine({
    script,
    bot: createBot(req.body.appUser)
});

stateMachine.receiveMessage({
    text: 'whatever your script expects'
})

Alternatively, you could have your handlePostback implementation call stateMachine.setState(state) and stateMachine.prompt(state) independently, if you wanted to have your postbacks behave differently from regular text responses.

查看更多
登录 后发表回答