Yoctol / bottender

⚡️ A framework for building conversational user interfaces.
https://bottender.js.org
MIT License
4.22k stars 335 forks source link

pre-parsing inputs? #837

Open dcsan opened 4 years ago

dcsan commented 4 years ago

Is your feature request related to a problem? Please describe. when using the router methods, is there a way to parse the text we're matching against? I want to do some basic replacement for example lowerCase the inputs.

I could use regex but the plain matches are nicer

text(['h', 'help'], Help)   // doesnt match Help
text(/^h$|^help$/i, Help),  // have to use regex

Describe the solution you'd like A way to process incoming text. For example a middleware on the incoming request?

as well as the lowercase example above I also want to do some synonym replacement to normalize things. This is nicer than doing complex regex everywhere... just clean up the inputs

Describe alternatives you've considered using regex but it quickly gets hard to read.

Additional context overall I really like the router with the chained responsibility and the way you've integrated regex match routes...

chentsulin commented 4 years ago

I'm not a big fan of transforming inputs in middleware. In my opinion, input events and input texts should be immutable. Just like we do not transform the keypress inputs when receiving keypress events in browser.

And also, it's hard to provide a powerful route without using a regex. For example, if I want to provide a route matches the following inputs:

We can't provide a built-in way to satisfy all user requirements. So, I'd like to leave it to the users, and they can build custom routes to solve this kind of case:

import { text, route } from 'bottender/router';

// instead of doing this
text(['h', 'help'], Help)

// you could write a custom route:
function myPowerfulTextRoute(strs, action) {
  return route(
    (context) => 
      context.event.isText && 
      strs.some(str => new RegExp(str, 'i').test(context.event.text)), 
    action
  );
}
myPowerfulTextRoute(['h', 'help'], Help)

See: https://bottender.js.org/docs/en/the-basics-routing#custom-routes

This approach could still be applied if you would like to use binary classifer model instead of regexp in the future.

dcsan commented 4 years ago

Thanks for writing this out. I'll refer to this technique in future.

I think this would work, but in the end I just wrote my own fallback system based on context.event.text

Another issue with bottender router is how it binds events. I have my own session objects (game objects) per user and wanted to dispatch directly to those, but when I did something like:

const game = findGame(context.session.id) // game instance for that user
// router
text('hi', game.greeting)

This won't work as the routes do something to self bind. It seems the events have to be static methods, not methods on an instance.

I did try a reacty

text('hi', () => { game.greeting })

No good. Perhaps there is some combination of this.binding the methods I used for routes but I gave up and just ended up with my own routing system as a fallback. maybe you've seen this pattern before?