An utility library for Bottender and higher-order handlers
npm install bottender-composeCreates 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'),
]);
};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'),
]);
};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'),
]);
};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.
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.
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')],
])
};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.'),
});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%')],
]);
};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...
);
};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.'))
};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'),
]);
};Assigns to the displayName property on the action.
const { setDisplayName, sendText } = require('bottender-compose');
setDisplayName('sayHello', sendText('hello'));
// curry function
setDisplayName('sayHello')(sendText('hello'));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'));
};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')]);setState()resetState()typing()
sendMessage()sendText()sendAttachment()sendAudio()sendImage()sendVideo()sendFile()sendTemplate()sendButtonTemplate()sendGenericTemplate()sendListTemplate()sendOpenGraphTemplate()sendMediaTemplate()sendReceiptTemplate()sendAirlineBoardingPassTemplate()sendAirlineCheckinTemplate()sendAirlineItineraryTemplate()sendAirlineUpdateTemplate()sendSenderAction()markSeen()typingOn()typingOff()passThreadControl()passThreadControlToPageInbox()takeThreadControl()requestThreadControl()associateLabel()dissociateLabel()
sendText()sendImage()sendVideo()sendAudio()sendLocation()sendSticker()sendImagemap()sendButtonTemplate()sendConfirmTemplate()sendCarouselTemplate()sendImageCarouselTemplate()reply()replyText()replyImage()replyVideo()replyAudio()replyLocation()replySticker()replyImagemap()replyButtonTemplate()replyConfirmTemplate()replyCarouselTemplate()replyImageCarouselTemplate()push()pushText()pushImage()pushVideo()pushAudio()pushLocation()pushSticker()pushImagemap()pushButtonTemplate()pushConfirmTemplate()pushCarouselTemplate()pushImageCarouselTemplate()linkRichMenu()unlinkRichMenu()
sendText()postMessage()postEphemeral()
sendText()sendMessage()sendPhoto()sendAudio()sendDocument()sendSticker()sendVideo()sendVoice()sendVideoNote()sendMediaGroup()sendLocation()sendVenue()sendContact()sendChatAction()editMessageText()editMessageCaption()editMessageReplyMarkup()deleteMessage()editMessageLiveLocation()stopMessageLiveLocation()forwardMessageFrom()forwardMessageTo()kickChatMember()unbanChatMember()restrictChatMember()promoteChatMember()exportChatInviteLink()setChatPhoto()deleteChatPhoto()setChatTitle()setChatDescription()setChatStickerSet()deleteChatStickerSet()pinChatMessage()unpinChatMessage()leaveChat()sendInvoice()answerShippingQuery()answerPreCheckoutQuery()answerInlineQuery()sendGame()setGameScore()
sendMessage()sendText()sendPicture()sendVideo()sendFile()sendContact()sendLocation()sendURL()sendSticker()sendCarouselContent()
sendComment()sendPrivateReply()sendLike()
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}`);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' });Creates a predicate function to return true when text matches.
const { isTextMatch } = require('bottender-compose');
isTextMatch('abc')(context); // boolean
isTextMatch(/abc/)(context); // booleanCreates a predicate function to return true when payload matches.
const { isPayloadMatch } = require('bottender-compose');
isPayloadMatch('abc')(context); // boolean
isPayloadMatch(/abc/)(context); // booleanCreates 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); // booleanCreates a predicate function with not condition.
const { not, hasStateEqual } = require('bottender-compose');
const predicate = not(hasStateEqual('x', 1));
predicate(context); // booleanCreates a predicate function with and condition.
const { and, hasStateEqual } = require('bottender-compose');
const predicate = and([
isTextMatch('abc'),
hasStateEqual('x', 1))
]);
predicate(context) // booleanCreates a predicate function with or condition.
const { or, hasStateEqual } = require('bottender-compose');
const predicate = or([
isTextMatch('abc'),
hasStateEqual('x', 1))
]);
predicate(context) // booleanCreates a predicate function that always return true.
const { alwaysTrue } = require('bottender-compose');
const predicate = alwaysTrue();
predicate(context); // trueCreates a predicate function that always return false.
const { alwaysFalse } = require('bottender-compose');
const predicate = alwaysFalse();
predicate(context); // falseisMessage()isText()hasAttachment()isImage()isAudio()isVideo()isLocation()isFile()isFallback()isSticker()isLikeSticker()isQuickReply()isEcho()isPostback()isGamePlay()isOptin()isPayment()isCheckoutUpdate()isPreCheckout()isRead()isDelivery()isPayload()isPolicyEnforcement()isAppRoles()isStandby()isPassThreadControl()isTakeThreadControl()isRequestThreadControl()isRequestThreadControlFromPageInbox()isFromCustomerChatPlugin()isReferral()isBrandedCamera()
isMessage()isText()isImage()isVideo()isAudio()isLocation()isSticker()isFollow()isUnfollow()isJoin()isLeave()isPostback()isPayload()isBeacon()isAccountLink()
isMessage()isChannelsMessage()isGroupsMessage()isImMessage()isMpimMessage()isText()isInteractiveMessage()isAppUninstalled()isChannelArchive()isChannelCreated()isChannelDeleted()isChannelHistoryChanged()isChannelRename()isChannelUnarchive()isDndUpdated()isDndUpdated_user()isEmailDomainChanged()isEmojiChanged()isFileChange()isFileCommentAdded()isFileCommentDeleted()isFileCommentEdited()isFileCreated()isFileDeleted()isFilePublic()isFileShared()isFileUnshared()isGridMigrationFinished()isGridMigrationStarted()isGroupArchive()isGroupClose()isGroupHistoryChanged()isGroupOpen()isGroupRename()isGroupUnarchive()isImClose()isImCreated()isImHistoryChanged()isImOpen()isLinkShared()isMemberJoinedChannel()isMemberLeftChannel()isPinAdded()isPinRemoved()isReactionAdded()isReactionRemoved()isStarAdded()isStarRemoved()isSubteamCreated()isSubteamMembersChanged()isSubteamSelfAdded()isSubteamSelfRemoved()isSubteamUpdated()isTeamDomainChange()isTeamJoin()isTeamRename()isTokensRevoked()isUrlVerification()isUserChange()
isMessage()isText()isAudio()isDocument()isGame()isPhoto()isSticker()isVideo()isVoice()isVideoNote()isContact()isLocation()isVenue()isEditedMessage()isChannelPost()isEditedChannelPost()isInlineQuery()isChosenInlineResult()isCallbackQuery()isPayload()isShippingQuery()isPreCheckoutQuery()
isMessage()isText()isPicture()isVideo()isFile()isSticker()isContact()isURL()isLocation()isSubscribed()isUnsubscribed()isConversationStarted()isDelivered()isSeen()isFailed()
isFeed()isStatus()isStatusAdd()isStatusEdited()isPost()isPostRemove()isComment()isCommentAdd()isCommentEdited()isCommentRemove()isLike()isLikeAdd()isLikeRemove()isReaction()isReactionAdd()isReactionEdit()isReactionRemove()
MIT © Yoctol
