Home

Awesome

bottender-compose

npm Build Status coverage License: MIT

An utility library for Bottender and higher-order handlers

Installation

npm install bottender-compose

API Reference

TOC

Actions

series()

Creates a function that executes methods in series.

const { series, sendText } = require('bottender-compose');

module.exports = function App() {
  return series([
    sendText('1. First Item'),
    sendText('2. Second Item'),
    sendText('3. Third Item'),
  ]);
};

parallel()

Creates a function that executes methods in parallel.

const { parallel, sendText } = require('bottender-compose');

module.exports = function App() {
  return parallel([
    sendText('- You got one of Items'),
    sendText('- You got one of Items'),
    sendText('- You got one of Items'),
  ]);
};

random()

Creates a function that executes one of method randomly.

const { random, sendText } = require('bottender-compose');

module.exports = function App() {
  return random([
    sendText('You got a random item: A'),
    sendText('You got a random item: B'),
    sendText('You got a random item: C'),
  ]);
};

branch()

Creates a function that will process either the onTrue or the onFalse function depending upon the result of the condition predicate.
Furthermore, branch can be sued as curry function.

const { branch, sendText } = require('bottender-compose');

module.exports = function App() {
  return branch(
    context => true,
    sendText('You are the lucky one.'),
    sendText('Too bad.')
  );
};

// curry function
const trueConditionBranch = branch(context => true);

module.exports = function App() {
  return trueConditionBranch(
    sendText('You are the lucky one.'), 
    sendText('Too bad.')
  );
};

Or you can executes function on true and do nothing when received false.

branch(context => true, sendText('You are the lucky one.'));

branch works well with predicates.

condition()

Creates a function that encapsulates if/else, if/else, ... logic.

const { condition, sendText } = require('bottender-compose');

module.exports = function App() {
  return condition([
    [context => false, sendText('a')],
    [context => false, sendText('b')],
    [context => true, sendText('c')],
  ]);
};

condition works well with predicates.

match()

Creates a function that encapsulates value matching logic.

const { match, sendText } = require('bottender-compose');

module.exports = function App() {
  return match('a', [
    ['a', sendText('You got a A')],
    ['b', sendText('You got a B')],
    ['c', sendText('You got a C')],
  ]);
};

It accepts function with context argument:

module.exports = function App() {
  return match(context => context.state.answer, [
    ['a', sendText('You got a A')],
    ['b', sendText('You got a B')],
    ['c', sendText('You got a C')],
  ]);
};

// curry function
const matchAnswer = match(context => context.state.answer);

module.exports = function App() {
  return matchAnswer([
    ['a', sendText('You got a A')],
    ['b', sendText('You got a B')],
    ['c', sendText('You got a C')],
  ]);
};

To assign default action, use _ as pattern:

const { _, match, sendText } = require('bottender-compose');

module.exports = function App() {
  return match('a', [
    ['a', sendText('You got a A')],
    ['b', sendText('You got a B')],
    ['c', sendText('You got a C')],
    [_, sendText('You got something')],
  ])
};

platform()

Creates a function that will process function depending upon the platform context.

const {
  platform,
  sendGenericTemplate,
  sendImagemap,
} = require('bottender-compose');

module.exports = function App() {
  return platform({
    messenger: sendGenericTemplate(...),
    line: sendImagemap(...),
  });
};

Or you can use others key to match other platforms:

platform({
  messenger: sendGenericTemplate(...),
  line: sendImagemap(...),
  others: sendText('Unsupported.'),
});

weight()

Creates a function that randomly executes one of method by its weight.

const { weight, sendText } = require('bottender-compose');

module.exports = function App() {
  return weight([
    [0.2, sendText('20%')],
    [0.4, sendText('40%')],
    [0.4, sendText('40%')],
  ]);
};

noop()

Creates a no-op function.

const { branch, sendText, noop } = require('bottender-compose');

module.exports = function App() {
  return branch(
    context => false,
    sendText('You are the lucky one.'),
    noop() // do exactly nothing...
  );
};

repeat()

Creates a function that executes the method repeatedly.
Furthermore, repeat can be sued as curry function.

const { repeat, sendText } = require('bottender-compose');

module.exports = function App() {
  return repeat(3, sendText('This will be sent 3 times.'));
};

// curry function
const repeatFiveTimes = repeat(5);

module.exports = function App() {
  return repeatFiveTimes(sendText('This will be sent 5 times.'))
};

delay()

Creates a function that executes methods after a number of milliseconds.

const { series, delay, sendText } = require('bottender-compose');

module.exports = function App() {
  return series([
    sendText('1. First Item'),
    delay(1000),
    sendText('2. Second Item'),
    delay(1000),
    sendText('3. Third Item'),
  ]);
};

setDisplayName()

Assigns to the displayName property on the action.

const { setDisplayName, sendText } = require('bottender-compose');

setDisplayName('sayHello', sendText('hello'));

// curry function
setDisplayName('sayHello')(sendText('hello'));

attachOptions()

Attaches additional options to the action.

const { attachOptions, sendText } = require('bottender-compose');

module.exports = function App() {
  return attachOptions(
    { tag: 'ISSUE_RESOLUTION' }, 
    sendText('Issue Resolved')
  );
};

// curry function
const attachIssueResolutionTag = attachOptions({ 
  tag: 'ISSUE_RESOLUTION',
});

module.exports = function App() {
  reutnr attachIssueResolutionTag(sendText('Issue Resolved'));
};

Logger Methods

B.series([
  B.log('sending hello'),
  B.info('sending hello'),
  B.warn('sending hello'),
  B.error('sending hello'),

  B.sendText('hello'),
]);

It supports template too.

B.series([
  B.log('user: {{ session.user.id }} x: {{ state.x }}'),
  B.sendText('hello'),
]);

You can use your owner adapter for the logger:

const { log, info, warn, error } = B.createLogger({
  log: debug('log'),
  info: debug('info'),
  warn: debug('warn'),
  error: debug('error'),
});

B.series([log('sending hello'), B.sendText('hello')]);

Context Methods

Common

Messenger

LINE

Slack

Telegram

Viber

Facebook

Passing Function as Argument to Context Method

You can pass function as argument to handle time-specified or context-specified case, for example:

// Lazy execution
B.sendText(() => `Now: ${new Date()}`);

// Use event information
B.sendText(context => `Received: ${context.event.text}`);

Use Template in String

You can use context, session, event, state to access values in your template string:

B.sendText('Received: {{event.text}}');
B.sendText('State: {{state.xxx}}');

Or use props to access object values that provided as props when calling action:

B.sendText('User: {{props.name}}')(context, { name: 'Super User' });

Predicates

isTextMatch()

Creates a predicate function to return true when text matches.

const { isTextMatch } = require('bottender-compose');

isTextMatch('abc')(context); // boolean
isTextMatch(/abc/)(context); // boolean

isPayloadMatch()

Creates a predicate function to return true when payload matches.

const { isPayloadMatch } = require('bottender-compose');

isPayloadMatch('abc')(context); // boolean
isPayloadMatch(/abc/)(context); // boolean

hasStateEqual()

Creates a predicate function to return true when state matches.

const { hasStateEqual } = require('bottender-compose');

hasStateEqual('x', 1)(context); // boolean
hasStateEqual('x.y.z', 1)(context); // boolean
hasStateEqual('x', { y: { z: 1 } })(context); // boolean

not()

Creates a predicate function with not condition.

const { not, hasStateEqual } = require('bottender-compose');

const predicate = not(hasStateEqual('x', 1));

predicate(context); // boolean

and()

Creates a predicate function with and condition.

const { and, hasStateEqual } = require('bottender-compose');

const predicate = and([
  isTextMatch('abc'),
  hasStateEqual('x', 1))
]);

predicate(context) // boolean

or()

Creates a predicate function with or condition.

const { or, hasStateEqual } = require('bottender-compose');

const predicate = or([
  isTextMatch('abc'),
  hasStateEqual('x', 1))
]);

predicate(context) // boolean

alwaysTrue

Creates a predicate function that always return true.

const { alwaysTrue } = require('bottender-compose');

const predicate = alwaysTrue();

predicate(context); // true

alwaysFalse

Creates a predicate function that always return false.

const { alwaysFalse } = require('bottender-compose');

const predicate = alwaysFalse();

predicate(context); // false

Event Predicates

Messenger

LINE

Slack

Telegram

Viber

Facebook

License

MIT © Yoctol