Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Machina not uniquely handling timers for concurrent users #168

Open
Harrisonkamau opened this issue Feb 4, 2020 · 1 comment
Open

Machina not uniquely handling timers for concurrent users #168

Harrisonkamau opened this issue Feb 4, 2020 · 1 comment

Comments

@Harrisonkamau
Copy link

Introduction
I have a Node.js Facebook Messenger Bot built with Bottender.js & Machina.js.
Bottender connects to Facebook and offloads events to Machina for handling. Machina manages the conversation flow - in terms of states.

Packages and their versions

  • Machina - v 4.0.2
  • Node.js - v 10.16.0,
  • Bottender - v 0.15.17

Problem statement
I'm using BehavioralFSM instances across the bot. Let's explore this code snippet:

const machina = require('machina');
const contactUsFsm = new machina.BehavioralFsm({
  namespace: 'contact-us-fsm',
  initialState: 'uninitialized',
  states: {
    uninitialized: {
      async messengerPostback(context, payload){
        // handles postback events from FB
      },
      async messengerText(context, payload) {
        // handles text responses from FB
      }
    },

    getContactInfo: {
      _onEnter(context) {
        this.hasInput = false; // gets updated in `getEmail` & `getPhone` states.
        this.timer = setTimeout(
          () => {
            if(!this.hasInput()) {
              // send user another prompt to provide their contact info
            } else {
              //cancel this timer & reset to the initial state
              this.transition(context, )
            }
          },
          12000 // 12s
        )
      },

      async messengerText(context, payload) {
        // once this handler is triggered, update 'hasInput'
        this.hasInput = true;
        // if they provide an email, take them to 'getPhone' state
        // if they provide a phone, take them to 'getEmail' state
      }
    },

    getEmail: {
     // handles both 'SKIP EMAIL' functionality and actual data
    },

    getPhone: {
    // handles both 'SKIP PHONE' functionality and actual data
    },

    sendContactInfo: {
      // send this data to an external API
    }
  }
});

A user is required to provide either their phone number or email address. If they don't do this in 15s, we prompt then again. This timer doesn't always work for concurrent users. See below.

hasInput is used to track if a user has provided either phone number or email address so as to cancel the initial timeout which prompts them for contact info after 15s of inactivity.

The other failing part is:
I'd like to watch out for SUBMIT PHONE or SUBMIT EMAIL. The getEmail and getPhone states allow the user to click a button to skip the state (i.e. if they already have provided one contact info, they do not have to provide the second one). There's another timeout that sends the available contact info to an external API (basically transitions to sendContactInfo state)after 2 minutes of inactivity. I.e. If the user doesn't click on either SUBMIT PHONE or SUBMIT_EMAIL buttons. The challenge with this functionality as it is now is machina has a shared FSM between concurrent users so when a user fails clicks on Submit buttons and another clicks, the timer are both reset or the this keyword only works for the first user. Subsequent users are ignored.

Any ideas are welcome!

@xuebutayan
Copy link

Set it as a separate state machine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants