├── website ├── .nvmrc ├── static │ ├── CNAME │ ├── img │ │ ├── favicon.ico │ │ ├── og-image.png │ │ ├── users │ │ │ ├── abl.png │ │ │ ├── lol.png │ │ │ ├── todoBot.jpg │ │ │ └── agricolaScore.png │ │ ├── favicon-192x192.png │ │ ├── favicon-32x32.png │ │ ├── og-image-holidays.png │ │ ├── blog │ │ │ ├── 2018-08-03 │ │ │ │ ├── console-bot.png │ │ │ │ └── console-debug.jpg │ │ │ └── 2017-10-31 │ │ │ │ ├── logo-600x600.png │ │ │ │ └── init-screenshot.png │ │ ├── hero_background.svg │ │ ├── element_modular.svg │ │ ├── element_flexible.svg │ │ ├── element_morden.svg │ │ └── element_anywhere.svg │ ├── js │ │ └── sw.js │ └── css │ │ └── code-blocks-buttons.css ├── .gitignore ├── tsconfig.json ├── versions.json ├── CustomFields.d.ts ├── src │ ├── pages │ │ └── versions.tsx │ ├── css │ │ └── customTheme.css │ └── components │ │ └── Showcase.tsx ├── docs │ ├── the-basics-context-event.md │ ├── api-console-context.md │ ├── channel-whatsapp-sending-messages.md │ ├── channel-slack-handling-events.md │ ├── channel-viber-handling-events.md │ └── channel-line-errors.md └── versioned_docs │ ├── version-1.4 │ ├── the-basics-context-event.md │ ├── api-console-context.md │ ├── channel-whatsapp-sending-messages.md │ ├── channel-slack-handling-events.md │ └── channel-viber-handling-events.md │ ├── version-1.5 │ ├── the-basics-context-event.md │ ├── api-console-context.md │ ├── channel-whatsapp-sending-messages.md │ ├── channel-slack-handling-events.md │ └── channel-viber-handling-events.md │ ├── version-1.0.5 │ ├── the-basics-context-event.md │ ├── api-console-context.md │ ├── channel-whatsapp-sending-messages.md │ ├── channel-slack-handling-events.md │ └── channel-viber-handling-events.md │ ├── version-1.1.0 │ ├── the-basics-context-event.md │ ├── api-console-context.md │ └── channel-whatsapp-sending-messages.md │ ├── version-1.2.0 │ ├── the-basics-context-event.md │ ├── api-console-context.md │ └── channel-whatsapp-sending-messages.md │ ├── version-1.3.0 │ ├── the-basics-context-event.md │ ├── api-console-context.md │ └── channel-whatsapp-sending-messages.md │ ├── version-1.3.1 │ ├── the-basics-context-event.md │ ├── api-console-context.md │ ├── channel-whatsapp-sending-messages.md │ ├── channel-slack-handling-events.md │ └── channel-viber-handling-events.md │ └── version-0.15.17 │ └── APIReference-ConsoleContext.md ├── examples ├── session-file │ ├── .gitignore │ ├── index.js │ ├── package.json │ ├── bottender.config.js │ └── README.md ├── browser-only │ ├── .env │ ├── public │ │ ├── favicon.ico │ │ └── manifest.json │ ├── src │ │ ├── BottenderApp.js │ │ ├── index.js │ │ ├── BrowserBot │ │ │ ├── BrowserContext.js │ │ │ ├── index.js │ │ │ └── BrowserEvent.js │ │ ├── App.css │ │ └── index.css │ ├── .gitignore │ ├── package.json │ └── README.md ├── telegram-poll │ ├── .env │ ├── package.json │ └── bottender.config.js ├── viber-hello-world │ ├── .env │ ├── index.js │ ├── package.json │ └── bottender.config.js ├── telegram-hello-world │ ├── .env │ ├── index.js │ ├── package.json │ └── bottender.config.js ├── telegram-inline-query │ ├── .env │ ├── package.json │ └── bottender.config.js ├── .gitignore ├── telegram-inline-keyboard │ ├── .env │ ├── package.json │ └── bottender.config.js ├── telegram-reply-keyboard │ ├── .env │ ├── package.json │ └── bottender.config.js ├── line-hello-world │ ├── .env │ ├── index.js │ ├── package.json │ └── bottender.config.js ├── line-rich-menu │ ├── .env │ ├── rich_menu.jpg │ ├── index.js │ ├── package.json │ └── bottender.config.js ├── slack-home-tab │ ├── .env │ ├── package.json │ └── bottender.config.js ├── slack-hello-world │ ├── .env │ ├── index.js │ ├── package.json │ └── bottender.config.js ├── slack-modal-form │ ├── .env │ ├── package.json │ └── bottender.config.js ├── slack-modal-on-home │ ├── .env │ ├── package.json │ └── bottender.config.js ├── slack-modal-push │ ├── .env │ ├── package.json │ └── bottender.config.js ├── slack-modal-update │ ├── .env │ ├── package.json │ └── bottender.config.js ├── slack-slash-command │ ├── .env │ ├── package.json │ ├── bottender.config.js │ └── index.js ├── line-liff-v1 │ ├── .env │ ├── index.js │ ├── bottender.config.js │ ├── package.json │ └── server.js ├── line-liff-v2 │ ├── .env │ ├── index.js │ ├── bottender.config.js │ └── package.json ├── slack-post-ephemeral │ ├── .env │ ├── index.js │ ├── package.json │ └── bottender.config.js ├── slack-update-and-delete │ ├── .env │ ├── package.json │ ├── bottender.config.js │ └── index.js ├── telegram-game │ ├── .env │ ├── bottender.config.js │ ├── gameSession.js │ └── package.json ├── with-luis.ai │ ├── .env │ ├── package.json │ ├── index.js │ └── README.md ├── with-qna-maker │ ├── .env │ ├── package.json │ ├── index.js │ └── README.md ├── slack-interactive-message │ ├── .env │ ├── package.json │ └── bottender.config.js ├── custom-server-express-typescript │ ├── index.js │ ├── .env │ ├── src │ │ ├── index.ts │ │ └── server.ts │ ├── package.json │ └── tsconfig.json ├── with-dialogflow │ ├── .env │ ├── package.json │ ├── index.js │ └── README.md ├── todos │ ├── bottender.config.js │ ├── package.json │ └── index.js ├── whatsapp-hello-world │ ├── .env │ ├── index.js │ ├── package.json │ └── bottender.config.js ├── with-aws-lambda │ ├── .env │ ├── index.js │ ├── package.json │ ├── serverless.yml │ └── server.js ├── custom-server-koa │ ├── index.js │ ├── .env │ └── package.json ├── messenger-batch │ ├── index.js │ ├── .env │ ├── package.json │ └── bottender.config.js ├── session-memory │ ├── index.js │ ├── package.json │ ├── bottender.config.js │ └── README.md ├── session-mongo │ ├── index.js │ ├── package.json │ ├── bottender.config.js │ └── README.md ├── session-redis │ ├── index.js │ ├── package.json │ ├── bottender.config.js │ └── README.md ├── .eslintrc.js ├── custom-server-express │ ├── index.js │ ├── .env │ └── package.json ├── custom-server-restify │ ├── index.js │ ├── .env │ ├── package.json │ └── server.js ├── line-multiple-channels │ ├── index.js │ ├── .env │ └── package.json ├── line-rich-menu-submenu │ ├── main_menu.jpg │ ├── submenu_A.jpg │ ├── submenu_B.jpg │ ├── .env │ ├── package.json │ ├── bottender.config.js │ └── index.js ├── messenger-hello-world │ ├── index.js │ ├── .env │ ├── package.json │ └── bottender.config.js ├── messenger-multi-pages │ ├── index.js │ ├── package.json │ ├── .env │ └── bottender.config.js ├── with-gcp-cloud-function │ ├── .env │ ├── index.js │ ├── package.json │ └── server.js ├── custom-connector-facebook │ ├── index.js │ ├── .env │ ├── package.json │ └── bottender.config.js ├── line-notify │ ├── .env │ ├── index.js │ ├── notify.html │ ├── bottender.config.js │ └── package.json ├── messenger-batch-multi-pages │ ├── index.js │ ├── .env │ └── package.json ├── messenger-persistent-menu │ ├── index.js │ ├── .env │ └── package.json ├── with-state │ ├── bottender.config.js │ ├── package.json │ ├── index.js │ └── README.md ├── messenger-account-linking │ ├── public │ │ ├── favicon.ico │ │ └── images │ │ │ └── check.svg │ ├── .env │ ├── views │ │ ├── index.jade │ │ ├── error.jade │ │ ├── create-account-success.jade │ │ └── layout.jade │ ├── package.json │ ├── models │ │ └── User.js │ ├── routes │ │ └── index.js │ └── bottender.config.js ├── messenger-typing │ ├── .env │ ├── package.json │ ├── index.js │ └── bottender.config.js ├── messenger-built-in-nlp │ ├── .env │ ├── package.json │ ├── bottender.config.js │ └── index.js ├── messenger-handover │ ├── .env │ ├── package.json │ ├── index.js │ └── bottender.config.js ├── multiple-channels │ ├── index.js │ ├── package.json │ └── .env ├── echo-bot │ ├── index.js │ ├── package.json │ └── README.md ├── messenger-persona │ ├── .env │ ├── package.json │ ├── bottender.config.js │ └── index.js ├── messenger-one-time-notification │ ├── package.json │ ├── .env │ └── bottender.config.js └── with-rasa │ ├── package.json │ ├── index.js │ └── README.md ├── types ├── envinfo.d.ts ├── ngrok.d.ts ├── nodemon.d.ts ├── enquirer.d.ts ├── urlencoded-body-parser.d.ts └── validate-npm-package-name.d.ts ├── packages ├── bottender │ ├── router.d.ts │ ├── src │ │ ├── test-utils │ │ │ ├── index.ts │ │ │ ├── __tests__ │ │ │ │ └── index.spec.ts │ │ │ └── SimulatedContext.ts │ │ ├── session │ │ │ ├── Session.ts │ │ │ ├── SessionStore.ts │ │ │ ├── __tests__ │ │ │ │ ├── MemorySessionStore.spec.ts │ │ │ │ └── RedisSessionStore.spec.ts │ │ │ ├── RedisSessionStore.ts │ │ │ └── MemorySessionStore.ts │ │ ├── console │ │ │ └── ConsoleClient.ts │ │ ├── cli │ │ │ └── providers │ │ │ │ ├── line │ │ │ │ ├── help.ts │ │ │ │ ├── index.ts │ │ │ │ └── __tests__ │ │ │ │ │ └── index.spec.ts │ │ │ │ ├── sh │ │ │ │ ├── utils │ │ │ │ │ ├── argCommon.ts │ │ │ │ │ ├── getArgs.ts │ │ │ │ │ ├── getSubArgs.ts │ │ │ │ │ └── __tests__ │ │ │ │ │ │ └── getArgs.spec.ts │ │ │ │ ├── __tests__ │ │ │ │ │ ├── help.spec.ts │ │ │ │ │ └── index.spec.ts │ │ │ │ ├── index.ts │ │ │ │ ├── init.ts │ │ │ │ └── help.ts │ │ │ │ ├── viber │ │ │ │ ├── index.ts │ │ │ │ ├── __tests__ │ │ │ │ │ ├── help.spec.ts │ │ │ │ │ └── index.spec.ts │ │ │ │ └── help.ts │ │ │ │ ├── telegram │ │ │ │ ├── index.ts │ │ │ │ ├── __tests__ │ │ │ │ │ ├── help.spec.ts │ │ │ │ │ └── index.spec.ts │ │ │ │ └── help.ts │ │ │ │ ├── index.ts │ │ │ │ └── messenger │ │ │ │ ├── __tests__ │ │ │ │ ├── help.spec.ts │ │ │ │ ├── profile.spec.ts │ │ │ │ └── index.spec.ts │ │ │ │ ├── index.ts │ │ │ │ └── help.ts │ │ ├── context │ │ │ └── Event.ts │ │ ├── server │ │ │ └── DevServer.ts │ │ ├── cache │ │ │ └── CacheStore.ts │ │ ├── bottender.ts │ │ ├── line │ │ │ ├── __tests__ │ │ │ │ └── LineBot.spec.ts │ │ │ └── LineBot.ts │ │ ├── whatsapp │ │ │ ├── __tests__ │ │ │ │ └── WhatsappBot.spec.ts │ │ │ └── WhatsappBot.ts │ │ ├── viber │ │ │ ├── __tests__ │ │ │ │ └── ViberBot.spec.ts │ │ │ └── ViberBot.ts │ │ ├── withProps.ts │ │ ├── shared │ │ │ ├── log.ts │ │ │ ├── getBottenderConfig.ts │ │ │ └── __tests__ │ │ │ │ └── log.spec.ts │ │ ├── browser.ts │ │ ├── __tests__ │ │ │ ├── withProps.spec.ts │ │ │ └── browser.spec.ts │ │ ├── messenger │ │ │ ├── __tests__ │ │ │ │ └── MessengerBot.spec.ts │ │ │ └── MessengerBot.ts │ │ └── bot │ │ │ └── Connector.ts │ ├── router.js │ ├── test-utils.js │ ├── bin │ │ └── cli.js │ └── tsconfig.json ├── create-bottender-app │ ├── template │ │ ├── .eslintignore │ │ ├── index.js │ │ ├── src │ │ │ └── index.test.js │ │ ├── jest.config.js │ │ ├── .env.example │ │ └── .eslintrc.js │ ├── template-typescript │ │ ├── .eslintignore │ │ ├── index.js │ │ ├── src │ │ │ └── index.test.ts │ │ ├── .env.example │ │ ├── jest.config.js │ │ ├── tsconfig.json │ │ └── .eslintrc.js │ ├── bin │ │ └── cli.js │ ├── tsconfig.json │ ├── src │ │ ├── types.ts │ │ └── shared │ │ │ └── log.ts │ └── package.json ├── bottender-handlers │ ├── src │ │ ├── SlackHandler.ts │ │ ├── middleware.ts │ │ ├── __tests__ │ │ │ ├── SlackHandler.spec.ts │ │ │ └── index.spec.ts │ │ └── index.ts │ ├── tsconfig.json │ └── package.json ├── bottender-express │ ├── src │ │ ├── types.ts │ │ ├── index.ts │ │ ├── __tests__ │ │ │ └── index.spec.ts │ │ └── createServer.ts │ ├── tsconfig.json │ └── package.json ├── bottender-facebook │ ├── tsconfig.json │ ├── src │ │ ├── index.ts │ │ └── __tests__ │ │ │ └── index.spec.ts │ └── package.json ├── bottender-luis │ ├── tsconfig.json │ ├── package.json │ └── src │ │ └── types.ts ├── bottender-rasa │ ├── tsconfig.json │ ├── src │ │ └── types.ts │ └── package.json ├── bottender-dialogflow │ ├── tsconfig.json │ └── package.json └── bottender-qna-maker │ ├── tsconfig.json │ ├── package.json │ └── src │ └── types.ts ├── __mocks__ ├── ioredis.ts ├── messaging-api-line.ts └── fs.ts ├── .husky └── pre-commit ├── prettier.config.js ├── codecov.yml ├── .gitpod.yml ├── .github └── ISSUE_TEMPLATE │ ├── custom.md │ ├── bug_report.md │ └── feature_request.md ├── .eslintignore ├── tsconfig.test.json ├── tsconfig.json ├── .editorconfig ├── lint-staged.config.js ├── lerna.json ├── test └── setupJest.js ├── .gitignore ├── tsconfig.build.json ├── crowdin.yaml ├── tsconfig.base.json └── jest.config.js /website/.nvmrc: -------------------------------------------------------------------------------- 1 | lts/* 2 | -------------------------------------------------------------------------------- /website/static/CNAME: -------------------------------------------------------------------------------- 1 | bottender.js.org 2 | -------------------------------------------------------------------------------- /examples/session-file/.gitignore: -------------------------------------------------------------------------------- 1 | .sessions 2 | -------------------------------------------------------------------------------- /types/envinfo.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'envinfo'; 2 | -------------------------------------------------------------------------------- /types/ngrok.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'ngrok'; 2 | -------------------------------------------------------------------------------- /types/nodemon.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'nodemon'; 2 | -------------------------------------------------------------------------------- /types/enquirer.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'enquirer'; 2 | -------------------------------------------------------------------------------- /examples/browser-only/.env: -------------------------------------------------------------------------------- 1 | SKIP_PREFLIGHT_CHECK=true 2 | -------------------------------------------------------------------------------- /examples/telegram-poll/.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_ACCESS_TOKEN= 2 | -------------------------------------------------------------------------------- /examples/viber-hello-world/.env: -------------------------------------------------------------------------------- 1 | VIBER_ACCESS_TOKEN= 2 | -------------------------------------------------------------------------------- /examples/telegram-hello-world/.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_ACCESS_TOKEN= 2 | -------------------------------------------------------------------------------- /examples/telegram-inline-query/.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_ACCESS_TOKEN= 2 | -------------------------------------------------------------------------------- /examples/.gitignore: -------------------------------------------------------------------------------- 1 | yarn.lock 2 | package-lock.json 3 | !.env 4 | -------------------------------------------------------------------------------- /examples/telegram-inline-keyboard/.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_ACCESS_TOKEN= 2 | -------------------------------------------------------------------------------- /examples/telegram-reply-keyboard/.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_ACCESS_TOKEN= 2 | -------------------------------------------------------------------------------- /packages/bottender/router.d.ts: -------------------------------------------------------------------------------- 1 | export * from './dist/router'; 2 | -------------------------------------------------------------------------------- /__mocks__/ioredis.ts: -------------------------------------------------------------------------------- 1 | module.exports = jest.genMockFromModule('ioredis'); 2 | -------------------------------------------------------------------------------- /examples/line-hello-world/.env: -------------------------------------------------------------------------------- 1 | LINE_ACCESS_TOKEN= 2 | LINE_CHANNEL_SECRET= 3 | -------------------------------------------------------------------------------- /examples/line-rich-menu/.env: -------------------------------------------------------------------------------- 1 | LINE_ACCESS_TOKEN= 2 | LINE_CHANNEL_SECRET= 3 | -------------------------------------------------------------------------------- /examples/slack-home-tab/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /types/urlencoded-body-parser.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'urlencoded-body-parser'; 2 | -------------------------------------------------------------------------------- /examples/slack-hello-world/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /examples/slack-modal-form/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /examples/slack-modal-on-home/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /examples/slack-modal-push/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /examples/slack-modal-update/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /examples/slack-slash-command/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /examples/line-liff-v1/.env: -------------------------------------------------------------------------------- 1 | LINE_ACCESS_TOKEN= 2 | LINE_CHANNEL_SECRET= 3 | LINE_LIFF_ID= -------------------------------------------------------------------------------- /examples/line-liff-v2/.env: -------------------------------------------------------------------------------- 1 | LINE_ACCESS_TOKEN= 2 | LINE_CHANNEL_SECRET= 3 | LINE_LIFF_ID= -------------------------------------------------------------------------------- /examples/slack-post-ephemeral/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /examples/slack-update-and-delete/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /examples/telegram-game/.env: -------------------------------------------------------------------------------- 1 | TELEGRAM_ACCESS_TOKEN= 2 | GAME_NAME= 3 | ROOT_PATH= 4 | -------------------------------------------------------------------------------- /examples/with-luis.ai/.env: -------------------------------------------------------------------------------- 1 | LUIS_APP_ID= 2 | LUIS_APP_KEY= 3 | LUIS_APP_ENDPOINT= 4 | -------------------------------------------------------------------------------- /examples/with-qna-maker/.env: -------------------------------------------------------------------------------- 1 | RESOURCE_NAME= 2 | KNOWLEDGE_BASE_ID= 3 | ENDPOINT_KEY= 4 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src'); 2 | -------------------------------------------------------------------------------- /types/validate-npm-package-name.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'validate-npm-package-name'; 2 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | . "$(dirname "$0")/_/husky.sh" 3 | 4 | npx lint-staged 5 | -------------------------------------------------------------------------------- /examples/slack-interactive-message/.env: -------------------------------------------------------------------------------- 1 | SLACK_ACCESS_TOKEN= 2 | SLACK_SIGNING_SECRET= 3 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template-typescript/.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | -------------------------------------------------------------------------------- /examples/custom-server-express-typescript/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./dist').default; 2 | -------------------------------------------------------------------------------- /examples/with-dialogflow/.env: -------------------------------------------------------------------------------- 1 | GOOGLE_APPLICATION_CREDENTIALS= 2 | GOOGLE_APPLICATION_PROJECT_ID= 3 | -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | trailingComma: 'es5', 3 | singleQuote: true, 4 | }; 5 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | # production 2 | /build 3 | 4 | # generated files 5 | .docusaurus 6 | .cache-loader 7 | -------------------------------------------------------------------------------- /packages/bottender/src/test-utils/index.ts: -------------------------------------------------------------------------------- 1 | export { default as ContextSimulator } from './ContextSimulator'; 2 | -------------------------------------------------------------------------------- /website/static/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/favicon.ico -------------------------------------------------------------------------------- /examples/todos/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | initialState: { 3 | todos: [], 4 | }, 5 | }; 6 | -------------------------------------------------------------------------------- /packages/bottender/src/session/Session.ts: -------------------------------------------------------------------------------- 1 | type Session = Record; 2 | 3 | export default Session; 4 | -------------------------------------------------------------------------------- /website/static/img/og-image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/og-image.png -------------------------------------------------------------------------------- /website/static/img/users/abl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/users/abl.png -------------------------------------------------------------------------------- /website/static/img/users/lol.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/users/lol.png -------------------------------------------------------------------------------- /website/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/docusaurus/tsconfig.json", 3 | "include": ["src/"] 4 | } 5 | -------------------------------------------------------------------------------- /examples/whatsapp-hello-world/.env: -------------------------------------------------------------------------------- 1 | WHATSAPP_ACCOUNT_SID= 2 | WHATSAPP_AUTH_TOKEN= 3 | WHATSAPP_PHONE_NUMBER=whatsapp:+ 4 | -------------------------------------------------------------------------------- /packages/bottender/router.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | module.exports = require('./dist/router'); 3 | -------------------------------------------------------------------------------- /examples/line-rich-menu/rich_menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/examples/line-rich-menu/rich_menu.jpg -------------------------------------------------------------------------------- /packages/bottender/src/console/ConsoleClient.ts: -------------------------------------------------------------------------------- 1 | export type ConsoleClient = { 2 | sendText(text: string): void; 3 | }; 4 | -------------------------------------------------------------------------------- /website/static/img/favicon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/favicon-192x192.png -------------------------------------------------------------------------------- /website/static/img/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/favicon-32x32.png -------------------------------------------------------------------------------- /website/static/img/users/todoBot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/users/todoBot.jpg -------------------------------------------------------------------------------- /examples/browser-only/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/examples/browser-only/public/favicon.ico -------------------------------------------------------------------------------- /examples/session-file/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/with-aws-lambda/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_ACCESS_TOKEN= 2 | MESSENGER_VERIFY_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | -------------------------------------------------------------------------------- /packages/bottender/test-utils.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | module.exports = require('./dist/test-utils'); 3 | -------------------------------------------------------------------------------- /website/static/img/og-image-holidays.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/og-image-holidays.png -------------------------------------------------------------------------------- /examples/custom-server-koa/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/line-hello-world/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/messenger-batch/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/session-memory/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/session-mongo/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/session-redis/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/slack-hello-world/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/with-aws-lambda/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /website/static/img/users/agricolaScore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/users/agricolaScore.png -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: '75%' 6 | patch: 7 | enabled: false 8 | -------------------------------------------------------------------------------- /examples/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | rules: { 3 | 'import/no-unresolved': 'off', 4 | 'no-console': 'off', 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /examples/custom-server-express/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/custom-server-restify/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/line-multiple-channels/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/line-rich-menu-submenu/main_menu.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/examples/line-rich-menu-submenu/main_menu.jpg -------------------------------------------------------------------------------- /examples/line-rich-menu-submenu/submenu_A.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/examples/line-rich-menu-submenu/submenu_A.jpg -------------------------------------------------------------------------------- /examples/line-rich-menu-submenu/submenu_B.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/examples/line-rich-menu-submenu/submenu_B.jpg -------------------------------------------------------------------------------- /examples/messenger-hello-world/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/messenger-multi-pages/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/telegram-hello-world/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/with-gcp-cloud-function/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_ACCESS_TOKEN= 2 | MESSENGER_VERIFY_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | -------------------------------------------------------------------------------- /packages/bottender/bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // eslint-disable-next-line import/no-unresolved 4 | require('../dist/cli'); 5 | -------------------------------------------------------------------------------- /examples/custom-connector-facebook/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/line-notify/.env: -------------------------------------------------------------------------------- 1 | LINE_ACCESS_TOKEN= 2 | LINE_CHANNEL_SECRET= 3 | 4 | LINE_NOTIFY_CLIENT_ID= 5 | LINE_NOTIFY_CLIENT_SECRET= 6 | ROOT_PATH= -------------------------------------------------------------------------------- /examples/messenger-batch-multi-pages/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/messenger-persistent-menu/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/with-gcp-cloud-function/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/with-state/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | initialState: { 3 | asking: false, 4 | nickname: null, 5 | }, 6 | }; 7 | -------------------------------------------------------------------------------- /packages/bottender-handlers/src/SlackHandler.ts: -------------------------------------------------------------------------------- 1 | import Handler from './Handler'; 2 | 3 | export default class SlackHandler extends Handler {} 4 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template-typescript/index.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable import/no-unresolved */ 2 | module.exports = require('./dist').default; 3 | -------------------------------------------------------------------------------- /website/static/img/blog/2018-08-03/console-bot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/blog/2018-08-03/console-bot.png -------------------------------------------------------------------------------- /examples/line-rich-menu-submenu/.env: -------------------------------------------------------------------------------- 1 | LINE_ACCESS_TOKEN= 2 | LINE_CHANNEL_SECRET= 3 | MAIN_RICH_MENU_ID= 4 | SUB_RICH_MENU_A_ID= 5 | SUB_RICH_MENU_B_ID= 6 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/examples/messenger-account-linking/public/favicon.ico -------------------------------------------------------------------------------- /examples/messenger-batch/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | -------------------------------------------------------------------------------- /examples/messenger-typing/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | -------------------------------------------------------------------------------- /website/static/img/blog/2017-10-31/logo-600x600.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/blog/2017-10-31/logo-600x600.png -------------------------------------------------------------------------------- /website/static/img/blog/2018-08-03/console-debug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/blog/2018-08-03/console-debug.jpg -------------------------------------------------------------------------------- /examples/custom-connector-facebook/.env: -------------------------------------------------------------------------------- 1 | FACEBOOK_PAGE_ID= 2 | FACEBOOK_ACCESS_TOKEN= 3 | FACEBOOK_APP_ID= 4 | FACEBOOK_APP_SECRET= 5 | FACEBOOK_VERIFY_TOKEN= 6 | -------------------------------------------------------------------------------- /examples/line-rich-menu/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText(`User have clicked ${context.event.text}`); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/messenger-built-in-nlp/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | -------------------------------------------------------------------------------- /examples/messenger-handover/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | -------------------------------------------------------------------------------- /examples/messenger-hello-world/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | -------------------------------------------------------------------------------- /packages/bottender-express/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Bot = any; // FIXME: import from bottender 2 | 3 | export type RouteConfig = { 4 | path?: string; 5 | }; 6 | -------------------------------------------------------------------------------- /website/static/img/blog/2017-10-31/init-screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Yoctol/bottender/HEAD/website/static/img/blog/2017-10-31/init-screenshot.png -------------------------------------------------------------------------------- /website/versions.json: -------------------------------------------------------------------------------- 1 | [ 2 | "1.5", 3 | "1.4", 4 | "1.3.1", 5 | "1.3.0", 6 | "1.2.0", 7 | "1.1.0", 8 | "1.0.5", 9 | "0.15.17" 10 | ] 11 | -------------------------------------------------------------------------------- /examples/messenger-persistent-menu/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | -------------------------------------------------------------------------------- /examples/slack-post-ephemeral/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.chat.postEphemeral('This is a ephemeral message.'); 3 | }; 4 | -------------------------------------------------------------------------------- /website/CustomFields.d.ts: -------------------------------------------------------------------------------- 1 | export type CustomFields = { 2 | gaGtag: boolean; 3 | disableHeaderTitle: boolean; 4 | translationRecruitingLink: string; 5 | }; 6 | -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | tasks: 2 | - init: yarn 3 | - init: cd website && npm install 4 | command: npm run start 5 | ports: 6 | - port: 3000 7 | onOpen: open-preview 8 | -------------------------------------------------------------------------------- /examples/multiple-channels/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText(`Hello World. Platform: ${context.platform}`); 3 | }; 4 | -------------------------------------------------------------------------------- /examples/echo-bot/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | if (context.event.isText) { 3 | await context.sendText(context.event.text); 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/custom.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Custom issue template 3 | about: Describe this issue template's purpose here. 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | -------------------------------------------------------------------------------- /examples/line-notify/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | const url = `${process.env.ROOT_PATH}/notify/new`; 3 | await context.sendText(url); 4 | }; 5 | -------------------------------------------------------------------------------- /examples/viber-hello-world/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | if (context.event.isMessage) { 3 | await context.sendText('Hello World'); 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /examples/whatsapp-hello-world/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | if (context.event.isText) { 3 | await context.sendText('Hello World'); 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /packages/create-bottender-app/bin/cli.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | // eslint-disable-next-line import/no-unresolved 4 | const init = require('../dist').default; 5 | 6 | init(); 7 | -------------------------------------------------------------------------------- /examples/line-liff-v1/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | const liffUrl = `line://app/${process.env.LINE_LIFF_ID}`; 3 | await context.sendText(liffUrl); 4 | }; 5 | -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | !.eslintrc.js 4 | !.babelrc.js 5 | examples/browser-only 6 | packages/create-bottender-app/template 7 | packages/create-bottender-app/template-typescript 8 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/.env: -------------------------------------------------------------------------------- 1 | SERVER_ORIGIN= 2 | 3 | MESSENGER_PAGE_ID= 4 | MESSENGER_ACCESS_TOKEN= 5 | MESSENGER_APP_ID= 6 | MESSENGER_APP_SECRET= 7 | MESSENGER_VERIFY_TOKEN= 8 | 9 | -------------------------------------------------------------------------------- /examples/line-liff-v2/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | const liffUrl = `https://liff.line.me/${process.env.LINE_LIFF_ID}`; 3 | await context.sendText(liffUrl); 4 | }; 5 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/line/help.ts: -------------------------------------------------------------------------------- 1 | const help = (): void => { 2 | console.log(` 3 | bottender line [options] 4 | `); 5 | }; 6 | 7 | export default help; 8 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/line/index.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'LINE', 3 | subcommands: new Set([]), 4 | get help() { 5 | return require('./help').default; 6 | }, 7 | }; 8 | -------------------------------------------------------------------------------- /examples/browser-only/src/BottenderApp.js: -------------------------------------------------------------------------------- 1 | export default async function App(context) { 2 | await context.sendText('Hello World'); 3 | await context.sendText(`Received: ${context.event.text}`); 4 | } 5 | -------------------------------------------------------------------------------- /examples/messenger-persona/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | PERSONA_1= 8 | PERSONA_2= 9 | 10 | -------------------------------------------------------------------------------- /examples/todos/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/echo-bot/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/line-multiple-channels/.env: -------------------------------------------------------------------------------- 1 | CHANNEL_1_CHANNEL_ID= 2 | CHANNEL_1_ACCESS_TOKEN= 3 | CHANNEL_1_CHANNEL_SECRET= 4 | 5 | CHANNEL_2_CHANNEL_ID= 6 | CHANNEL_2_ACCESS_TOKEN= 7 | CHANNEL_2_CHANNEL_SECRET= 8 | -------------------------------------------------------------------------------- /examples/session-file/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/with-state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.test.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base", 3 | "compilerOptions": { 4 | "noEmit": true, 5 | "lib": ["es2017", "esnext.asynciterable"], 6 | "types": ["node", "jest"] 7 | }, 8 | } 9 | -------------------------------------------------------------------------------- /examples/line-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/line-rich-menu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/messenger-persona/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/messenger-typing/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/multiple-channels/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/session-memory/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/session-mongo/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/session-redis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/slack-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/telegram-poll/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/viber-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/line/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import line from '..'; 2 | 3 | describe('LINE cli', () => { 4 | it('should exist', () => { 5 | expect(line).toBeDefined(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template/src/index.test.js: -------------------------------------------------------------------------------- 1 | const App = require('.'); 2 | 3 | describe('index.js', () => { 4 | it('should be defined', () => { 5 | expect(App).toBeDefined(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /examples/line-multiple-channels/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "next" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/line-rich-menu-submenu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/messenger-built-in-nlp/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/messenger-handover/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/messenger-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/messenger-multi-pages/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/slack-home-tab/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "1.5.1-alpha.4" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/slack-post-ephemeral/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/slack-slash-command/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/telegram-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/telegram-inline-query/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/whatsapp-hello-world/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "next" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template-typescript/src/index.test.ts: -------------------------------------------------------------------------------- 1 | import App from '.'; 2 | 3 | describe('index.js', () => { 4 | it('should be defined', () => { 5 | expect(App).toBeDefined(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /examples/messenger-persistent-menu/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/session-file/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | session: { 3 | driver: 'file', 4 | stores: { 5 | file: { 6 | dirname: '.sessions', 7 | }, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/slack-interactive-message/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/slack-modal-form/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "1.5.1-alpha.4" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/slack-modal-on-home/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "1.5.1-alpha.4" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/slack-modal-push/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "1.5.1-alpha.4" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/slack-modal-update/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "1.5.1-alpha.4" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/slack-update-and-delete/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/telegram-inline-keyboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /examples/telegram-reply-keyboard/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/src/pages/versions.tsx: -------------------------------------------------------------------------------- 1 | import Layout from '@theme/Layout'; 2 | import React, { FC } from 'react'; 3 | 4 | const Versions: FC = () => { 5 | return Versions; 6 | }; 7 | 8 | export default Versions; 9 | -------------------------------------------------------------------------------- /examples/browser-only/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import App from './App'; 5 | import './index.css'; 6 | 7 | ReactDOM.render(, document.getElementById('root')); 8 | -------------------------------------------------------------------------------- /examples/messenger-one-time-notification/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /website/static/js/sw.js: -------------------------------------------------------------------------------- 1 | if ('serviceWorker' in navigator) { 2 | navigator.serviceWorker.getRegistrations().then((registrations) => { 3 | registrations.forEach((registration) => registration.unregister()); 4 | }); 5 | } 6 | -------------------------------------------------------------------------------- /packages/bottender-express/src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as createServer } from './createServer'; 2 | export { default as createMiddleware } from './createMiddleware'; 3 | export { default as registerRoutes } from './registerRoutes'; 4 | -------------------------------------------------------------------------------- /packages/bottender/src/test-utils/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import * as utils from '..'; 2 | 3 | describe('micro', () => { 4 | it('export public apis', () => { 5 | expect(utils.ContextSimulator).toBeDefined(); 6 | }); 7 | }); 8 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/sh/utils/argCommon.ts: -------------------------------------------------------------------------------- 1 | const ARG_COMMON = { 2 | '--help': Boolean, 3 | '-h': '--help', 4 | 5 | '--version': Boolean, 6 | '-v': '--version', 7 | }; 8 | 9 | export default () => ARG_COMMON; 10 | -------------------------------------------------------------------------------- /examples/messenger-batch-multi-pages/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_ACCESS_TOKEN= 2 | MESSENGER_APP_ID= 3 | MESSENGER_APP_SECRET= 4 | MESSENGER_VERIFY_TOKEN= 5 | 6 | PAGE_1_PAGE_ID= 7 | PAGE_1_ACCESS_TOKEN= 8 | 9 | PAGE_2_PAGE_ID= 10 | PAGE_2_ACCESS_TOKEN= 11 | -------------------------------------------------------------------------------- /examples/messenger-batch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest", 8 | "messenger-batch": "^0.3.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/messenger-typing/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | await context.sendText('Hello World'); 3 | await context.sendText('Hello World'); 4 | await context.sendText('Hello World~~~~~~~~~~', { delay: 2000 }); 5 | }; 6 | -------------------------------------------------------------------------------- /packages/bottender-express/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["**/__tests__"] 9 | } 10 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true 4 | }, 5 | "files": [], 6 | "include": [], 7 | "references": [ 8 | { "path": "./tsconfig.build.json" }, 9 | { "path": "./tsconfig.test.json" }, 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | end_of_line = lf 6 | charset = utf-8 7 | trim_trailing_whitespace = true 8 | insert_final_newline = true 9 | indent_size = 2 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | -------------------------------------------------------------------------------- /examples/telegram-game/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | telegram: { 4 | enabled: true, 5 | path: '/webhooks/telegram', 6 | accessToken: process.env.TELEGRAM_ACCESS_TOKEN, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /examples/telegram-poll/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | telegram: { 4 | enabled: true, 5 | path: '/webhooks/telegram', 6 | accessToken: process.env.TELEGRAM_ACCESS_TOKEN, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /packages/bottender-facebook/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["**/__tests__"] 9 | } 10 | -------------------------------------------------------------------------------- /packages/bottender-handlers/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["**/__tests__"] 9 | } 10 | -------------------------------------------------------------------------------- /examples/browser-only/src/BrowserBot/BrowserContext.js: -------------------------------------------------------------------------------- 1 | import { Context } from 'bottender'; 2 | 3 | class BrowserContext extends Context { 4 | sendText(text) { 5 | this._client.sendText(text); 6 | } 7 | } 8 | 9 | export default BrowserContext; 10 | -------------------------------------------------------------------------------- /examples/custom-connector-facebook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "@bottender/facebook": "next", 8 | "bottender": "next" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/messenger-batch-multi-pages/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest", 8 | "messenger-batch": "^0.3.1" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /examples/telegram-hello-world/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | telegram: { 4 | enabled: true, 5 | path: '/webhooks/telegram', 6 | accessToken: process.env.TELEGRAM_ACCESS_TOKEN, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /examples/telegram-inline-keyboard/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | telegram: { 4 | enabled: true, 5 | path: '/webhooks/telegram', 6 | accessToken: process.env.TELEGRAM_ACCESS_TOKEN, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /examples/telegram-inline-query/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | telegram: { 4 | enabled: true, 5 | path: '/webhooks/telegram', 6 | accessToken: process.env.TELEGRAM_ACCESS_TOKEN, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /examples/telegram-reply-keyboard/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | telegram: { 4 | enabled: true, 5 | path: '/webhooks/telegram', 6 | accessToken: process.env.TELEGRAM_ACCESS_TOKEN, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /examples/messenger-multi-pages/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | PAGE_1_PAGE_ID= 8 | PAGE_1_ACCESS_TOKEN= 9 | 10 | PAGE_2_PAGE_ID= 11 | PAGE_2_ACCESS_TOKEN= 12 | -------------------------------------------------------------------------------- /examples/with-rasa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "@bottender/rasa": "latest", 8 | "axios": "^0.19.0", 9 | "bottender": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/session-memory/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | session: { 3 | driver: 'memory', 4 | stores: { 5 | memory: { 6 | maxSize: 500, // The maximum size of the cache, default will be 500. 7 | }, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/session-mongo/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | session: { 3 | driver: 'mongo', 4 | stores: { 5 | mongo: { 6 | url: 'mongodb://localhost:27017', 7 | collectionName: 'sessions', 8 | }, 9 | }, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /examples/with-luis.ai/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "@bottender/luis": "latest", 8 | "axios": "^0.19.0", 9 | "bottender": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/with-qna-maker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "@bottender/qna-maker": "latest", 8 | "axios": "^0.19.0", 9 | "bottender": "latest" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /examples/line-notify/notify.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Loading... 5 | 6 | 7 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /examples/with-dialogflow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "bottender dev", 4 | "start": "bottender start" 5 | }, 6 | "dependencies": { 7 | "@bottender/dialogflow": "latest", 8 | "bottender": "latest", 9 | "dialogflow": "^0.14.1" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /packages/bottender/src/context/Event.ts: -------------------------------------------------------------------------------- 1 | import { JsonObject } from 'type-fest'; 2 | 3 | export interface Event { 4 | readonly rawEvent: RE; 5 | readonly isMessage: boolean; 6 | readonly isText: boolean; 7 | readonly message?: JsonObject | null; 8 | } 9 | -------------------------------------------------------------------------------- /examples/session-redis/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | session: { 3 | driver: 'redis', 4 | stores: { 5 | redis: { 6 | port: 6379, 7 | host: '127.0.0.1', 8 | password: 'auth', 9 | db: 0, 10 | }, 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/create-bottender-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["**/__tests__", "**/template", "**/template-typescript"] 9 | } 10 | -------------------------------------------------------------------------------- /__mocks__/messaging-api-line.ts: -------------------------------------------------------------------------------- 1 | // eslint-disable-next-line @typescript-eslint/no-explicit-any 2 | const MessagingAPILine = jest.genMockFromModule('messaging-api-line') as any; 3 | 4 | MessagingAPILine.Line = jest.requireActual('messaging-api-line').Line; 5 | 6 | module.exports = MessagingAPILine; 7 | -------------------------------------------------------------------------------- /examples/line-liff-v1/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | line: { 4 | enabled: true, 5 | path: '/webhooks/line', 6 | accessToken: process.env.LINE_ACCESS_TOKEN, 7 | channelSecret: process.env.LINE_CHANNEL_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/line-liff-v2/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | line: { 4 | enabled: true, 5 | path: '/webhooks/line', 6 | accessToken: process.env.LINE_ACCESS_TOKEN, 7 | channelSecret: process.env.LINE_CHANNEL_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/line-notify/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | line: { 4 | enabled: true, 5 | path: '/webhooks/line', 6 | accessToken: process.env.LINE_ACCESS_TOKEN, 7 | channelSecret: process.env.LINE_CHANNEL_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/viber/index.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Viber', 3 | subcommands: new Set(['help', 'webhook']), 4 | get webhook() { 5 | return require('./webhook').default; 6 | }, 7 | get help() { 8 | return require('./help').default; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/line-hello-world/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | line: { 4 | enabled: true, 5 | path: '/webhooks/line', 6 | accessToken: process.env.LINE_ACCESS_TOKEN, 7 | channelSecret: process.env.LINE_CHANNEL_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/line-rich-menu/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | line: { 4 | enabled: true, 5 | path: '/webhooks/line', 6 | accessToken: process.env.LINE_ACCESS_TOKEN, 7 | channelSecret: process.env.LINE_CHANNEL_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/slack-home-tab/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/telegram/index.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Telegram', 3 | subcommands: new Set(['help', 'webhook']), 4 | get webhook() { 5 | return require('./webhook').default; 6 | }, 7 | get help() { 8 | return require('./help').default; 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/line-rich-menu-submenu/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | line: { 4 | enabled: true, 5 | path: '/webhooks/line', 6 | accessToken: process.env.LINE_ACCESS_TOKEN, 7 | channelSecret: process.env.LINE_CHANNEL_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/slack-hello-world/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/slack-modal-form/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/slack-modal-on-home/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/slack-modal-push/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/slack-modal-update/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/slack-slash-command/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/viber-hello-world/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | viber: { 4 | enabled: true, 5 | path: '/webhooks/viber', 6 | accessToken: process.env.VIBER_ACCESS_TOKEN, 7 | sender: { 8 | name: 'xxxx', 9 | }, 10 | }, 11 | }, 12 | }; 13 | -------------------------------------------------------------------------------- /website/docs/the-basics-context-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: the-basics-context-event 3 | title: Context and Event 4 | --- 5 | 6 | ## Context 7 | 8 | ```js 9 | async function App(context) {} 10 | ``` 11 | 12 | ## Event 13 | 14 | ```js 15 | async function App(context) { 16 | context.event; 17 | } 18 | ``` 19 | -------------------------------------------------------------------------------- /examples/slack-post-ephemeral/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /examples/slack-update-and-delete/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | moduleFileExtensions: ['js'], 4 | transformIgnorePatterns: ['/node_modules/'], 5 | testMatch: [ 6 | '/**/__tests__/**/*.js', 7 | '/**/*.{spec,test}.js', 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /examples/slack-interactive-message/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | slack: { 4 | enabled: true, 5 | path: '/webhooks/slack', 6 | accessToken: process.env.SLACK_ACCESS_TOKEN, 7 | signingSecret: process.env.SLACK_SIGNING_SECRET, 8 | }, 9 | }, 10 | }; 11 | -------------------------------------------------------------------------------- /lint-staged.config.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/explicit-function-return-type */ 2 | 3 | module.exports = { 4 | '*.ts': () => 'tsc --build tsconfig.build.json', 5 | '*.{ts,js}': ['eslint --ext=ts,tsx --fix'], 6 | '*.md': ['prettier --write'], 7 | 'package.json': ['prettier-package-json --write'], 8 | }; 9 | -------------------------------------------------------------------------------- /packages/bottender-luis/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["**/__tests__"], 9 | "references": [ 10 | { "path": "../bottender" } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/bottender-rasa/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["**/__tests__"], 9 | "references": [ 10 | { "path": "../bottender" } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/bottender/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["**/__tests__"], 9 | "references": [ 10 | { "path": "../bottender-express" } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "lerna": "3.4.0", 3 | "npmClient": "yarn", 4 | "useWorkspaces": true, 5 | "version": "1.5.5", 6 | "command": { 7 | "publish": { 8 | "ignoreChanges": [ 9 | "__tests__/**" 10 | ] 11 | } 12 | }, 13 | "publishConfig": { 14 | "access": "public" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/bottender-dialogflow/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["**/__tests__"], 9 | "references": [ 10 | { "path": "../bottender" } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/bottender-qna-maker/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../../tsconfig.base", 3 | "compilerOptions": { 4 | "outDir": "./dist", 5 | "rootDir": "./src" 6 | }, 7 | "include": ["src/**/*"], 8 | "exclude": ["**/__tests__"], 9 | "references": [ 10 | { "path": "../bottender" } 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/index.ts: -------------------------------------------------------------------------------- 1 | import line from './line'; 2 | import messenger from './messenger'; 3 | import sh from './sh'; 4 | import telegram from './telegram'; 5 | import viber from './viber'; 6 | 7 | export default { 8 | sh, 9 | messenger, 10 | telegram, 11 | line, 12 | viber, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/bottender/src/server/DevServer.ts: -------------------------------------------------------------------------------- 1 | import Server from './Server'; 2 | 3 | // TODO: add watcher 4 | class DevServer extends Server { 5 | public async prepare(): Promise { 6 | // TODO: implement more dev features here 7 | return super.prepare(); 8 | } 9 | } 10 | 11 | export default DevServer; 12 | -------------------------------------------------------------------------------- /examples/browser-only/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-header { 6 | background-color: #282c34; 7 | min-height: 100vh; 8 | display: flex; 9 | flex-direction: column; 10 | align-items: center; 11 | justify-content: center; 12 | font-size: calc(10px + 2vmin); 13 | color: white; 14 | } 15 | -------------------------------------------------------------------------------- /examples/custom-server-koa/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | LINE_ACCESS_TOKEN= 8 | LINE_CHANNEL_SECRET= 9 | 10 | TELEGRAM_ACCESS_TOKEN= 11 | 12 | SLACK_ACCESS_TOKEN= 13 | SLACK_VERIFICATION_TOKEN= 14 | 15 | VIBER_ACCESS_TOKEN= 16 | -------------------------------------------------------------------------------- /examples/multiple-channels/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | LINE_ACCESS_TOKEN= 8 | LINE_CHANNEL_SECRET= 9 | 10 | TELEGRAM_ACCESS_TOKEN= 11 | 12 | SLACK_ACCESS_TOKEN= 13 | SLACK_VERIFICATION_TOKEN= 14 | 15 | VIBER_ACCESS_TOKEN= 16 | -------------------------------------------------------------------------------- /test/setupJest.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | // Attach only one listener per process 4 | if (!process.env.REJECTION_HANDLED) { 5 | process.on('unhandledRejection', (reason, p) => { 6 | console.log('Unhandled Rejection at:', p, 'reason:', reason); 7 | }); 8 | 9 | process.env.REJECTION_HANDLED = true; 10 | } 11 | -------------------------------------------------------------------------------- /examples/custom-server-express/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | LINE_ACCESS_TOKEN= 8 | LINE_CHANNEL_SECRET= 9 | 10 | TELEGRAM_ACCESS_TOKEN= 11 | 12 | SLACK_ACCESS_TOKEN= 13 | SLACK_VERIFICATION_TOKEN= 14 | 15 | VIBER_ACCESS_TOKEN= 16 | -------------------------------------------------------------------------------- /examples/custom-server-restify/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | LINE_ACCESS_TOKEN= 8 | LINE_CHANNEL_SECRET= 9 | 10 | TELEGRAM_ACCESS_TOKEN= 11 | 12 | SLACK_ACCESS_TOKEN= 13 | SLACK_VERIFICATION_TOKEN= 14 | 15 | VIBER_ACCESS_TOKEN= 16 | -------------------------------------------------------------------------------- /packages/bottender-express/src/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import * as express from '..'; 2 | 3 | describe('express', () => { 4 | it('export public apis', () => { 5 | expect(express.createServer).toBeDefined(); 6 | expect(express.createMiddleware).toBeDefined(); 7 | expect(express.registerRoutes).toBeDefined(); 8 | }); 9 | }); 10 | -------------------------------------------------------------------------------- /examples/browser-only/src/BrowserBot/index.js: -------------------------------------------------------------------------------- 1 | import { Bot } from 'bottender'; 2 | 3 | import BrowserConnector from './BrowserConnector'; 4 | 5 | class BrowserBot extends Bot { 6 | constructor({ client }) { 7 | const connector = new BrowserConnector(client); 8 | super({ connector }); 9 | } 10 | } 11 | 12 | export default BrowserBot; 13 | -------------------------------------------------------------------------------- /examples/custom-server-express-typescript/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | LINE_ACCESS_TOKEN= 8 | LINE_CHANNEL_SECRET= 9 | 10 | TELEGRAM_ACCESS_TOKEN= 11 | 12 | SLACK_ACCESS_TOKEN= 13 | SLACK_VERIFICATION_TOKEN= 14 | 15 | VIBER_ACCESS_TOKEN= 16 | -------------------------------------------------------------------------------- /packages/create-bottender-app/src/types.ts: -------------------------------------------------------------------------------- 1 | export type Platform = 2 | | 'messenger' 3 | | 'whatsapp' 4 | | 'line' 5 | | 'slack' 6 | | 'telegram' 7 | | 'viber'; 8 | export type Session = 'memory' | 'file' | 'redis' | 'mongo'; 9 | export type Answer = { 10 | name: string; 11 | platforms: Platform[]; 12 | session: Session; 13 | }; 14 | -------------------------------------------------------------------------------- /examples/telegram-game/gameSession.js: -------------------------------------------------------------------------------- 1 | const session = {}; 2 | 3 | function createSession(data) { 4 | const key = Math.random().toString(36).substr(2, 10); 5 | session[key] = data; 6 | return key; 7 | } 8 | 9 | function getSession(key) { 10 | return session[key]; 11 | } 12 | 13 | module.exports = { 14 | getSession, 15 | createSession, 16 | }; 17 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/views/index.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Copyright 2017-present, Facebook, Inc. All rights reserved. 3 | 4 | This source code is licensed under the license found in the 5 | LICENSE file in the root directory of fbsamples/messenger-bot-samples. 6 | 7 | extends layout 8 | 9 | block content 10 | h1= title 11 | p Welcome to #{title} 12 | -------------------------------------------------------------------------------- /examples/whatsapp-hello-world/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | whatsapp: { 4 | enabled: true, 5 | path: '/webhooks/whatsapp', 6 | accountSid: process.env.WHATSAPP_ACCOUNT_SID, 7 | authToken: process.env.WHATSAPP_AUTH_TOKEN, 8 | phoneNumber: process.env.WHATSAPP_PHONE_NUMBER, 9 | }, 10 | }, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/bottender-handlers/src/middleware.ts: -------------------------------------------------------------------------------- 1 | import compose from 'koa-compose'; 2 | 3 | import { Builder } from './Handler'; 4 | 5 | type Middleware = (context?: any, next?: Middleware) => {}; 6 | 7 | const middleware = (m: (Middleware | Builder)[]) => 8 | compose(m.map((item) => ('build' in item ? item.build() : item))); 9 | 10 | export default middleware; 11 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.4/the-basics-context-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: the-basics-context-event 3 | title: Context and Event 4 | original_id: the-basics-context-event 5 | --- 6 | 7 | ## Context 8 | 9 | ```js 10 | async function App(context) {} 11 | ``` 12 | 13 | ## Event 14 | 15 | ```js 16 | async function App(context) { 17 | context.event; 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.5/the-basics-context-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: the-basics-context-event 3 | title: Context and Event 4 | original_id: the-basics-context-event 5 | --- 6 | 7 | ## Context 8 | 9 | ```js 10 | async function App(context) {} 11 | ``` 12 | 13 | ## Event 14 | 15 | ```js 16 | async function App(context) { 17 | context.event; 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /examples/custom-server-restify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "nodemon server.js", 4 | "start": "cross-env NODE_ENV=production node server.js" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest", 8 | "cross-env": "^6.0.3", 9 | "restify": "^8.5.0" 10 | }, 11 | "devDependencies": { 12 | "nodemon": "^2.0.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.0.5/the-basics-context-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: the-basics-context-event 3 | title: Context and Event 4 | original_id: the-basics-context-event 5 | --- 6 | 7 | ## Context 8 | 9 | ```js 10 | async function App(context) {} 11 | ``` 12 | 13 | ## Event 14 | 15 | ```js 16 | async function App(context) { 17 | context.event; 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.1.0/the-basics-context-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: the-basics-context-event 3 | title: Context and Event 4 | original_id: the-basics-context-event 5 | --- 6 | 7 | ## Context 8 | 9 | ```js 10 | async function App(context) {} 11 | ``` 12 | 13 | ## Event 14 | 15 | ```js 16 | async function App(context) { 17 | context.event; 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.2.0/the-basics-context-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: the-basics-context-event 3 | title: Context and Event 4 | original_id: the-basics-context-event 5 | --- 6 | 7 | ## Context 8 | 9 | ```js 10 | async function App(context) {} 11 | ``` 12 | 13 | ## Event 14 | 15 | ```js 16 | async function App(context) { 17 | context.event; 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.3.0/the-basics-context-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: the-basics-context-event 3 | title: Context and Event 4 | original_id: the-basics-context-event 5 | --- 6 | 7 | ## Context 8 | 9 | ```js 10 | async function App(context) {} 11 | ``` 12 | 13 | ## Event 14 | 15 | ```js 16 | async function App(context) { 17 | context.event; 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.3.1/the-basics-context-event.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: the-basics-context-event 3 | title: Context and Event 4 | original_id: the-basics-context-event 5 | --- 6 | 7 | ## Context 8 | 9 | ```js 10 | async function App(context) {} 11 | ``` 12 | 13 | ## Event 14 | 15 | ```js 16 | async function App(context) { 17 | context.event; 18 | } 19 | ``` 20 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/views/error.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Copyright 2017-present, Facebook, Inc. All rights reserved. 3 | 4 | This source code is licensed under the license found in the 5 | LICENSE file in the root directory of fbsamples/messenger-bot-samples. 6 | 7 | extends layout 8 | 9 | block content 10 | h1= message 11 | h2= error.status 12 | pre #{error.stack} 13 | -------------------------------------------------------------------------------- /examples/line-liff-v1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "nodemon server.js", 4 | "start": "cross-env NODE_ENV=production node server.js" 5 | }, 6 | "dependencies": { 7 | "body-parser": "^1.19.0", 8 | "bottender": "latest", 9 | "cross-env": "^6.0.3", 10 | "express": "^4.17.1" 11 | }, 12 | "devDependencies": { 13 | "nodemon": "^2.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/line-liff-v2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "nodemon server.js", 4 | "start": "cross-env NODE_ENV=production node server.js" 5 | }, 6 | "dependencies": { 7 | "body-parser": "^1.19.0", 8 | "bottender": "latest", 9 | "cross-env": "^6.0.3", 10 | "express": "^4.17.1" 11 | }, 12 | "devDependencies": { 13 | "nodemon": "^2.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /__mocks__/fs.ts: -------------------------------------------------------------------------------- 1 | const fs = jest.genMockFromModule('fs') as any; 2 | 3 | let mockFiles; 4 | 5 | function __setMockFiles(newMockFiles) { 6 | mockFiles = newMockFiles; 7 | } 8 | 9 | function readFileSync() { 10 | return JSON.stringify(mockFiles || { mockKey: 'mockValue' }); 11 | } 12 | 13 | fs.__setMockFiles = __setMockFiles; 14 | fs.readFileSync = readFileSync; 15 | 16 | module.exports = fs; 17 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/sh/utils/getArgs.ts: -------------------------------------------------------------------------------- 1 | import arg, { Options, Spec } from 'arg'; 2 | 3 | import getCommonArgs from './argCommon'; 4 | 5 | const getArgs = (argv: string[], argsOptions: Spec, argOptions: Options) => 6 | arg( 7 | { 8 | ...getCommonArgs(), 9 | ...argsOptions, 10 | }, 11 | { ...argOptions, argv } 12 | ); 13 | 14 | export default getArgs; 15 | -------------------------------------------------------------------------------- /examples/custom-server-express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "nodemon server.js", 4 | "start": "cross-env NODE_ENV=production node server.js" 5 | }, 6 | "dependencies": { 7 | "body-parser": "^1.19.0", 8 | "bottender": "latest", 9 | "cross-env": "^6.0.3", 10 | "express": "^4.17.1" 11 | }, 12 | "devDependencies": { 13 | "nodemon": "^2.0.1" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /examples/messenger-one-time-notification/.env: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID=1895382890692545 2 | MESSENGER_ACCESS_TOKEN=EAAFK0AZBZBnIwBAPTvZCULq44OwYDxze3DgXMUEgnDnqgMYJ9sAuO9iFIRzkBj4jRvnynZAZCyn1vY0gKuXGpxqYCuLnc2ZCm4YvEaarN0l3uFXroqLMVd1zm5cwBzCUYZBjznMO9VhzjHgaAUMzN7ZAfBOtfbvOZCYvxwAYvlc6eLQZDZD 3 | MESSENGER_APP_ID=363732454513804 4 | MESSENGER_APP_SECRET=94b0639173ee2b80a69ec37f669ed9d2 5 | MESSENGER_VERIFY_TOKEN=1234 6 | -------------------------------------------------------------------------------- /examples/browser-only/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /packages/bottender/src/session/SessionStore.ts: -------------------------------------------------------------------------------- 1 | import Session from './Session'; 2 | 3 | type SessionStore = { 4 | init(): Promise>; 5 | 6 | read(key: string): Promise; 7 | 8 | all(): Promise; 9 | 10 | write(key: string, sess: Session): Promise; 11 | 12 | destroy(key: string): Promise; 13 | }; 14 | 15 | export default SessionStore; 16 | -------------------------------------------------------------------------------- /examples/line-notify/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "nodemon server.js", 4 | "start": "cross-env NODE_ENV=production node server.js" 5 | }, 6 | "dependencies": { 7 | "axios": "^0.19.2", 8 | "body-parser": "^1.19.0", 9 | "bottender": "latest", 10 | "ejs": "^3.0.1", 11 | "express": "^4.17.1" 12 | }, 13 | "devDependencies": { 14 | "nodemon": "^2.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/with-state/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | if (context.state.asking) { 3 | context.setState({ nickname: context.event.text, asking: false }); 4 | await context.sendText(`Hello ${context.state.nickname}`); 5 | } else { 6 | context.resetState(); 7 | context.setState({ asking: true }); 8 | await context.sendText("Hi, what's your nickname?"); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /packages/bottender-rasa/src/types.ts: -------------------------------------------------------------------------------- 1 | export type ParsedResult = { 2 | entities: Entity[]; 3 | intent: Intent; 4 | intent_ranking: Intent[]; 5 | text: string; 6 | }; 7 | 8 | export type Intent = { 9 | name: string; 10 | confidence: number; 11 | }; 12 | 13 | export type Entity = { 14 | start: number; 15 | end: number; 16 | value: string; 17 | entity: string; 18 | confidence: number; 19 | }; 20 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/sh/__tests__/help.spec.ts: -------------------------------------------------------------------------------- 1 | import help from '../help'; 2 | 3 | describe('help', () => { 4 | beforeEach(() => { 5 | console.log = jest.fn(); 6 | }); 7 | 8 | it('shouild exist', () => { 9 | expect(help).toBeDefined(); 10 | }); 11 | 12 | it('should call console.log', () => { 13 | help(); 14 | 15 | expect(console.log).toBeCalled(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/viber/__tests__/help.spec.ts: -------------------------------------------------------------------------------- 1 | import help from '../help'; 2 | 3 | describe('help', () => { 4 | beforeEach(() => { 5 | console.log = jest.fn(); 6 | }); 7 | 8 | it('shouild exist', () => { 9 | expect(help).toBeDefined(); 10 | }); 11 | 12 | it('should call console.log', () => { 13 | help(); 14 | 15 | expect(console.log).toBeCalled(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /website/docs/api-console-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-console-context 3 | title: ConsoleContext 4 | --- 5 | 6 | Extends from [Context](api-context.md). 7 | 8 | #### `platform` 9 | 10 | Example: 11 | 12 | ```js 13 | context.platform; // 'console' 14 | ``` 15 | 16 | #### `sendText(text)` 17 | 18 | Send text to the owner of the session. 19 | 20 | Example: 21 | 22 | ```js 23 | context.sendText('Hello'); 24 | ``` 25 | -------------------------------------------------------------------------------- /examples/browser-only/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/messenger/__tests__/help.spec.ts: -------------------------------------------------------------------------------- 1 | import help from '../help'; 2 | 3 | describe('help', () => { 4 | beforeEach(() => { 5 | console.log = jest.fn(); 6 | }); 7 | 8 | it('shouild exist', () => { 9 | expect(help).toBeDefined(); 10 | }); 11 | 12 | it('should call console.log', () => { 13 | help(); 14 | 15 | expect(console.log).toBeCalled(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/telegram/__tests__/help.spec.ts: -------------------------------------------------------------------------------- 1 | import help from '../help'; 2 | 3 | describe('help', () => { 4 | beforeEach(() => { 5 | console.log = jest.fn(); 6 | }); 7 | 8 | it('shouild exist', () => { 9 | expect(help).toBeDefined(); 10 | }); 11 | 12 | it('should call console.log', () => { 13 | help(); 14 | 15 | expect(console.log).toBeCalled(); 16 | }); 17 | }); 18 | -------------------------------------------------------------------------------- /examples/custom-server-koa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "nodemon server.js", 4 | "start": "cross-env NODE_ENV=production node server.js" 5 | }, 6 | "dependencies": { 7 | "bottender": "latest", 8 | "cross-env": "^6.0.3", 9 | "koa": "^2.11.0", 10 | "koa-bodyparser": "^4.2.1", 11 | "koa-router": "^7.4.0" 12 | }, 13 | "devDependencies": { 14 | "nodemon": "^2.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/with-aws-lambda/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "nodemon server.js", 4 | "start": "cross-env NODE_ENV=production node server.js" 5 | }, 6 | "dependencies": { 7 | "body-parser": "^1.19.0", 8 | "bottender": "latest", 9 | "cross-env": "^6.0.3", 10 | "express": "^4.17.1", 11 | "serverless-http": "^2.3.0" 12 | }, 13 | "devDependencies": { 14 | "nodemon": "^2.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/with-gcp-cloud-function/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "main": "server.js", 3 | "scripts": { 4 | "dev": "nodemon server.js", 5 | "start": "cross-env NODE_ENV=production node server.js" 6 | }, 7 | "dependencies": { 8 | "body-parser": "^1.19.0", 9 | "bottender": "latest", 10 | "cross-env": "^6.0.3", 11 | "express": "^4.17.1" 12 | }, 13 | "devDependencies": { 14 | "nodemon": "^2.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template/.env.example: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | WHATSAPP_ACCOUNT_SID= 8 | WHATSAPP_AUTH_TOKEN= 9 | WHATSAPP_PHONE_NUMBER= 10 | 11 | LINE_ACCESS_TOKEN= 12 | LINE_CHANNEL_SECRET= 13 | 14 | TELEGRAM_ACCESS_TOKEN= 15 | 16 | SLACK_ACCESS_TOKEN= 17 | SLACK_SIGNING_SECRET= 18 | 19 | VIBER_ACCESS_TOKEN= 20 | -------------------------------------------------------------------------------- /examples/telegram-game/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "nodemon server.js", 4 | "start": "cross-env NODE_ENV=production node server.js" 5 | }, 6 | "dependencies": { 7 | "body-parser": "^1.19.0", 8 | "bottender": "latest", 9 | "cross-env": "^6.0.3", 10 | "express": "^4.17.1", 11 | "messaging-api-telegram": "^0.8.2" 12 | }, 13 | "devDependencies": { 14 | "nodemon": "^2.0.1" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/sh/index.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Bottender', 3 | subcommands: new Set(['help', 'init', 'start', 'dev']), 4 | get init() { 5 | return require('./init').default; 6 | }, 7 | get start() { 8 | return require('./start').default; 9 | }, 10 | get dev() { 11 | return require('./dev').default; 12 | }, 13 | get help() { 14 | return require('./help').default; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template-typescript/.env.example: -------------------------------------------------------------------------------- 1 | MESSENGER_PAGE_ID= 2 | MESSENGER_ACCESS_TOKEN= 3 | MESSENGER_APP_ID= 4 | MESSENGER_APP_SECRET= 5 | MESSENGER_VERIFY_TOKEN= 6 | 7 | WHATSAPP_ACCOUNT_SID= 8 | WHATSAPP_AUTH_TOKEN= 9 | WHATSAPP_PHONE_NUMBER= 10 | 11 | LINE_ACCESS_TOKEN= 12 | LINE_CHANNEL_SECRET= 13 | 14 | TELEGRAM_ACCESS_TOKEN= 15 | 16 | SLACK_ACCESS_TOKEN= 17 | SLACK_SIGNING_SECRET= 18 | 19 | VIBER_ACCESS_TOKEN= 20 | -------------------------------------------------------------------------------- /examples/custom-server-express-typescript/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | LineContext, 3 | MessengerContext, 4 | SlackContext, 5 | TelegramContext, 6 | ViberContext, 7 | } from 'bottender'; 8 | 9 | export default async function App( 10 | context: 11 | | MessengerContext 12 | | LineContext 13 | | SlackContext 14 | | TelegramContext 15 | | ViberContext 16 | ): Promise { 17 | await context.sendText('Hello World'); 18 | } 19 | -------------------------------------------------------------------------------- /packages/bottender-facebook/src/index.ts: -------------------------------------------------------------------------------- 1 | import * as FacebookTypes from './FacebookTypes'; 2 | 3 | export { default as FacebookBatch } from './FacebookBatch'; 4 | export { default as FacebookClient } from './FacebookClient'; 5 | export { default as FacebookConnector } from './FacebookConnector'; 6 | export { default as FacebookContext } from './FacebookContext'; 7 | export { default as FacebookEvent } from './FacebookEvent'; 8 | 9 | export { FacebookTypes }; 10 | -------------------------------------------------------------------------------- /website/static/img/hero_background.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /examples/messenger-persona/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | messenger: { 4 | enabled: true, 5 | path: '/webhooks/messenger', 6 | pageId: process.env.MESSENGER_PAGE_ID, 7 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 8 | appId: process.env.MESSENGER_APP_ID, 9 | appSecret: process.env.MESSENGER_APP_SECRET, 10 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /examples/with-aws-lambda/serverless.yml: -------------------------------------------------------------------------------- 1 | service: bottender-with-aws-lambda 2 | provider: 3 | name: aws 4 | runtime: nodejs12.x 5 | stage: dev 6 | region: us-east-1 7 | memorySize: 128 8 | functions: 9 | app: 10 | handler: server.handler 11 | events: 12 | - http: 13 | path: / 14 | method: ANY 15 | cors: true 16 | - http: 17 | path: /{proxy+} 18 | method: ANY 19 | cors: true 20 | -------------------------------------------------------------------------------- /examples/browser-only/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /examples/messenger-built-in-nlp/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | messenger: { 4 | enabled: true, 5 | path: '/webhooks/messenger', 6 | pageId: process.env.MESSENGER_PAGE_ID, 7 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 8 | appId: process.env.MESSENGER_APP_ID, 9 | appSecret: process.env.MESSENGER_APP_SECRET, 10 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /examples/messenger-hello-world/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | messenger: { 4 | enabled: true, 5 | path: '/webhooks/messenger', 6 | pageId: process.env.MESSENGER_PAGE_ID, 7 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 8 | appId: process.env.MESSENGER_APP_ID, 9 | appSecret: process.env.MESSENGER_APP_SECRET, 10 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/bottender-facebook/src/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import { 2 | FacebookBatch, 3 | FacebookClient, 4 | FacebookConnector, 5 | FacebookContext, 6 | FacebookEvent, 7 | } from '..'; 8 | 9 | it('should export public api', () => { 10 | expect(FacebookBatch).toBeDefined(); 11 | expect(FacebookClient).toBeDefined(); 12 | expect(FacebookConnector).toBeDefined(); 13 | expect(FacebookContext).toBeDefined(); 14 | expect(FacebookEvent).toBeDefined(); 15 | }); 16 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.0.5/api-console-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-console-context 3 | title: ConsoleContext 4 | original_id: api-console-context 5 | --- 6 | 7 | Extends from [Context](api-context). 8 | 9 | #### `platform` 10 | 11 | Example: 12 | 13 | ```js 14 | context.platform; // 'console' 15 | ``` 16 | 17 | #### `sendText(text)` 18 | 19 | Send text to the owner of the session. 20 | 21 | Example: 22 | 23 | ```js 24 | context.sendText('Hello'); 25 | ``` 26 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.1.0/api-console-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-console-context 3 | title: ConsoleContext 4 | original_id: api-console-context 5 | --- 6 | 7 | Extends from [Context](api-context). 8 | 9 | #### `platform` 10 | 11 | Example: 12 | 13 | ```js 14 | context.platform; // 'console' 15 | ``` 16 | 17 | #### `sendText(text)` 18 | 19 | Send text to the owner of the session. 20 | 21 | Example: 22 | 23 | ```js 24 | context.sendText('Hello'); 25 | ``` 26 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.2.0/api-console-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-console-context 3 | title: ConsoleContext 4 | original_id: api-console-context 5 | --- 6 | 7 | Extends from [Context](api-context). 8 | 9 | #### `platform` 10 | 11 | Example: 12 | 13 | ```js 14 | context.platform; // 'console' 15 | ``` 16 | 17 | #### `sendText(text)` 18 | 19 | Send text to the owner of the session. 20 | 21 | Example: 22 | 23 | ```js 24 | context.sendText('Hello'); 25 | ``` 26 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.3.0/api-console-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-console-context 3 | title: ConsoleContext 4 | original_id: api-console-context 5 | --- 6 | 7 | Extends from [Context](api-context). 8 | 9 | #### `platform` 10 | 11 | Example: 12 | 13 | ```js 14 | context.platform; // 'console' 15 | ``` 16 | 17 | #### `sendText(text)` 18 | 19 | Send text to the owner of the session. 20 | 21 | Example: 22 | 23 | ```js 24 | context.sendText('Hello'); 25 | ``` 26 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.3.1/api-console-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-console-context 3 | title: ConsoleContext 4 | original_id: api-console-context 5 | --- 6 | 7 | Extends from [Context](api-context). 8 | 9 | #### `platform` 10 | 11 | Example: 12 | 13 | ```js 14 | context.platform; // 'console' 15 | ``` 16 | 17 | #### `sendText(text)` 18 | 19 | Send text to the owner of the session. 20 | 21 | Example: 22 | 23 | ```js 24 | context.sendText('Hello'); 25 | ``` 26 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.4/api-console-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-console-context 3 | title: ConsoleContext 4 | original_id: api-console-context 5 | --- 6 | 7 | Extends from [Context](api-context.md). 8 | 9 | #### `platform` 10 | 11 | Example: 12 | 13 | ```js 14 | context.platform; // 'console' 15 | ``` 16 | 17 | #### `sendText(text)` 18 | 19 | Send text to the owner of the session. 20 | 21 | Example: 22 | 23 | ```js 24 | context.sendText('Hello'); 25 | ``` 26 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.5/api-console-context.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-console-context 3 | title: ConsoleContext 4 | original_id: api-console-context 5 | --- 6 | 7 | Extends from [Context](api-context.md). 8 | 9 | #### `platform` 10 | 11 | Example: 12 | 13 | ```js 14 | context.platform; // 'console' 15 | ``` 16 | 17 | #### `sendText(text)` 18 | 19 | Send text to the owner of the session. 20 | 21 | Example: 22 | 23 | ```js 24 | context.sendText('Hello'); 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/messenger-one-time-notification/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | messenger: { 4 | enabled: true, 5 | path: '/webhooks/messenger', 6 | pageId: process.env.MESSENGER_PAGE_ID, 7 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 8 | appId: process.env.MESSENGER_APP_ID, 9 | appSecret: process.env.MESSENGER_APP_SECRET, 10 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 11 | }, 12 | }, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/bottender-handlers/src/__tests__/SlackHandler.spec.ts: -------------------------------------------------------------------------------- 1 | import SlackHandler from '../SlackHandler'; 2 | 3 | const setup = () => { 4 | const builder = new SlackHandler(); 5 | return { 6 | builder, 7 | }; 8 | }; 9 | 10 | describe('#constructor', () => { 11 | it('should construct without error', () => { 12 | const { builder } = setup(); 13 | expect(SlackHandler).toBeDefined(); 14 | expect(builder).toBeInstanceOf(SlackHandler); 15 | }); 16 | }); 17 | -------------------------------------------------------------------------------- /packages/bottender-handlers/src/index.ts: -------------------------------------------------------------------------------- 1 | /* HandlerBuilder */ 2 | export { default as middleware } from './middleware'; 3 | export { default as Handler } from './Handler'; 4 | export { default as MessengerHandler } from './MessengerHandler'; 5 | export { default as LineHandler } from './LineHandler'; 6 | export { default as SlackHandler } from './SlackHandler'; 7 | export { default as TelegramHandler } from './TelegramHandler'; 8 | export { default as ViberHandler } from './ViberHandler'; 9 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/messenger/index.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | title: 'Bottender', 3 | subcommands: new Set(['help', 'persona', 'profile', 'webhook']), 4 | get persona() { 5 | return require('./persona').default; 6 | }, 7 | get profile() { 8 | return require('./profile').default; 9 | }, 10 | get webhook() { 11 | return require('./webhook').default; 12 | }, 13 | get help() { 14 | return require('./help').default; 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /website/versioned_docs/version-0.15.17/APIReference-ConsoleContext.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: api-consolecontext 3 | title: ConsoleContext 4 | original_id: api-consolecontext 5 | --- 6 | 7 | Extends from [Context](api-context). 8 | 9 | #### `platform` 10 | 11 | Example: 12 | 13 | ```js 14 | context.platform; // 'console' 15 | ``` 16 | 17 | #### `sendText(text)` 18 | 19 | Send text to the owner of the session. 20 | 21 | Example: 22 | 23 | ```js 24 | context.sendText('Hello'); 25 | ``` 26 | -------------------------------------------------------------------------------- /examples/echo-bot/README.md: -------------------------------------------------------------------------------- 1 | # Echo Bot 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/echo-bot 9 | cd echo-bot 10 | npm install 11 | npm run dev -- --console 12 | ``` 13 | 14 | ## Idea of This Example 15 | 16 | This is a bot which echoes the text messages the bot receives from users. 17 | -------------------------------------------------------------------------------- /examples/messenger-persona/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | if (context.event.text === '/help') { 3 | context.usePersona(process.env.PERSONA_1); 4 | await context.sendText('Hello'); 5 | await context.sendText('World'); 6 | } else { 7 | await context.sendText('Hello', { 8 | personaId: process.env.PERSONA_2, 9 | }); 10 | await context.sendText('World', { 11 | personaId: process.env.PERSONA_2, 12 | }); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template-typescript/jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | preset: 'ts-jest', 4 | moduleFileExtensions: ['js', 'ts'], 5 | transformIgnorePatterns: ['/node_modules/'], 6 | testMatch: [ 7 | '/**/__tests__/**/*.ts', 8 | '/**/*.{spec,test}.ts', 9 | ], 10 | globals: { 11 | 'ts-jest': { 12 | tsConfig: '/tsconfig.json', 13 | diagnostics: false, 14 | }, 15 | }, 16 | }; 17 | -------------------------------------------------------------------------------- /packages/bottender-handlers/src/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import * as handlers from '..'; 2 | 3 | it('export handler builders', () => { 4 | expect(handlers.middleware).toBeDefined(); 5 | expect(handlers.Handler).toBeDefined(); 6 | expect(handlers.MessengerHandler).toBeDefined(); 7 | expect(handlers.LineHandler).toBeDefined(); 8 | expect(handlers.SlackHandler).toBeDefined(); 9 | expect(handlers.TelegramHandler).toBeDefined(); 10 | expect(handlers.ViberHandler).toBeDefined(); 11 | }); 12 | -------------------------------------------------------------------------------- /packages/bottender/src/cache/CacheStore.ts: -------------------------------------------------------------------------------- 1 | import { JsonObject } from 'type-fest'; 2 | 3 | export type CacheValue = number | JsonObject; 4 | 5 | type CacheStore = { 6 | get(key: string): Promise; 7 | 8 | all(): Promise; 9 | 10 | put(key: string, value: CacheValue, minutes: number): Promise; 11 | 12 | forget(key: string): Promise; 13 | 14 | flush(): Promise; 15 | 16 | getPrefix(): string; 17 | }; 18 | 19 | export default CacheStore; 20 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/sh/init.ts: -------------------------------------------------------------------------------- 1 | import childProcess from 'child_process'; 2 | 3 | const init = () => { 4 | const cp = childProcess.exec('npx create-bottender-app'); 5 | 6 | if (cp) { 7 | if (cp.stdout) { 8 | cp.stdout.on('data', console.log); 9 | } 10 | if (cp.stderr) { 11 | cp.stderr.on('data', console.log); 12 | } 13 | 14 | cp.on('exit', (code: number) => { 15 | process.exit(code); 16 | }); 17 | } 18 | }; 19 | 20 | export default init; 21 | -------------------------------------------------------------------------------- /website/docs/channel-whatsapp-sending-messages.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-whatsapp-sending-messages 3 | title: Sending WhatsApp Messages 4 | --- 5 | 6 | ## Sending Text Messages 7 | 8 | Use this method to send text messages. 9 | 10 | ```js 11 | async function SendHi(context) { 12 | await context.sendText('Hi!'); 13 | } 14 | ``` 15 | 16 | To send with media URLs, you may use `mediaUrl` options: 17 | 18 | ```js 19 | await context.sendText('Hi', { 20 | mediaUrl: ['https://example.com/image.jpg'], 21 | }); 22 | ``` 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore the compiled output. 2 | dist/ 3 | website/build 4 | 5 | # TypeScript incremental compilation cache 6 | *.tsbuildinfo 7 | 8 | # Logs 9 | logs 10 | *.log 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | 15 | # Coverage (from Jest) 16 | coverage/ 17 | 18 | # JUnit Reports (used mainly in CircleCI) 19 | reports/ 20 | junit.xml 21 | 22 | # Node modules 23 | node_modules/ 24 | 25 | # Mac OS 26 | .DS_Store 27 | 28 | # Docusaurus i18n 29 | website/i18n/* 30 | website/translated_docs 31 | -------------------------------------------------------------------------------- /examples/custom-server-express-typescript/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "build": "tsc", 4 | "dev": "nodemon --exec ts-node src/server.ts", 5 | "start": "cross-env NODE_ENV=production node dist/server.js" 6 | }, 7 | "dependencies": { 8 | "body-parser": "^1.19.0", 9 | "bottender": "latest", 10 | "cross-env": "^6.0.3", 11 | "express": "^4.17.1" 12 | }, 13 | "devDependencies": { 14 | "nodemon": "^2.0.1", 15 | "ts-node": "^8.8.2", 16 | "typescript": "^3.8.3" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/line-rich-menu-submenu/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | if (context.event.text === 'A') { 3 | await context.linkRichMenu(process.env.SUB_RICH_MENU_A_ID); 4 | } else if (context.event.text === 'B') { 5 | await context.linkRichMenu(process.env.SUB_RICH_MENU_B_ID); 6 | } else if (context.event.text === 'Back') { 7 | await context.linkRichMenu(process.env.MAIN_RICH_MENU_ID); 8 | } else { 9 | await context.sendText(`User have clicked ${context.event.text}`); 10 | } 11 | }; 12 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "scripts": { 3 | "dev": "nodemon server.js", 4 | "start": "cross-env NODE_ENV=production node server.js" 5 | }, 6 | "dependencies": { 7 | "body-parser": "^1.19.0", 8 | "bottender": "latest", 9 | "cross-env": "^6.0.3", 10 | "express": "^4.17.1", 11 | "jade": "^1.11.0", 12 | "lodash": "^4.17.15", 13 | "serve-favicon": "^2.5.0", 14 | "uuid": "^7.0.2" 15 | }, 16 | "devDependencies": { 17 | "nodemon": "^2.0.1" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/browser-only/src/BrowserBot/BrowserEvent.js: -------------------------------------------------------------------------------- 1 | class BrowserEvent { 2 | constructor(rawEvent) { 3 | this._rawEvent = rawEvent; 4 | } 5 | 6 | get rawEvent() { 7 | return this._rawEvent; 8 | } 9 | 10 | get isMessage() { 11 | return true; 12 | } 13 | 14 | get message() { 15 | return this._rawEvent.message; 16 | } 17 | 18 | get isText() { 19 | return true; 20 | } 21 | 22 | get text() { 23 | return this._rawEvent.message.text; 24 | } 25 | } 26 | 27 | export default BrowserEvent; 28 | -------------------------------------------------------------------------------- /packages/bottender/src/session/__tests__/MemorySessionStore.spec.ts: -------------------------------------------------------------------------------- 1 | import CacheBasedSessionStore from '../CacheBasedSessionStore'; 2 | import MemorySessionStore from '../MemorySessionStore'; 3 | 4 | it('should be instanceof CacheBasedSessionStore', () => { 5 | expect(new MemorySessionStore()).toBeInstanceOf(CacheBasedSessionStore); 6 | expect(new MemorySessionStore(500)).toBeInstanceOf(CacheBasedSessionStore); 7 | expect(new MemorySessionStore({ maxSize: 500 })).toBeInstanceOf( 8 | CacheBasedSessionStore 9 | ); 10 | }); 11 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.0.5/channel-whatsapp-sending-messages.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-whatsapp-sending-messages 3 | title: Sending WhatsApp Messages 4 | --- 5 | 6 | ## Sending Text Messages 7 | 8 | Use this method to send text messages. 9 | 10 | ```js 11 | async function SendHi(context) { 12 | await context.sendText('Hi!'); 13 | } 14 | ``` 15 | 16 | To send with media URLs, you may use `mediaUrl` options: 17 | 18 | ```js 19 | await context.sendText('Hi', { 20 | mediaUrl: ['https://example.com/image.jpg'], 21 | }); 22 | ``` 23 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.1.0/channel-whatsapp-sending-messages.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-whatsapp-sending-messages 3 | title: Sending WhatsApp Messages 4 | --- 5 | 6 | ## Sending Text Messages 7 | 8 | Use this method to send text messages. 9 | 10 | ```js 11 | async function SendHi(context) { 12 | await context.sendText('Hi!'); 13 | } 14 | ``` 15 | 16 | To send with media URLs, you may use `mediaUrl` options: 17 | 18 | ```js 19 | await context.sendText('Hi', { 20 | mediaUrl: ['https://example.com/image.jpg'], 21 | }); 22 | ``` 23 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.2.0/channel-whatsapp-sending-messages.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-whatsapp-sending-messages 3 | title: Sending WhatsApp Messages 4 | --- 5 | 6 | ## Sending Text Messages 7 | 8 | Use this method to send text messages. 9 | 10 | ```js 11 | async function SendHi(context) { 12 | await context.sendText('Hi!'); 13 | } 14 | ``` 15 | 16 | To send with media URLs, you may use `mediaUrl` options: 17 | 18 | ```js 19 | await context.sendText('Hi', { 20 | mediaUrl: ['https://example.com/image.jpg'], 21 | }); 22 | ``` 23 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/models/User.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017-present, Facebook, Inc. All rights reserved. 3 | * 4 | * This source code is licensed under the license found in the 5 | * LICENSE file in the root directory of fbsamples/messenger-bot-samples. 6 | */ 7 | 8 | /** 9 | * User Model 10 | * @class User 11 | */ 12 | module.exports = class User { 13 | constructor(username, password, messengerId) { 14 | this.username = username; 15 | this.password = password; 16 | this.messengerId = messengerId; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /packages/bottender/src/bottender.ts: -------------------------------------------------------------------------------- 1 | import Server, { ServerOptions } from './server/Server'; 2 | 3 | type BottenderServerOptions = ServerOptions & { 4 | /** 5 | * Whether to launch Bottender in dev mode - @default false 6 | */ 7 | dev?: boolean; 8 | }; 9 | 10 | function bottender(options: BottenderServerOptions): Server { 11 | if (options.dev) { 12 | const DevServer = require('./server/DevServer').default; 13 | return new DevServer(options); 14 | } 15 | 16 | return new Server(options); 17 | } 18 | 19 | export default bottender; 20 | -------------------------------------------------------------------------------- /packages/bottender/src/line/__tests__/LineBot.spec.ts: -------------------------------------------------------------------------------- 1 | import LineBot from '../LineBot'; 2 | import LineConnector from '../LineConnector'; 3 | 4 | it('should construct bot with LineConnector', () => { 5 | const bot = new LineBot({ 6 | accessToken: 'FAKE_TOKEN', 7 | channelSecret: 'FAKE_SECRET', 8 | }); 9 | 10 | expect(bot).toBeDefined(); 11 | expect(bot.onEvent).toBeDefined(); 12 | expect(bot.createRequestHandler).toBeDefined(); 13 | expect(bot.connector).toBeDefined(); 14 | expect(bot.connector).toBeInstanceOf(LineConnector); 15 | }); 16 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/messenger/__tests__/profile.spec.ts: -------------------------------------------------------------------------------- 1 | import { trimDomain } from '../profile'; 2 | 3 | describe('#trimDomain', () => { 4 | it('should remove whitelisted_domains end slash', () => { 5 | const testProfile = { 6 | whitelistedDomains: ['https://www.facebook.com/', 'https://facebook.com'], 7 | }; 8 | 9 | const trimProfile = trimDomain(testProfile); 10 | 11 | expect(trimProfile).toEqual({ 12 | whitelistedDomains: ['https://www.facebook.com', 'https://facebook.com'], 13 | }); 14 | }); 15 | }); 16 | -------------------------------------------------------------------------------- /examples/messenger-typing/bottender.config.js: -------------------------------------------------------------------------------- 1 | const { withTyping } = require('bottender'); 2 | 3 | module.exports = { 4 | channels: { 5 | messenger: { 6 | enabled: true, 7 | path: '/webhooks/messenger', 8 | pageId: process.env.MESSENGER_PAGE_ID, 9 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 10 | appId: process.env.MESSENGER_APP_ID, 11 | appSecret: process.env.MESSENGER_APP_SECRET, 12 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 13 | }, 14 | }, 15 | 16 | plugins: [withTyping({ delay: 1000 })], 17 | }; 18 | -------------------------------------------------------------------------------- /website/src/css/customTheme.css: -------------------------------------------------------------------------------- 1 | :root{ 2 | --ifm-color-primary-lightest: #0D243E; 3 | --ifm-color-primary-lighter: #0B2037; 4 | --ifm-color-primary-light: #0B1F35; 5 | --ifm-color-primary: #0A1C30; 6 | --ifm-color-primary-dark: #09192B; 7 | --ifm-color-primary-darker: #081829; 8 | --ifm-color-primary-darkest: #071422; 9 | 10 | --ifm-footer-background-color: #0a1c30; 11 | --ifm-footer-color: #ffffff; 12 | --ifm-footer-link-color: #ffffff; 13 | --ifm-footer-link-hover-color: #ffffff; 14 | 15 | --ifm-link-color: var(--ifm-color-info-darkest); 16 | } 17 | -------------------------------------------------------------------------------- /packages/bottender-handlers/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bottender/handlers", 3 | "version": "1.5.1", 4 | "license": "MIT", 5 | "homepage": "https://bottender.js.org/", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/Yoctol/bottender.git" 9 | }, 10 | "main": "dist/index.js", 11 | "files": [ 12 | "dist" 13 | ], 14 | "types": "dist/index.d.ts", 15 | "dependencies": { 16 | "@types/koa-compose": "^3.2.5", 17 | "@types/warning": "^3.0.0", 18 | "koa-compose": "^4.1.0", 19 | "warning": "^4.0.3" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /packages/bottender/src/whatsapp/__tests__/WhatsappBot.spec.ts: -------------------------------------------------------------------------------- 1 | import WhatsappBot from '../WhatsappBot'; 2 | import WhatsappConnector from '../WhatsappConnector'; 3 | 4 | it('should construct bot with WhatsappConnector', () => { 5 | const bot = new WhatsappBot({ 6 | accountSid: 'ACCOUNT_SID', 7 | authToken: 'AUTH_TOKEN', 8 | }); 9 | expect(bot).toBeDefined(); 10 | expect(bot.onEvent).toBeDefined(); 11 | expect(bot.createRequestHandler).toBeDefined(); 12 | expect(bot.connector).toBeDefined(); 13 | expect(bot.connector).toBeInstanceOf(WhatsappConnector); 14 | }); 15 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parserOptions: { 3 | ecmaVersion: 2018, 4 | }, 5 | extends: ['eslint:recommended', 'prettier'], 6 | env: { 7 | node: true, 8 | }, 9 | rules: { 10 | 'prettier/prettier': [ 11 | 'error', 12 | { 13 | trailingComma: 'es5', 14 | singleQuote: true, 15 | }, 16 | ], 17 | }, 18 | plugins: ['prettier'], 19 | overrides: [ 20 | { 21 | files: ['**/*.test.js'], 22 | env: { 23 | jest: true, 24 | }, 25 | }, 26 | ], 27 | }; 28 | -------------------------------------------------------------------------------- /examples/browser-only/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "browser-only", 4 | "version": "0.1.0", 5 | "scripts": { 6 | "build": "react-scripts build", 7 | "start": "react-scripts start" 8 | }, 9 | "dependencies": { 10 | "@chentsulin/react-botui": "^0.2.1", 11 | "bottender": "latest", 12 | "botui": "^0.3.9", 13 | "react": "^16.12.0", 14 | "react-dom": "^16.12.0", 15 | "react-scripts": "3.3.0" 16 | }, 17 | "browserslist": [ 18 | ">0.2%", 19 | "not dead", 20 | "not ie <= 11", 21 | "not op_mini all" 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/routes/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright 2017-present, Facebook, Inc. All rights reserved. 3 | * 4 | * This source code is licensed under the license found in the 5 | * LICENSE file in the root directory of fbsamples/messenger-bot-samples. 6 | */ 7 | 8 | // ===== MODULES =============================================================== 9 | const { Router } = require('express'); 10 | 11 | const router = Router(); 12 | 13 | /* GET home page. */ 14 | router.get('/', function (req, res) { 15 | res.render('index'); 16 | }); 17 | 18 | module.exports = router; 19 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/viber/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import sh from '..'; 2 | 3 | jest.mock('../webhook'); 4 | jest.mock('../help'); 5 | 6 | describe('telegram cli', () => { 7 | it('should exist', () => { 8 | expect(sh).toBeDefined(); 9 | }); 10 | 11 | it('should return webhook module', () => { 12 | const webhook = require('../webhook').default; 13 | expect(sh.webhook).toEqual(webhook); 14 | }); 15 | 16 | it('should return help module', () => { 17 | const help = require('../help').default; 18 | expect(sh.help).toEqual(help); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.3.0/channel-whatsapp-sending-messages.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-whatsapp-sending-messages 3 | title: Sending WhatsApp Messages 4 | original_id: channel-whatsapp-sending-messages 5 | --- 6 | 7 | ## Sending Text Messages 8 | 9 | Use this method to send text messages. 10 | 11 | ```js 12 | async function SendHi(context) { 13 | await context.sendText('Hi!'); 14 | } 15 | ``` 16 | 17 | To send with media URLs, you may use `mediaUrl` options: 18 | 19 | ```js 20 | await context.sendText('Hi', { 21 | mediaUrl: ['https://example.com/image.jpg'], 22 | }); 23 | ``` 24 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.3.1/channel-whatsapp-sending-messages.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-whatsapp-sending-messages 3 | title: Sending WhatsApp Messages 4 | original_id: channel-whatsapp-sending-messages 5 | --- 6 | 7 | ## Sending Text Messages 8 | 9 | Use this method to send text messages. 10 | 11 | ```js 12 | async function SendHi(context) { 13 | await context.sendText('Hi!'); 14 | } 15 | ``` 16 | 17 | To send with media URLs, you may use `mediaUrl` options: 18 | 19 | ```js 20 | await context.sendText('Hi', { 21 | mediaUrl: ['https://example.com/image.jpg'], 22 | }); 23 | ``` 24 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.4/channel-whatsapp-sending-messages.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-whatsapp-sending-messages 3 | title: Sending WhatsApp Messages 4 | original_id: channel-whatsapp-sending-messages 5 | --- 6 | 7 | ## Sending Text Messages 8 | 9 | Use this method to send text messages. 10 | 11 | ```js 12 | async function SendHi(context) { 13 | await context.sendText('Hi!'); 14 | } 15 | ``` 16 | 17 | To send with media URLs, you may use `mediaUrl` options: 18 | 19 | ```js 20 | await context.sendText('Hi', { 21 | mediaUrl: ['https://example.com/image.jpg'], 22 | }); 23 | ``` 24 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.5/channel-whatsapp-sending-messages.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-whatsapp-sending-messages 3 | title: Sending WhatsApp Messages 4 | original_id: channel-whatsapp-sending-messages 5 | --- 6 | 7 | ## Sending Text Messages 8 | 9 | Use this method to send text messages. 10 | 11 | ```js 12 | async function SendHi(context) { 13 | await context.sendText('Hi!'); 14 | } 15 | ``` 16 | 17 | To send with media URLs, you may use `mediaUrl` options: 18 | 19 | ```js 20 | await context.sendText('Hi', { 21 | mediaUrl: ['https://example.com/image.jpg'], 22 | }); 23 | ``` 24 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/sh/utils/getSubArgs.ts: -------------------------------------------------------------------------------- 1 | import { Options, Result, Spec } from 'arg'; 2 | 3 | import getArgs from './getArgs'; 4 | 5 | const getSubArgs = ( 6 | argv: Result, 7 | argsOptions: T, 8 | argOptions: Options = { permissive: true } 9 | ): Result & Pick, Exclude> => { 10 | const { _, ...rest } = argv; 11 | 12 | return { 13 | ...getArgs(_, argsOptions, argOptions), 14 | ...rest, 15 | } as Result & Pick, Exclude>; 16 | }; 17 | 18 | export default getSubArgs; 19 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/telegram/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import sh from '..'; 2 | 3 | jest.mock('../webhook'); 4 | jest.mock('../help'); 5 | 6 | describe('telegram cli', () => { 7 | it('should exist', () => { 8 | expect(sh).toBeDefined(); 9 | }); 10 | 11 | it('should return webhook module', () => { 12 | const webhook = require('../webhook').default; 13 | expect(sh.webhook).toEqual(webhook); 14 | }); 15 | 16 | it('should return help module', () => { 17 | const help = require('../help').default; 18 | expect(sh.help).toEqual(help); 19 | }); 20 | }); 21 | -------------------------------------------------------------------------------- /packages/bottender/src/test-utils/SimulatedContext.ts: -------------------------------------------------------------------------------- 1 | import Context from '../context/Context'; 2 | import { Client, Event } from '../types'; 3 | 4 | export default class SimulatedContext< 5 | C extends Client = any, 6 | E extends Event = any 7 | > extends Context { 8 | _platform: string; 9 | 10 | constructor(options: any) { 11 | super(options); 12 | 13 | this._platform = options.platform; 14 | } 15 | 16 | get platform() { 17 | return this._platform; 18 | } 19 | 20 | // eslint-disable-next-line @typescript-eslint/no-empty-function 21 | sendText() {} 22 | } 23 | -------------------------------------------------------------------------------- /examples/custom-connector-facebook/bottender.config.js: -------------------------------------------------------------------------------- 1 | const { FacebookConnector } = require('@bottender/facebook'); 2 | 3 | module.exports = { 4 | channels: { 5 | facebook: { 6 | enabled: true, 7 | path: '/webhooks/facebook', 8 | connector: new FacebookConnector({ 9 | pageId: process.env.FACEBOOK_PAGE_ID, 10 | accessToken: process.env.FACEBOOK_ACCESS_TOKEN, 11 | appId: process.env.FACEBOOK_APP_ID, 12 | appSecret: process.env.FACEBOOK_APP_SECRET, 13 | verifyToken: process.env.FACEBOOK_VERIFY_TOKEN, 14 | }), 15 | }, 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /examples/with-rasa/index.js: -------------------------------------------------------------------------------- 1 | const { chain } = require('bottender'); 2 | const rasa = require('@bottender/rasa'); 3 | 4 | async function SayHello(context) { 5 | await context.sendText('Hello!'); 6 | } 7 | 8 | async function Unknown(context) { 9 | await context.sendText('Sorry, I don’t know what you say.'); 10 | } 11 | 12 | const Rasa = rasa({ 13 | origin: 'http://localhost:5005', 14 | actions: { 15 | greeting: SayHello, 16 | }, 17 | confidenceThreshold: 0.7, 18 | }); 19 | 20 | module.exports = async function App() { 21 | return chain([ 22 | Rasa, // 23 | Unknown, 24 | ]); 25 | }; 26 | -------------------------------------------------------------------------------- /examples/slack-update-and-delete/index.js: -------------------------------------------------------------------------------- 1 | module.exports = async function App(context) { 2 | if ( 3 | context.event.rawEvent.subtype === 'message_changed' || 4 | context.event.rawEvent.subtype === 'message_deleted' 5 | ) { 6 | return; 7 | } 8 | 9 | const response = await context.chat.postMessage({ 10 | text: 'This is a normal message by chat.postMessage.', 11 | }); 12 | 13 | const { ts, channel } = response; 14 | 15 | await context.chat.update({ 16 | text: 'Message updated.', 17 | ts, 18 | channel, 19 | }); 20 | 21 | await context.chat.delete({ 22 | ts, 23 | }); 24 | }; 25 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true 4 | }, 5 | "files": [], 6 | "include": [], 7 | "references": [ 8 | { "path": "./packages/bottender" }, 9 | { "path": "./packages/bottender-dialogflow" }, 10 | { "path": "./packages/bottender-express" }, 11 | { "path": "./packages/bottender-facebook" }, 12 | { "path": "./packages/bottender-handlers" }, 13 | { "path": "./packages/bottender-luis" }, 14 | { "path": "./packages/bottender-qna-maker" }, 15 | { "path": "./packages/bottender-rasa" }, 16 | { "path": "./packages/create-bottender-app" } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /website/static/img/element_modular.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /examples/with-dialogflow/index.js: -------------------------------------------------------------------------------- 1 | const { chain } = require('bottender'); 2 | const dialogflow = require('@bottender/dialogflow'); 3 | 4 | async function SayHello(context) { 5 | await context.sendText('Hello!'); 6 | } 7 | 8 | async function Unknown(context) { 9 | await context.sendText('Sorry, I don’t know what you say.'); 10 | } 11 | 12 | const Dialogflow = dialogflow({ 13 | projectId: process.env.GOOGLE_APPLICATION_PROJECT_ID, 14 | actions: { 15 | greeting: SayHello, 16 | }, 17 | }); 18 | 19 | module.exports = async function App() { 20 | return chain([ 21 | Dialogflow, // 22 | Unknown, 23 | ]); 24 | }; 25 | -------------------------------------------------------------------------------- /examples/with-qna-maker/index.js: -------------------------------------------------------------------------------- 1 | const { chain } = require('bottender'); 2 | const qnaMaker = require('@bottender/qna-maker'); 3 | 4 | const { RESOURCE_NAME, KNOWLEDGE_BASE_ID, ENDPOINT_KEY } = process.env; 5 | 6 | async function Unknown(context) { 7 | await context.sendText('Sorry, I don’t know what you say.'); 8 | } 9 | 10 | const QnaMaker = qnaMaker({ 11 | resourceName: RESOURCE_NAME, 12 | knowledgeBaseId: KNOWLEDGE_BASE_ID, 13 | endpointKey: ENDPOINT_KEY, 14 | scoreThreshold: 70, 15 | }); 16 | 17 | module.exports = async function App() { 18 | return chain([ 19 | QnaMaker, // 20 | Unknown, 21 | ]); 22 | }; 23 | -------------------------------------------------------------------------------- /examples/custom-server-express-typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/**/*"], 3 | "exclude": ["**/__tests__", "**/*.{spec,test}.ts"], 4 | "compilerOptions": { 5 | "target": "es2016", 6 | "lib": ["es2017", "es2018", "es2019", "esnext.asynciterable"], 7 | "module": "commonjs", 8 | "skipLibCheck": true, 9 | "moduleResolution": "node", 10 | "esModuleInterop": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "rootDir": "./src", 16 | "outDir": "./dist", 17 | "types": ["node"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/browser-only/README.md: -------------------------------------------------------------------------------- 1 | # Browser Only 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/browser-only 9 | cd browser-only 10 | npm install 11 | npm start 12 | ``` 13 | 14 | ![](https://user-images.githubusercontent.com/3382565/71249572-75487b00-2358-11ea-90a3-f4fdbd6dd4bb.gif) 15 | 16 | ## Idea of This Example 17 | 18 | This example is a bot running in your browser. It shows how to build a frontend bot (no server) with Bottender and React. 19 | -------------------------------------------------------------------------------- /packages/bottender-express/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bottender/express", 3 | "version": "1.5.1", 4 | "license": "MIT", 5 | "homepage": "https://bottender.js.org/", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/Yoctol/bottender.git" 9 | }, 10 | "main": "dist/index.js", 11 | "files": [ 12 | "dist" 13 | ], 14 | "types": "dist/index.d.ts", 15 | "dependencies": { 16 | "@types/body-parser": "^1.19.0", 17 | "@types/express": "^4.17.2", 18 | "@types/lodash": "^4.14.149", 19 | "body-parser": "^1.19.0", 20 | "express": "^4.17.1", 21 | "lodash": "^4.17.15" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Go to '...' 16 | 2. Call on '....' 17 | 3. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Additional context** 26 | Add any other context about the problem here. 27 | -------------------------------------------------------------------------------- /packages/bottender/src/viber/__tests__/ViberBot.spec.ts: -------------------------------------------------------------------------------- 1 | import ViberBot from '../ViberBot'; 2 | import ViberConnector from '../ViberConnector'; 3 | 4 | // FIXME: improve mock or remove mock 5 | jest.unmock('messaging-api-viber'); 6 | 7 | it('should construct bot with ViberConnector', () => { 8 | const bot = new ViberBot({ 9 | accessToken: 'FAKE_TOKEN', 10 | sender: { name: 'sender' }, 11 | }); 12 | 13 | expect(bot).toBeDefined(); 14 | expect(bot.onEvent).toBeDefined(); 15 | expect(bot.createRequestHandler).toBeDefined(); 16 | expect(bot.connector).toBeDefined(); 17 | expect(bot.connector).toBeInstanceOf(ViberConnector); 18 | }); 19 | -------------------------------------------------------------------------------- /packages/bottender/src/withProps.ts: -------------------------------------------------------------------------------- 1 | import partial from 'lodash/partial'; 2 | 3 | import Context from './context/Context'; 4 | import { Action } from './types'; 5 | 6 | function withProps>( 7 | action: Action, 8 | props: P 9 | ): Action { 10 | // TODO: we may only apply this on dev env 11 | Object.freeze(props); 12 | 13 | const actionWithProps = partial(action, partial.placeholder, props); 14 | 15 | Object.defineProperty(actionWithProps, 'name', { 16 | value: action.name || 'Anonymous', 17 | }); 18 | 19 | return actionWithProps; 20 | } 21 | 22 | export default withProps; 23 | -------------------------------------------------------------------------------- /packages/bottender/src/shared/log.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import chalk from 'chalk'; 3 | import figures from 'figures'; 4 | 5 | export function log(msg: string, color = 'blue', icon = 'pointer'): void { 6 | console.log(`${(chalk as any)[color]((figures as any)[icon])} ${msg}`); 7 | } 8 | 9 | export function print(msg: string): void { 10 | log(msg, 'green'); 11 | } 12 | 13 | export function warn(msg: string): void { 14 | log(msg, 'yellow', 'warning'); 15 | } 16 | 17 | export function error(msg: string): void { 18 | log(msg, 'red', 'cross'); 19 | } 20 | 21 | export function bold(msg: string): string { 22 | return chalk.bold(msg); 23 | } 24 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template-typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": ["src/**/*"], 3 | "exclude": ["**/__tests__", "**/*.{spec,test}.ts"], 4 | "compilerOptions": { 5 | "target": "es2016", 6 | "lib": ["es2017", "es2018", "es2019", "es2020", "esnext.asynciterable"], 7 | "module": "commonjs", 8 | "skipLibCheck": true, 9 | "moduleResolution": "node", 10 | "esModuleInterop": true, 11 | "strict": true, 12 | "forceConsistentCasingInFileNames": true, 13 | "resolveJsonModule": true, 14 | "isolatedModules": true, 15 | "rootDir": "./src", 16 | "outDir": "./dist", 17 | "types": ["node", "jest"] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/views/create-account-success.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Copyright 2017-present, Facebook, Inc. All rights reserved. 3 | 4 | This source code is licensed under the license found in the 5 | LICENSE file in the root directory of fbsamples/messenger-bot-samples. 6 | 7 | extends layout 8 | block head 9 | - var title = 'Account Created!' 10 | block content 11 | div(class='main row') 12 | div(class='column') 13 | div(class='header') 14 | h2 Almost done... 15 | div(class='subtitle') 16 | p 17 | | You've created a Jasper's account! Now, just 18 | strong  log in  19 | | to get started. 20 | -------------------------------------------------------------------------------- /crowdin.yaml: -------------------------------------------------------------------------------- 1 | project_identifier_env: CROWDIN_BOTTENDER_PROJECT_ID 2 | api_key_env: CROWDIN_BOTTENDER_API_KEY 3 | base_path: './' 4 | preserve_hierarchy: true 5 | 6 | files: 7 | - source: '/docs/**/*.md' 8 | translation: '/website/translated_docs/%locale%/**/%original_file_name%' 9 | languages_mapping: &anchor 10 | locale: 11 | 'ja': 'ja' 12 | 'zh-TW': 'zh-TW' 13 | - source: '/website/versioned_docs/**/*.md' 14 | translation: '/website/translated_docs/%locale%/**/%original_file_name%' 15 | languages_mapping: *anchor 16 | - source: '/website/i18n/en.json' 17 | translation: '/website/i18n/%locale%.json' 18 | languages_mapping: *anchor 19 | -------------------------------------------------------------------------------- /examples/messenger-handover/index.js: -------------------------------------------------------------------------------- 1 | // This bot should be assigned as primary receiver app 2 | module.exports = async function App(context) { 3 | if (context.event.isText && !context.event.isEcho) { 4 | if (context.event.isStandby) { 5 | if (context.event.text === '/back') { 6 | await context.takeThreadControl(); 7 | await context.sendText('Taking thread control back.'); 8 | } 9 | } else if (context.event.text === '/help') { 10 | await context.sendText('Passing thread control to the page inbox.'); 11 | await context.passThreadControlToPageInbox(); 12 | } else { 13 | await context.sendText('Respond by bot.'); 14 | } 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 11 | 12 | **Describe the solution you'd like** 13 | A clear and concise description of what you want to happen. 14 | 15 | **Describe alternatives you've considered** 16 | A clear and concise description of any alternative solutions or features you've considered. 17 | 18 | **Additional context** 19 | Add any other context or screenshots about the feature request here. 20 | -------------------------------------------------------------------------------- /examples/messenger-batch/bottender.config.js: -------------------------------------------------------------------------------- 1 | const { isError613 } = require('messenger-batch'); 2 | 3 | module.exports = { 4 | channels: { 5 | messenger: { 6 | enabled: true, 7 | path: '/webhooks/messenger', 8 | pageId: process.env.MESSENGER_PAGE_ID, 9 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 10 | appId: process.env.MESSENGER_APP_ID, 11 | appSecret: process.env.MESSENGER_APP_SECRET, 12 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 13 | batchConfig: { 14 | delay: 1000, 15 | shouldRetry: isError613, // (#613) Calls to this api have exceeded the rate limit. 16 | retryTimes: 2, 17 | }, 18 | }, 19 | }, 20 | }; 21 | -------------------------------------------------------------------------------- /packages/bottender/src/session/RedisSessionStore.ts: -------------------------------------------------------------------------------- 1 | import RedisCacheStore from '../cache/RedisCacheStore'; 2 | 3 | import CacheBasedSessionStore from './CacheBasedSessionStore'; 4 | import SessionStore from './SessionStore'; 5 | 6 | type RedisOption = 7 | | number 8 | | string 9 | | { 10 | port?: number; 11 | host?: string; 12 | family?: number; 13 | password?: string; 14 | db?: number; 15 | }; 16 | 17 | export default class RedisSessionStore 18 | extends CacheBasedSessionStore 19 | implements SessionStore 20 | { 21 | constructor(arg: RedisOption, expiresIn?: number) { 22 | const cache = new RedisCacheStore(arg); 23 | super(cache, expiresIn); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /packages/bottender/src/browser.ts: -------------------------------------------------------------------------------- 1 | /* Core */ 2 | export { default as Bot } from './bot/Bot'; 3 | export { default as Context } from './context/Context'; 4 | 5 | /* Action */ 6 | export { default as chain } from './chain'; 7 | export { default as withProps } from './withProps'; 8 | 9 | /* Cache */ 10 | export { default as MemoryCacheStore } from './cache/MemoryCacheStore'; 11 | 12 | /* Session */ 13 | export { default as CacheBasedSessionStore } from './session/CacheBasedSessionStore'; 14 | export { default as MemorySessionStore } from './session/MemorySessionStore'; 15 | 16 | /** 17 | * Private Exports (unstable) 18 | */ 19 | 20 | /* Plugins */ 21 | export { default as withTyping } from './plugins/withTyping'; 22 | -------------------------------------------------------------------------------- /examples/slack-slash-command/index.js: -------------------------------------------------------------------------------- 1 | async function HandleSlashCommand(context) { 2 | // check the action from button or menu 3 | console.log(context.event.command); 4 | console.log(context.event.text); 5 | 6 | await context.sendText( 7 | `I received slash command '${context.event.command}' with arguments: '${context.event.text}'` 8 | ); 9 | } 10 | 11 | async function HandleDefaultEvent(context) { 12 | await context.sendText("I didn't receive a slash command"); 13 | } 14 | 15 | module.exports = async function App(context) { 16 | // check if an event is from slash command 17 | if (context.event.isCommand) { 18 | return HandleSlashCommand; 19 | } 20 | 21 | return HandleDefaultEvent; 22 | }; 23 | -------------------------------------------------------------------------------- /examples/with-rasa/README.md: -------------------------------------------------------------------------------- 1 | # With Rasa 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [Bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/with-rasa 9 | cd with-rasa 10 | npm install 11 | ``` 12 | 13 | Before you run this example, make sure you have start the Rasa server locally on port 5005 with your NLU model following [The official Guide](https://rasa.com/docs/rasa/nlu/using-nlu-only/). 14 | 15 | ```sh 16 | npm run dev -- --console 17 | ``` 18 | 19 | ## Idea of This Example 20 | 21 | This example shows how to combine your bot with [Rasa NLU](https://rasa.com/docs/rasa/nlu/about/). 22 | -------------------------------------------------------------------------------- /packages/create-bottender-app/src/shared/log.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import chalk from 'chalk'; 3 | import figures from 'figures'; 4 | 5 | export function log( 6 | msg: string, 7 | color: 'green' | 'yellow' | 'red' | 'blue' = 'blue', 8 | icon: keyof typeof figures = 'pointer' 9 | ): void { 10 | console.log(`${chalk[color](figures[icon])} ${msg}`); 11 | } 12 | 13 | export function print(msg = ''): void { 14 | log(msg, 'green'); 15 | } 16 | 17 | export function warn(msg = ''): void { 18 | log(msg, 'yellow', 'warning'); 19 | } 20 | 21 | export function error(msg = ''): void { 22 | log(msg, 'red', 'cross'); 23 | } 24 | 25 | export function bold(msg = ''): string { 26 | return chalk.bold(msg); 27 | } 28 | -------------------------------------------------------------------------------- /examples/with-luis.ai/index.js: -------------------------------------------------------------------------------- 1 | const { chain } = require('bottender'); 2 | const luis = require('@bottender/luis'); 3 | 4 | const { LUIS_APP_KEY, LUIS_APP_ENDPOINT, LUIS_APP_ID } = process.env; 5 | 6 | async function SayHello(context) { 7 | await context.sendText('Hello!'); 8 | } 9 | 10 | async function Unknown(context) { 11 | await context.sendText('Sorry, I don’t know what you say.'); 12 | } 13 | 14 | const Luis = luis({ 15 | appId: LUIS_APP_ID, 16 | appKey: LUIS_APP_KEY, 17 | endpoint: LUIS_APP_ENDPOINT, 18 | actions: { 19 | greeting: SayHello, 20 | }, 21 | scoreThreshold: 0.7, 22 | }); 23 | 24 | module.exports = async function App() { 25 | return chain([ 26 | Luis, // 27 | Unknown, 28 | ]); 29 | }; 30 | -------------------------------------------------------------------------------- /examples/with-qna-maker/README.md: -------------------------------------------------------------------------------- 1 | # With QnA Maker 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [Bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/with-qna-maker 9 | cd with-qna-maker 10 | npm install 11 | ``` 12 | 13 | Before you run this example, make sure you have got `RESOURCE_NAME`, `KNOWLEDGE_BASE_ID`, and `ENDPOINT_KEY` from [QnA Maker](https://www.qnamaker.ai/) and filled in your `.env` file. 14 | 15 | ```sh 16 | npm run dev -- --console 17 | ``` 18 | 19 | ## Idea of This Example 20 | 21 | This example shows how to integrate your bot with [QnA Maker](https://www.qnamaker.ai/). 22 | -------------------------------------------------------------------------------- /packages/bottender-facebook/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bottender/facebook", 3 | "version": "1.5.5", 4 | "license": "MIT", 5 | "homepage": "https://bottender.js.org/", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/Yoctol/bottender.git" 9 | }, 10 | "main": "dist/index.js", 11 | "files": [ 12 | "dist" 13 | ], 14 | "types": "dist/index.d.ts", 15 | "dependencies": { 16 | "axios-error": "^1.0.4", 17 | "facebook-batch": "1.0.6", 18 | "lodash": "^4.17.21", 19 | "type-fest": "^1.4.0", 20 | "warning": "^4.0.3" 21 | }, 22 | "peerDependencies": { 23 | "bottender": ">= 1.5.1-alpha.4" 24 | }, 25 | "devDependencies": { 26 | "bottender": "^1.5.5" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /packages/bottender/src/__tests__/withProps.spec.ts: -------------------------------------------------------------------------------- 1 | import Context from '../context/Context'; 2 | import withProps from '../withProps'; 3 | import { Props } from '../types'; 4 | import { run } from '../bot/Bot'; 5 | 6 | function setup() { 7 | const context = { 8 | sendText: jest.fn(), 9 | }; 10 | 11 | return { 12 | context, 13 | }; 14 | } 15 | 16 | async function SendSomeText(context: Context, { text }: Props) { 17 | await context.sendText(text); 18 | } 19 | 20 | it('should support', async () => { 21 | const { context } = setup(); 22 | 23 | const SendHello = withProps(SendSomeText, { text: 'hello' }); 24 | 25 | await run(SendHello)(context); 26 | 27 | expect(context.sendText).toBeCalledWith('hello'); 28 | }); 29 | -------------------------------------------------------------------------------- /packages/bottender/src/messenger/__tests__/MessengerBot.spec.ts: -------------------------------------------------------------------------------- 1 | import MessengerBot from '../MessengerBot'; 2 | import MessengerConnector from '../MessengerConnector'; 3 | 4 | beforeEach(() => { 5 | console.error = jest.fn(); 6 | }); 7 | 8 | const ACCESS_TOKEN = 'FAKE_TOKEN'; 9 | const APP_SECRET = 'FAKE_SECRET'; 10 | 11 | it('should construct bot with MessengerConnector', () => { 12 | const bot = new MessengerBot({ 13 | accessToken: ACCESS_TOKEN, 14 | appSecret: APP_SECRET, 15 | }); 16 | expect(bot).toBeDefined(); 17 | expect(bot.onEvent).toBeDefined(); 18 | expect(bot.createRequestHandler).toBeDefined(); 19 | expect(bot.connector).toBeDefined(); 20 | expect(bot.connector).toBeInstanceOf(MessengerConnector); 21 | }); 22 | -------------------------------------------------------------------------------- /packages/bottender/src/session/__tests__/RedisSessionStore.spec.ts: -------------------------------------------------------------------------------- 1 | import CacheBasedSessionStore from '../CacheBasedSessionStore'; 2 | import RedisSessionStore from '../RedisSessionStore'; 3 | 4 | it('should be instanceof CacheBasedSessionStore', () => { 5 | expect(new RedisSessionStore(6379)).toBeInstanceOf(CacheBasedSessionStore); 6 | expect( 7 | new RedisSessionStore({ 8 | port: 6379, // Redis port 9 | host: '127.0.0.1', // Redis host 10 | family: 4, // 4 (IPv4) or 6 (IPv6) 11 | password: 'auth', 12 | db: 0, 13 | }) 14 | ).toBeInstanceOf(CacheBasedSessionStore); 15 | expect( 16 | new RedisSessionStore('redis://:authpassword@127.0.0.1:6380/4') 17 | ).toBeInstanceOf(CacheBasedSessionStore); 18 | }); 19 | -------------------------------------------------------------------------------- /examples/messenger-handover/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | messenger: { 4 | enabled: true, 5 | path: '/webhooks/messenger', 6 | pageId: process.env.MESSENGER_PAGE_ID, 7 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 8 | appId: process.env.MESSENGER_APP_ID, 9 | appSecret: process.env.MESSENGER_APP_SECRET, 10 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 11 | fields: [ 12 | 'messages', 13 | 'messaging_postbacks', 14 | 'messaging_optins', 15 | 'messaging_referrals', 16 | 'messaging_handovers', 17 | 'messaging_policy_enforcement', 18 | 'message_echoes', 19 | 'standby', 20 | ], 21 | }, 22 | }, 23 | }; 24 | -------------------------------------------------------------------------------- /examples/session-memory/README.md: -------------------------------------------------------------------------------- 1 | # Session: Memory 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/session-memory 9 | cd session-memory 10 | npm install 11 | npm run dev -- --console 12 | ``` 13 | 14 | ## Idea of This Example 15 | 16 | This example shows how to use session to store some information from users in memory. For more information, check our [session guide](https://bottender.js.org/docs/the-basics-session). 17 | 18 | ## Related Examples 19 | 20 | - [session-file](../session-file) 21 | - [session-mongo](../session-mongo) 22 | - [session-redis](../session-redis) 23 | -------------------------------------------------------------------------------- /website/static/img/element_flexible.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /examples/session-file/README.md: -------------------------------------------------------------------------------- 1 | # Session: File 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/session-file 9 | cd session-file 10 | npm install 11 | npm run dev -- --console 12 | ``` 13 | 14 | ## Idea of This Example 15 | 16 | This example shows how to use session to store some information from users in your local files. For more information, check our [session guide](https://bottender.js.org/docs/the-basics-session). 17 | 18 | ## Related Examples 19 | 20 | - [session-memory](../session-memory) 21 | - [session-mongo](../session-mongo) 22 | - [session-redis](../session-redis) 23 | -------------------------------------------------------------------------------- /examples/session-mongo/README.md: -------------------------------------------------------------------------------- 1 | # Session: MongoDB 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/session-mongo 9 | cd session-mongo 10 | npm install 11 | npm run dev -- --console 12 | ``` 13 | 14 | ## Idea of This Example 15 | 16 | This example shows how to use session to store some information from users in your MongoDB. For more information, check our [session guide](https://bottender.js.org/docs/the-basics-session). 17 | 18 | ## Related Examples 19 | 20 | - [session-memory](../session-memory) 21 | - [session-file](../session-file) 22 | - [session-redis](../session-redis) 23 | -------------------------------------------------------------------------------- /examples/with-luis.ai/README.md: -------------------------------------------------------------------------------- 1 | # With LUIS.ai 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [Bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/with-luis.ai 9 | cd with-luis.ai 10 | npm install 11 | ``` 12 | 13 | Before you run this example, make sure you have got `LUIS_APP_ID`, `LUIS_APP_KEY`, and `LUIS_APP_ENDPOINT` from [LUIS.ai](https://www.luis.ai/) and filled in your `.env` file. 14 | 15 | ```sh 16 | npm run dev -- --console 17 | ``` 18 | 19 | ## Idea of This Example 20 | 21 | This example shows how to combine your bot with **semantic analysis tool**. In this case, we take [LUIS.ai](https://www.luis.ai/) as an example. 22 | -------------------------------------------------------------------------------- /packages/create-bottender-app/template-typescript/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | parser: '@typescript-eslint/parser', 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/eslint-recommended', 6 | 'plugin:@typescript-eslint/recommended', 7 | 'prettier', 8 | 'prettier/@typescript-eslint', 9 | ], 10 | env: { 11 | node: true, 12 | }, 13 | rules: { 14 | 'prettier/prettier': [ 15 | 'error', 16 | { 17 | trailingComma: 'es5', 18 | singleQuote: true, 19 | }, 20 | ], 21 | }, 22 | plugins: ['import', 'prettier', '@typescript-eslint'], 23 | overrides: [ 24 | { 25 | files: ['**/*.test.ts'], 26 | env: { 27 | jest: true, 28 | }, 29 | }, 30 | ], 31 | }; 32 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/views/layout.jade: -------------------------------------------------------------------------------- 1 | //- 2 | Copyright 2017-present, Facebook, Inc. All rights reserved. 3 | 4 | This source code is licensed under the license found in the 5 | LICENSE file in the root directory of fbsamples/messenger-bot-samples. 6 | 7 | doctype html 8 | html 9 | block head 10 | - var title = title ? title : "Jasper's Market - Account Linking Demo" 11 | title #{title} 12 | link(rel='stylesheet', href='https://fonts.googleapis.com/css?family=Roboto:100,300,400') 13 | link(rel='stylesheet', href='/stylesheets/style.css') 14 | script(src="/scripts/disable-submit.js") 15 | meta(name='viewport', content='width=device-width, initial-scale=1, maximum-scale=1, user-scalable=0') 16 | 17 | body 18 | block content 19 | -------------------------------------------------------------------------------- /examples/with-state/README.md: -------------------------------------------------------------------------------- 1 | # State 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/with-state 9 | cd with-state 10 | npm install 11 | npm run dev -- --console 12 | ``` 13 | 14 | ## Idea of This Example 15 | 16 | This example shows how to use session state to store some information from users. For more information, check our [session guide](https://bottender.js.org/docs/the-basics-session). 17 | 18 | ## Related Examples 19 | 20 | - [session-memory](../session-memory) 21 | - [session-file](../session-file) 22 | - [session-mongo](../session-mongo) 23 | - [session-redis](../session-redis) 24 | -------------------------------------------------------------------------------- /packages/bottender/src/shared/getBottenderConfig.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | 3 | import dotenv from 'dotenv'; 4 | 5 | import { BottenderConfig } from '../types'; 6 | 7 | dotenv.config(); 8 | 9 | /** 10 | * By default, it will try to require the module from `/bottender.config.js`. 11 | */ 12 | const getBottenderConfig = (): BottenderConfig | never => { 13 | try { 14 | // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-var-requires 15 | return require(path.resolve('bottender.config.js')); 16 | } catch (err) { 17 | // if config is not found, return empty config 18 | if (err.code && err.code === 'MODULE_NOT_FOUND') { 19 | return {}; 20 | } 21 | 22 | throw err; 23 | } 24 | }; 25 | 26 | export default getBottenderConfig; 27 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/viber/help.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | const help = (): void => { 4 | console.log(` 5 | bottender viber [options] 6 | 7 | ${chalk.dim('Commands:')} 8 | 9 | webhook Manage webhook 10 | 11 | ${chalk.dim('Actions:')} 12 | 13 | set Set the property you request 14 | del, delete Delete the webhook 15 | 16 | ${chalk.dim('Options:')} 17 | 18 | -w, --webhook Webhook callback URL 19 | --ngrok-port ngrok port(default: 4040) 20 | 21 | ${chalk.dim('Examples:')} 22 | 23 | ${chalk.dim('-')} Set viber webhook 24 | 25 | ${chalk.cyan('$ bottender viber webhook set -w http://example.com')} 26 | `); 27 | }; 28 | 29 | export default help; 30 | -------------------------------------------------------------------------------- /website/static/img/element_morden.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/create-bottender-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "create-bottender-app", 3 | "version": "1.5.2", 4 | "bin": { 5 | "create-bottender-app": "bin/cli.js" 6 | }, 7 | "files": [ 8 | "dist", 9 | "template", 10 | "template-typescript" 11 | ], 12 | "types": "dist/index.d.ts", 13 | "dependencies": { 14 | "@types/cross-spawn": "^6.0.2", 15 | "@types/figures": "^3.0.1", 16 | "@types/fs-extra": "^9.0.13", 17 | "@types/inquirer": "^7.3.3", 18 | "@types/node": "^16.10.2", 19 | "chalk": "^4.1.2", 20 | "commander": "^4.1.1", 21 | "cross-spawn": "^7.0.3", 22 | "envinfo": "^7.8.1", 23 | "figures": "^3.0.0", 24 | "fs-extra": "^9.1.0", 25 | "inquirer": "^8.1.5", 26 | "prettier": "^2.4.1", 27 | "validate-npm-package-name": "^3.0.0" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/messenger/help.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | const help = (): void => { 4 | console.log(` 5 | bottender messenger 6 | 7 | ${chalk.dim('Commands:')} 8 | 9 | profile Manage Messenger profile 10 | webhook Manage webhook 11 | persona Manage persona 12 | 13 | ${chalk.dim('Actions:')} 14 | 15 | set Set the property you request 16 | del, delete Delete the property you request 17 | help Show more detail usage for the command 18 | 19 | ${chalk.dim('Examples:')} 20 | 21 | ${chalk.dim('-')} Set the messenger profile 22 | 23 | ${chalk.cyan('$ bottender messenger profile set')} 24 | `); 25 | }; 26 | 27 | export default help; 28 | -------------------------------------------------------------------------------- /website/static/css/code-blocks-buttons.css: -------------------------------------------------------------------------------- 1 | /* "Copy" code block button */ 2 | pre { 3 | position: relative; 4 | } 5 | 6 | pre .btnIcon { 7 | position: absolute; 8 | top: 8px; 9 | right: 12px; 10 | z-index: 2; 11 | cursor: pointer; 12 | border: 1px solid transparent; 13 | padding: 0; 14 | color: #000; 15 | background-color: transparent; 16 | height: 30px; 17 | transition: all .25s ease-out; 18 | } 19 | 20 | pre .btnIcon:hover { 21 | text-decoration: none; 22 | } 23 | 24 | .btnIcon__body { 25 | background-color: white; 26 | padding: 4px 6px; 27 | border-radius: 4px; 28 | align-items: center; 29 | display: flex; 30 | } 31 | 32 | .btnIcon svg { 33 | fill: currentColor; 34 | margin-right: .4em; 35 | } 36 | 37 | .btnIcon__label { 38 | font-size: 11px; 39 | } 40 | 41 | .btnClipboard { 42 | right: 10px; 43 | } 44 | -------------------------------------------------------------------------------- /packages/bottender/src/bot/Connector.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events'; 2 | 3 | import { JsonObject } from 'type-fest'; 4 | 5 | import Session from '../session/Session'; 6 | import { Event } from '../context/Event'; 7 | import { RequestContext } from '../types'; 8 | 9 | export interface Connector { 10 | client?: C; 11 | platform: string; 12 | getUniqueSessionKey( 13 | bodyOrEvent: B | Event, 14 | requestContext?: RequestContext 15 | ): string | null; 16 | updateSession(session: Session, bodyOrEvent: B | Event): Promise; 17 | mapRequestToEvents(body: B): Event[]; 18 | createContext(params: { 19 | event: Event; 20 | session?: Session | null; 21 | initialState?: JsonObject | null; 22 | requestContext?: RequestContext; 23 | emitter?: EventEmitter | null; 24 | }): any; 25 | } 26 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/bottender.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | channels: { 3 | messenger: { 4 | enabled: true, 5 | path: '/webhooks/messenger', 6 | pageId: process.env.MESSENGER_PAGE_ID, 7 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 8 | appId: process.env.MESSENGER_APP_ID, 9 | appSecret: process.env.MESSENGER_APP_SECRET, 10 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 11 | fields: [ 12 | // For text message events 13 | 'messages', 14 | // For the get started button 15 | 'messaging_postbacks', 16 | // For account_linking linked and unlinked events 17 | 'messaging_account_linking', 18 | ], 19 | profile: { 20 | getStarted: { 21 | payload: 'GET_STARTED', 22 | }, 23 | }, 24 | }, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.0.5/channel-slack-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-slack-handling-events 3 | title: Handling Slack Events 4 | original_id: channel-slack-handling-events 5 | --- 6 | 7 | ## Handling Text Events 8 | 9 | The most common event that your bot would ever receive is text message. To determine whether the event is a text message event, you may use `context.event.isText` boolean value to do that: 10 | 11 | ```js 12 | async function App(context) { 13 | if (context.event.isText) { 14 | // handling the text message event 15 | } 16 | } 17 | ``` 18 | 19 | You can get the text content using `context.event.text` and use it in the reply: 20 | 21 | ```js 22 | async function App(context) { 23 | if (context.event.isText) { 24 | await context.sendText(`received the text message: ${context.event.text}`); 25 | } 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.0.5/channel-viber-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-viber-handling-events 3 | title: Handling Viber Events 4 | original_id: channel-viber-handling-events 5 | --- 6 | 7 | ## Handling Text Events 8 | 9 | The most common event that your bot would ever receive is text message. To determine whether the event is a text message event, you may use `context.event.isText` boolean value to do that: 10 | 11 | ```js 12 | async function App(context) { 13 | if (context.event.isText) { 14 | // handling the text message event 15 | } 16 | } 17 | ``` 18 | 19 | You can get the text content using `context.event.text` and use it in the reply: 20 | 21 | ```js 22 | async function App(context) { 23 | if (context.event.isText) { 24 | await context.sendText(`received the text message: ${context.event.text}`); 25 | } 26 | } 27 | ``` 28 | -------------------------------------------------------------------------------- /packages/bottender-luis/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bottender/luis", 3 | "version": "1.5.5", 4 | "description": "LUIS integration for Bottender.", 5 | "license": "MIT", 6 | "homepage": "https://bottender.js.org/", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/Yoctol/bottender.git" 10 | }, 11 | "main": "dist/index.js", 12 | "files": [ 13 | "dist" 14 | ], 15 | "types": "dist/index.d.ts", 16 | "dependencies": { 17 | "axios": "^0.21.4", 18 | "invariant": "^2.2.4" 19 | }, 20 | "peerDependencies": { 21 | "bottender": ">= 1.2.0-0" 22 | }, 23 | "devDependencies": { 24 | "bottender": "^1.5.5" 25 | }, 26 | "keywords": [ 27 | "bot", 28 | "bottender", 29 | "chatbot", 30 | "language", 31 | "luis", 32 | "messaging", 33 | "nlu", 34 | "understanding" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /examples/messenger-built-in-nlp/index.js: -------------------------------------------------------------------------------- 1 | const { withProps } = require('bottender'); 2 | 3 | async function NoDatetime(context) { 4 | await context.sendText('Not a date or time'); 5 | } 6 | 7 | async function ReplyDatetime(context, { datetime }) { 8 | if (datetime.type === 'value') { 9 | await context.sendText(`Did you mean ${dt.value} ? :)`); 10 | } else if (datetime.type === 'interval') { 11 | const { from, to } = datetime; 12 | await context.sendText( 13 | `Did you mean from ${from.value} to ${to.value} ? :)` 14 | ); 15 | } 16 | } 17 | 18 | module.exports = async function App(context) { 19 | console.log(context.event.message.nlp.entities); 20 | const { datetime } = context.event.message.nlp.entities; 21 | 22 | if (!datetime) { 23 | return NoDatetime; 24 | } 25 | 26 | return withProps(ReplyDatetime, { datetime: datetime[0] }); 27 | }; 28 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/telegram/help.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | const help = (): void => { 4 | console.log(` 5 | bottender telegram [options] 6 | 7 | ${chalk.dim('Commands:')} 8 | 9 | webhook Manage webhook 10 | 11 | ${chalk.dim('Actions:')} 12 | 13 | get Get the webhook information 14 | set Set the property you request 15 | del, delete Delete the webhook 16 | 17 | ${chalk.dim('Options:')} 18 | 19 | -w, --webhook Webhook callback URL 20 | --ngrok-port ngrok port(default: 4040) 21 | 22 | ${chalk.dim('Examples:')} 23 | 24 | ${chalk.dim('-')} Set telegram webhook 25 | 26 | ${chalk.cyan('$ bottender telegram webhook set -w http://example.com')} 27 | `); 28 | }; 29 | 30 | export default help; 31 | -------------------------------------------------------------------------------- /packages/bottender-express/src/createServer.ts: -------------------------------------------------------------------------------- 1 | import bodyParser from 'body-parser'; 2 | import express from 'express'; 3 | 4 | import registerRoutes from './registerRoutes'; 5 | import { Bot, RouteConfig } from './types'; 6 | 7 | function createServer(bot: Bot, config: RouteConfig = {}) { 8 | const server = express(); 9 | 10 | server.use( 11 | bodyParser.urlencoded({ 12 | extended: false, 13 | verify: (req: express.Request & { rawBody?: string }, _, buf) => { 14 | req.rawBody = buf.toString(); 15 | }, 16 | }) 17 | ); 18 | 19 | server.use( 20 | bodyParser.json({ 21 | verify: (req: express.Request & { rawBody?: string }, _, buf) => { 22 | req.rawBody = buf.toString(); 23 | }, 24 | }) 25 | ); 26 | 27 | registerRoutes(server, bot, config); 28 | 29 | return server; 30 | } 31 | 32 | export default createServer; 33 | -------------------------------------------------------------------------------- /packages/bottender-qna-maker/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bottender/qna-maker", 3 | "version": "1.5.5", 4 | "description": "QnA Maker integration for Bottender.", 5 | "license": "MIT", 6 | "homepage": "https://bottender.js.org/", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/Yoctol/bottender.git" 10 | }, 11 | "main": "dist/index.js", 12 | "files": [ 13 | "dist" 14 | ], 15 | "types": "dist/index.d.ts", 16 | "dependencies": { 17 | "axios": "^0.21.4", 18 | "invariant": "^2.2.4" 19 | }, 20 | "peerDependencies": { 21 | "bottender": ">= 1.2.0-0" 22 | }, 23 | "devDependencies": { 24 | "bottender": "^1.5.5" 25 | }, 26 | "keywords": [ 27 | "bot", 28 | "bottender", 29 | "chatbot", 30 | "language", 31 | "messaging", 32 | "nlu", 33 | "qna-maker", 34 | "understanding" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/bottender/src/__tests__/browser.spec.ts: -------------------------------------------------------------------------------- 1 | import * as core from '../browser'; 2 | 3 | describe('browser', () => { 4 | it('export bots', () => { 5 | expect(core.Bot).toBeDefined(); 6 | }); 7 | 8 | it('export cache implements', () => { 9 | expect(core.MemoryCacheStore).toBeDefined(); 10 | }); 11 | 12 | it('export session stores', () => { 13 | expect(core.CacheBasedSessionStore).toBeDefined(); 14 | expect(core.MemorySessionStore).toBeDefined(); 15 | }); 16 | 17 | it('export contexts', () => { 18 | expect(core.Context).toBeDefined(); 19 | }); 20 | 21 | it('export extensions', () => { 22 | expect(core.withTyping).toBeDefined(); 23 | }); 24 | 25 | it('export chain', () => { 26 | expect(core.chain).toBeDefined(); 27 | }); 28 | 29 | it('export withProps', () => { 30 | expect(core.withProps).toBeDefined(); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /examples/with-gcp-cloud-function/server.js: -------------------------------------------------------------------------------- 1 | const bodyParser = require('body-parser'); 2 | const express = require('express'); 3 | const { bottender } = require('bottender'); 4 | 5 | const app = bottender({ 6 | dev: process.env.NODE_ENV !== 'production', 7 | }); 8 | 9 | const port = Number(process.env.PORT) || 5000; 10 | 11 | const handle = app.getRequestHandler(); 12 | 13 | const server = express(); 14 | server.use( 15 | bodyParser.json({ 16 | verify: (req, _, buf) => { 17 | req.rawBody = buf.toString(); 18 | }, 19 | }) 20 | ); 21 | 22 | server.all('*', (req, res) => { 23 | app.prepare().then(() => { 24 | return handle(req, res); 25 | }); 26 | }); 27 | 28 | server.listen(port, (err) => { 29 | if (err) { 30 | console.log(err); 31 | throw err; 32 | } 33 | console.log(`> Ready on http://localhost:${port}`); 34 | }); 35 | 36 | module.exports = { server }; 37 | -------------------------------------------------------------------------------- /packages/bottender-luis/src/types.ts: -------------------------------------------------------------------------------- 1 | export type LuisResult = { 2 | alteredQuery: string; 3 | compositeEntities: CompositeEntityModel[]; 4 | connectedServiceResult: LuisResult; 5 | entities: EntityModel[]; 6 | intents: IntentModel[]; 7 | query: string; 8 | sentimentAnalysis: Sentiment; 9 | topScoringIntent: IntentModel; 10 | }; 11 | 12 | export type CompositeEntityModel = { 13 | children: CompositeChildModel[]; 14 | parentType: string; 15 | value: string; 16 | }; 17 | 18 | export type CompositeChildModel = { 19 | type: string; 20 | value: string; 21 | }; 22 | 23 | export type IntentModel = { 24 | intent: string; 25 | score: number; 26 | }; 27 | 28 | export type EntityModel = { 29 | endIndex: number; 30 | entity: string; 31 | startIndex: number; 32 | type: string; 33 | }; 34 | 35 | export type Sentiment = { 36 | label: string; 37 | score: number; 38 | }; 39 | -------------------------------------------------------------------------------- /tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "target": "es2016", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "esModuleInterop": true, 8 | "sourceMap": true, 9 | "declaration": true, 10 | "declarationMap": true, 11 | "removeComments": true, 12 | "strict": true, 13 | "strictNullChecks": true, 14 | "strictFunctionTypes": true, 15 | "strictPropertyInitialization": true, 16 | "noImplicitThis": true, 17 | "noImplicitAny": true, 18 | "noImplicitReturns": true, 19 | "noFallthroughCasesInSwitch": true, 20 | "noUnusedParameters": true, 21 | "noUnusedLocals": true, 22 | "forceConsistentCasingInFileNames": true, 23 | "lib": ["es2017", "esnext.asynciterable"], 24 | "types": ["node"], 25 | "baseUrl": ".", 26 | "paths": { 27 | "*": ["types/*"] 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /packages/bottender-dialogflow/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bottender/dialogflow", 3 | "version": "1.5.5", 4 | "description": "Dialogflow integration for Bottender.", 5 | "license": "MIT", 6 | "homepage": "https://bottender.js.org/", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/Yoctol/bottender.git" 10 | }, 11 | "main": "dist/index.js", 12 | "files": [ 13 | "dist" 14 | ], 15 | "types": "dist/index.d.ts", 16 | "dependencies": { 17 | "@google-cloud/dialogflow": "^4.4.0", 18 | "invariant": "^2.2.4" 19 | }, 20 | "peerDependencies": { 21 | "bottender": ">= 1.2.0-0" 22 | }, 23 | "devDependencies": { 24 | "bottender": "^1.5.5" 25 | }, 26 | "keywords": [ 27 | "bot", 28 | "bottender", 29 | "chatbot", 30 | "dialogflow", 31 | "language", 32 | "messaging", 33 | "nlu", 34 | "understanding" 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /packages/bottender-rasa/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@bottender/rasa", 3 | "version": "1.5.5", 4 | "description": "Rasa NLU integration for Bottender.", 5 | "license": "MIT", 6 | "homepage": "https://bottender.js.org/", 7 | "repository": { 8 | "type": "git", 9 | "url": "https://github.com/Yoctol/bottender.git" 10 | }, 11 | "main": "dist/index.js", 12 | "files": [ 13 | "dist" 14 | ], 15 | "types": "dist/index.d.ts", 16 | "dependencies": { 17 | "@types/lodash": "^4.14.149", 18 | "axios": "^0.21.4", 19 | "lodash": "^4.17.15" 20 | }, 21 | "peerDependencies": { 22 | "bottender": ">= 1.2.0-0" 23 | }, 24 | "devDependencies": { 25 | "bottender": "^1.5.5" 26 | }, 27 | "keywords": [ 28 | "bot", 29 | "bottender", 30 | "chatbot", 31 | "language", 32 | "messaging", 33 | "nlu", 34 | "rasa", 35 | "understanding" 36 | ] 37 | } 38 | -------------------------------------------------------------------------------- /examples/custom-server-restify/server.js: -------------------------------------------------------------------------------- 1 | const restify = require('restify'); 2 | const { bottender } = require('bottender'); 3 | 4 | const app = bottender({ 5 | dev: process.env.NODE_ENV !== 'production', 6 | }); 7 | 8 | const port = Number(process.env.PORT) || 5000; 9 | 10 | const handle = app.getRequestHandler(); 11 | 12 | app.prepare().then(() => { 13 | const server = restify.createServer(); 14 | 15 | server.use(restify.plugins.queryParser()); 16 | server.use(restify.plugins.bodyParser()); 17 | 18 | server.get('/api', (req, res) => { 19 | res.send({ ok: true }); 20 | }); 21 | 22 | server.get('*', (req, res) => { 23 | return handle(req, res); 24 | }); 25 | server.post('*', (req, res) => { 26 | return handle(req, res); 27 | }); 28 | 29 | server.listen(port, (err) => { 30 | if (err) throw err; 31 | console.log(`> Ready on http://localhost:${port}`); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/sh/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import sh from '..'; 2 | 3 | jest.mock('../init'); 4 | jest.mock('../help'); 5 | jest.mock('../start'); 6 | jest.mock('../dev'); 7 | 8 | describe('sh cli', () => { 9 | it('should exist', () => { 10 | expect(sh).toBeDefined(); 11 | }); 12 | 13 | it('should return init module', () => { 14 | const init = require('../init').default; 15 | expect(sh.init).toEqual(init); 16 | }); 17 | 18 | it('should return help module', () => { 19 | const help = require('../help').default; 20 | expect(sh.help).toEqual(help); 21 | }); 22 | 23 | it('should return start module', () => { 24 | const start = require('../start').default; 25 | expect(sh.start).toEqual(start); 26 | }); 27 | 28 | it('should return dev module', () => { 29 | const dev = require('../dev').default; 30 | expect(sh.dev).toEqual(dev); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /website/src/components/Showcase.tsx: -------------------------------------------------------------------------------- 1 | import React, { FC } from 'react'; 2 | 3 | type UserLinkProps = { 4 | caption: string; 5 | image: string; 6 | infoLink: string; 7 | }; 8 | 9 | const UserLink: FC = ({ infoLink, image, caption }) => ( 10 | 11 | {caption} 12 | {caption} 13 | 14 | ); 15 | 16 | type ShowcaseProps = { 17 | users: { infoLink: string; image: string; caption: string }[]; 18 | }; 19 | 20 | const Showcase: FC = ({ users }) => ( 21 |
22 | {users.map((user) => ( 23 | 29 | ))} 30 |
31 | ); 32 | 33 | export default Showcase; 34 | -------------------------------------------------------------------------------- /website/static/img/element_anywhere.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /packages/bottender/src/line/LineBot.ts: -------------------------------------------------------------------------------- 1 | import { LineClient } from 'messaging-api-line'; 2 | 3 | import Bot, { OnRequest } from '../bot/Bot'; 4 | import SessionStore from '../session/SessionStore'; 5 | 6 | import LineConnector, { LineConnectorOptions } from './LineConnector'; 7 | import LineContext from './LineContext'; 8 | import LineEvent from './LineEvent'; 9 | import { LineRequestBody } from './LineTypes'; 10 | 11 | export default class LineBot extends Bot< 12 | LineRequestBody, 13 | LineClient, 14 | LineEvent, 15 | LineContext 16 | > { 17 | constructor({ 18 | sessionStore, 19 | sync, 20 | onRequest, 21 | ...connectorOptions 22 | }: LineConnectorOptions & { 23 | sessionStore?: SessionStore; 24 | sync?: boolean; 25 | onRequest?: OnRequest; 26 | }) { 27 | const connector = new LineConnector(connectorOptions); 28 | super({ connector, sessionStore, sync, onRequest }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/custom-server-express-typescript/src/server.ts: -------------------------------------------------------------------------------- 1 | import bodyParser from 'body-parser'; 2 | import express from 'express'; 3 | import { bottender } from 'bottender'; 4 | 5 | const app = bottender({ 6 | dev: process.env.NODE_ENV !== 'production', 7 | }); 8 | 9 | const port = Number(process.env.PORT) || 5000; 10 | 11 | const handle = app.getRequestHandler(); 12 | 13 | app.prepare().then(() => { 14 | const server = express(); 15 | 16 | server.use( 17 | bodyParser.json({ 18 | verify: (req, _, buf) => { 19 | (req as any).rawBody = buf.toString(); 20 | }, 21 | }) 22 | ); 23 | 24 | server.get('/api', (req, res) => { 25 | res.json({ ok: true }); 26 | }); 27 | 28 | server.all('*', (req, res) => { 29 | return handle(req, res); 30 | }); 31 | 32 | server.listen(port, (err) => { 33 | if (err) throw err; 34 | console.log(`> Ready on http://localhost:${port}`); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /packages/bottender/src/viber/ViberBot.ts: -------------------------------------------------------------------------------- 1 | import { ViberClient } from 'messaging-api-viber'; 2 | 3 | import Bot, { OnRequest } from '../bot/Bot'; 4 | import SessionStore from '../session/SessionStore'; 5 | 6 | import ViberConnector, { ViberConnectorOptions } from './ViberConnector'; 7 | import ViberContext from './ViberContext'; 8 | import ViberEvent from './ViberEvent'; 9 | import { ViberRequestBody } from './ViberTypes'; 10 | 11 | export default class ViberBot extends Bot< 12 | ViberRequestBody, 13 | ViberClient, 14 | ViberEvent, 15 | ViberContext 16 | > { 17 | constructor({ 18 | sessionStore, 19 | sync, 20 | onRequest, 21 | ...connectorOptions 22 | }: ViberConnectorOptions & { 23 | sessionStore?: SessionStore; 24 | sync?: boolean; 25 | onRequest?: OnRequest; 26 | }) { 27 | const connector = new ViberConnector(connectorOptions); 28 | super({ connector, sessionStore, sync, onRequest }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /examples/with-aws-lambda/server.js: -------------------------------------------------------------------------------- 1 | const serverless = require('serverless-http'); 2 | const bodyParser = require('body-parser'); 3 | const express = require('express'); 4 | const { bottender } = require('bottender'); 5 | 6 | const app = bottender({ 7 | dev: process.env.NODE_ENV !== 'production', 8 | }); 9 | 10 | const port = Number(process.env.PORT) || 5000; 11 | 12 | const handle = app.getRequestHandler(); 13 | 14 | const server = express(); 15 | server.use( 16 | bodyParser.json({ 17 | verify: (req, _, buf) => { 18 | req.rawBody = buf.toString(); 19 | }, 20 | }) 21 | ); 22 | 23 | server.all('*', (req, res) => { 24 | app.prepare().then(() => { 25 | return handle(req, res); 26 | }); 27 | }); 28 | 29 | server.listen(port, (err) => { 30 | if (err) { 31 | console.log(err); 32 | throw err; 33 | } 34 | console.log(`> Ready on http://localhost:${port}`); 35 | }); 36 | 37 | module.exports.handler = serverless(server); 38 | -------------------------------------------------------------------------------- /packages/bottender/src/session/MemorySessionStore.ts: -------------------------------------------------------------------------------- 1 | import MemoryCacheStore from '../cache/MemoryCacheStore'; 2 | 3 | import CacheBasedSessionStore from './CacheBasedSessionStore'; 4 | import SessionStore from './SessionStore'; 5 | 6 | type MemoryOption = 7 | | number 8 | | { 9 | maxSize?: number; 10 | }; 11 | 12 | function getMaxSize(arg?: MemoryOption): number | undefined { 13 | if (typeof arg === 'number') { 14 | return arg; 15 | } 16 | 17 | if (arg && typeof arg === 'object') { 18 | return arg.maxSize; 19 | } 20 | 21 | // eslint-disable-next-line no-useless-return 22 | return; 23 | } 24 | 25 | export default class MemorySessionStore 26 | extends CacheBasedSessionStore 27 | implements SessionStore 28 | { 29 | constructor(arg?: MemoryOption, expiresIn?: number) { 30 | const maxSize = getMaxSize(arg); 31 | 32 | const cache = new MemoryCacheStore(maxSize); 33 | 34 | super(cache, expiresIn); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /packages/bottender-qna-maker/src/types.ts: -------------------------------------------------------------------------------- 1 | export type QnaContext = { 2 | isContextOnly: boolean; 3 | prompts: PromptDTO[]; 4 | }; 5 | 6 | export type MetadataDTO = { 7 | name: string; 8 | value: string; 9 | }; 10 | 11 | export type PromptDTO = { 12 | displayOrder: number; 13 | displayText: string; 14 | qna: Qna; 15 | qnaId: number; 16 | }; 17 | 18 | export type Qna = { 19 | answer: string; 20 | context: QnaContext; 21 | id: number; 22 | metadata: MetadataDTO[]; 23 | questions: string[]; 24 | source: string; 25 | }; 26 | 27 | export type QnaSearchResult = Qna & { score: number }; 28 | 29 | export type QnaSearchResultList = { 30 | answers: QnaSearchResult[]; 31 | }; 32 | 33 | export type QueryDTO = { 34 | context?: QnaContext; 35 | isTest?: boolean; 36 | qnaId?: string; 37 | question: string; 38 | rankerType?: string; 39 | scoreThreshold?: number; 40 | strictFilters?: MetadataDTO[]; 41 | top?: number; 42 | userId?: string; 43 | }; 44 | -------------------------------------------------------------------------------- /examples/messenger-account-linking/public/images/check.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /packages/bottender/src/whatsapp/WhatsappBot.ts: -------------------------------------------------------------------------------- 1 | import Bot, { OnRequest } from '../bot/Bot'; 2 | import SessionStore from '../session/SessionStore'; 3 | 4 | import TwilioClient from './TwilioClient'; 5 | import WhatsappConnector, { 6 | WhatsappConnectorOptions, 7 | } from './WhatsappConnector'; 8 | import WhatsappContext from './WhatsappContext'; 9 | import WhatsappEvent from './WhatsappEvent'; 10 | import { WhatsappRequestBody } from './WhatsappTypes'; 11 | 12 | export default class WhatsappBot extends Bot< 13 | WhatsappRequestBody, 14 | TwilioClient, 15 | WhatsappEvent, 16 | WhatsappContext 17 | > { 18 | constructor({ 19 | sessionStore, 20 | sync, 21 | onRequest, 22 | ...connectorOptions 23 | }: WhatsappConnectorOptions & { 24 | sessionStore?: SessionStore; 25 | sync?: boolean; 26 | onRequest?: OnRequest; 27 | }) { 28 | const connector = new WhatsappConnector(connectorOptions); 29 | super({ connector, sessionStore, sync, onRequest }); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/sh/utils/__tests__/getArgs.spec.ts: -------------------------------------------------------------------------------- 1 | import getArgs from '../getArgs'; 2 | 3 | it('should be defined', () => { 4 | expect(getArgs).toBeDefined(); 5 | }); 6 | 7 | it('should parse args as expect', () => { 8 | const args = ['messenger', 'profile', 'set', '--force']; 9 | 10 | const result = getArgs( 11 | args, 12 | { 13 | '--force': Boolean, 14 | }, 15 | { permissive: true } 16 | ); 17 | 18 | expect(result).toEqual({ 19 | _: ['messenger', 'profile', 'set'], 20 | '--force': true, 21 | }); 22 | }); 23 | 24 | it('should handle args abbreviation', () => { 25 | const args = ['messenger', 'profile', 'set', '-f', 'other-args']; 26 | 27 | const result = getArgs( 28 | args, 29 | { 30 | '--force': Boolean, 31 | '-f': '--force', 32 | }, 33 | { permissive: true } 34 | ); 35 | 36 | expect(result).toEqual({ 37 | _: ['messenger', 'profile', 'set', 'other-args'], 38 | '--force': true, 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /website/docs/channel-slack-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-slack-handling-events 3 | title: Handling Slack Events 4 | --- 5 | 6 | ## Text Events 7 | 8 | The most common events are text message events. To determine whether the event is a text message event, you may use the `context.event.isText` boolean value: 9 | 10 | ```js 11 | async function App(context) { 12 | if (context.event.isText) { 13 | // handling the text message event 14 | } 15 | } 16 | ``` 17 | 18 | You can get the text content using `context.event.text` to use it in the reply: 19 | 20 | ```js 21 | async function App(context) { 22 | if (context.event.isText) { 23 | await context.sendText(`received the text message: ${context.event.text}`); 24 | } 25 | } 26 | ``` 27 | 28 | ## Handling Events with Router 29 | 30 | Bottender offers a bunch of helpers to route within your Slack or multi-platform app. To learn more about how to use those Slack particular routes with router, check out [Slack Routing](channel-slack-routing.md). 31 | -------------------------------------------------------------------------------- /website/docs/channel-viber-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-viber-handling-events 3 | title: Handling Viber Events 4 | --- 5 | 6 | ## Text Events 7 | 8 | The most common events are text message events. To determine whether the event is a text message event, you may use the `context.event.isText` boolean value: 9 | 10 | ```js 11 | async function App(context) { 12 | if (context.event.isText) { 13 | // handling the text message event 14 | } 15 | } 16 | ``` 17 | 18 | You can get the text content using `context.event.text` to use it in the reply: 19 | 20 | ```js 21 | async function App(context) { 22 | if (context.event.isText) { 23 | await context.sendText(`received the text message: ${context.event.text}`); 24 | } 25 | } 26 | ``` 27 | 28 | ## Handling Events with Router 29 | 30 | Bottender offers a bunch of helpers to route within your Viber or multi-platform app. To learn more about how to use those Viber particular routes with router, check out [Viber Routing](channel-viber-routing.md). 31 | -------------------------------------------------------------------------------- /examples/with-dialogflow/README.md: -------------------------------------------------------------------------------- 1 | # With Dialogflow 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [Bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/with-dialogflow 9 | cd with-dialogflow 10 | npm install 11 | ``` 12 | 13 | Before you run this example, make sure you have followed the [Official Setup Guide](https://cloud.google.com/dialogflow/docs/quick/setup) and filled the following two values in your `.env` file: 14 | 15 | - `GOOGLE_APPLICATION_CREDENTIALS` (the file path of the JSON file that contains your service account key) 16 | - `GOOGLE_APPLICATION_PROJECT_ID` (the GCP project ID) 17 | 18 | ```sh 19 | npm run dev -- --console 20 | ``` 21 | 22 | ## Idea of This Example 23 | 24 | This example shows how to combine your bot with modern NLU (Natural Language Understanding) technologies. In 25 | this case, we take [Dialogflow](https://dialogflow.com/) as an example. 26 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/messenger/__tests__/index.spec.ts: -------------------------------------------------------------------------------- 1 | import messenger from '..'; 2 | 3 | jest.mock('../profile'); 4 | jest.mock('../webhook'); 5 | jest.mock('../help'); 6 | jest.mock('../persona'); 7 | 8 | describe('messenger cli', () => { 9 | it('should exist', () => { 10 | expect(messenger).toBeDefined(); 11 | }); 12 | 13 | it('should return profile module', () => { 14 | const profile = require('../profile').default; 15 | expect(messenger.profile).toEqual(profile); 16 | }); 17 | 18 | it('should return webhook module', () => { 19 | const webhook = require('../webhook').default; 20 | expect(messenger.webhook).toEqual(webhook); 21 | }); 22 | 23 | it('should return help module', () => { 24 | const help = require('../help').default; 25 | expect(messenger.help).toEqual(help); 26 | }); 27 | 28 | it('should return persona module', () => { 29 | const persona = require('../persona').default; 30 | expect(messenger.persona).toEqual(persona); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /examples/todos/index.js: -------------------------------------------------------------------------------- 1 | const { withProps } = require('bottender'); 2 | const { router, text } = require('bottender/router'); 3 | 4 | async function TodoNotFound(context) { 5 | await context.sendText('No todos!'); 6 | } 7 | async function ShowTodos(context, { todos }) { 8 | if (todos.length > 0) { 9 | await context.sendText(todos.join('\n')); 10 | } else { 11 | return TodoNotFound; 12 | } 13 | } 14 | 15 | async function ClearTodos(context) { 16 | context.resetState(); 17 | await context.sendText('Successfully clear all todos!'); 18 | } 19 | 20 | async function AddTodo(context) { 21 | const newTodos = context.event.text; 22 | context.setState({ 23 | todos: [...context.state.todos, newTodos], 24 | }); 25 | await context.sendText(`Todo: ${newTodos} added!`); 26 | } 27 | 28 | module.exports = async function App(context) { 29 | return router([ 30 | text('/list', withProps(ShowTodos, { todos: context.state.todos })), 31 | text('/clear', ClearTodos), 32 | text('*', AddTodo), 33 | ]); 34 | }; 35 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testEnvironment: 'node', 3 | preset: 'ts-jest', 4 | moduleFileExtensions: ['js', 'json', 'ts'], 5 | testPathIgnorePatterns: [ 6 | '/node_modules/', 7 | '/examples/', 8 | '/dist/', 9 | 'src/cli/providers/sh/test.js', 10 | ], 11 | coverageDirectory: './coverage/', 12 | transformIgnorePatterns: ['/node_modules/'], 13 | testMatch: ['**/__tests__/*.ts'], 14 | timers: 'legacy', // TODO: use modern timers 15 | resetMocks: true, 16 | globals: { 17 | 'ts-jest': { 18 | tsconfig: '/tsconfig.test.json', 19 | diagnostics: false, 20 | }, 21 | }, 22 | collectCoverageFrom: ['packages/*/src/**/*.ts'], 23 | coveragePathIgnorePatterns: ['/node_modules/', '/__tests__/'], 24 | coverageThreshold: { 25 | global: { 26 | branches: 75, 27 | functions: 75, 28 | lines: 75, 29 | statements: 75, 30 | }, 31 | }, 32 | setupFiles: ['./test/setupJest.js'], 33 | reporters: ['default', 'jest-junit'], 34 | resetModules: true, 35 | }; 36 | -------------------------------------------------------------------------------- /packages/bottender/src/messenger/MessengerBot.ts: -------------------------------------------------------------------------------- 1 | import { MessengerClient } from 'messaging-api-messenger'; 2 | 3 | import Bot, { OnRequest } from '../bot/Bot'; 4 | import SessionStore from '../session/SessionStore'; 5 | 6 | import MessengerConnector, { 7 | MessengerConnectorOptions, 8 | } from './MessengerConnector'; 9 | import MessengerContext from './MessengerContext'; 10 | import MessengerEvent from './MessengerEvent'; 11 | import { MessengerRequestBody } from './MessengerTypes'; 12 | 13 | export default class MessengerBot extends Bot< 14 | MessengerRequestBody, 15 | MessengerClient, 16 | MessengerEvent, 17 | MessengerContext 18 | > { 19 | constructor({ 20 | sessionStore, 21 | sync, 22 | onRequest, 23 | ...connectorOptions 24 | }: MessengerConnectorOptions & { 25 | sessionStore?: SessionStore; 26 | sync?: boolean; 27 | onRequest?: OnRequest; 28 | }) { 29 | const connector = new MessengerConnector(connectorOptions); 30 | super({ connector, sessionStore, sync, onRequest }); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/line-liff-v1/server.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | const bodyParser = require('body-parser'); 4 | const express = require('express'); 5 | const { bottender } = require('bottender'); 6 | 7 | const app = bottender({ 8 | dev: process.env.NODE_ENV !== 'production', 9 | }); 10 | 11 | const port = Number(process.env.PORT) || 5000; 12 | 13 | const handle = app.getRequestHandler(); 14 | 15 | app.prepare().then(() => { 16 | const server = express(); 17 | 18 | server.use( 19 | bodyParser.json({ 20 | verify: (req, _, buf) => { 21 | req.rawBody = buf.toString(); 22 | }, 23 | }) 24 | ); 25 | 26 | server.get('/liff', (req, res) => { 27 | const filename = path.join(`${__dirname}/liff.html`); 28 | res.sendFile(filename); 29 | }); 30 | 31 | // delegate other requests to bottender 32 | server.all('*', (req, res) => { 33 | return handle(req, res); 34 | }); 35 | 36 | server.listen(port, (err) => { 37 | if (err) throw err; 38 | console.log(`> Ready on http://localhost:${port}`); 39 | }); 40 | }); 41 | -------------------------------------------------------------------------------- /packages/bottender/src/shared/__tests__/log.spec.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | import chalk from 'chalk'; 3 | import figures from 'figures'; 4 | 5 | import { bold, error, log, print, warn } from '../log'; 6 | 7 | describe('console', () => { 8 | beforeEach(() => { 9 | console.log = jest.fn(); 10 | }); 11 | 12 | it('log', () => { 13 | log('log!'); 14 | expect(console.log).toBeCalledWith(`${chalk.blue(figures.pointer)} log!`); 15 | }); 16 | 17 | it('print', () => { 18 | print('print!'); 19 | expect(console.log).toBeCalledWith( 20 | `${chalk.green(figures.pointer)} print!` 21 | ); 22 | }); 23 | 24 | it('warn', () => { 25 | warn('warn!'); 26 | expect(console.log).toBeCalledWith( 27 | `${chalk.yellow(figures.warning)} warn!` 28 | ); 29 | }); 30 | 31 | it('error', () => { 32 | error('error!'); 33 | expect(console.log).toBeCalledWith(`${chalk.red(figures.cross)} error!`); 34 | }); 35 | 36 | it('bold', () => { 37 | expect(bold('bold!')).toEqual(chalk.bold('bold!')); 38 | }); 39 | }); 40 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.4/channel-slack-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-slack-handling-events 3 | title: Handling Slack Events 4 | original_id: channel-slack-handling-events 5 | --- 6 | 7 | ## Text Events 8 | 9 | The most common events are text message events. To determine whether the event is a text message event, you may use the `context.event.isText` boolean value: 10 | 11 | ```js 12 | async function App(context) { 13 | if (context.event.isText) { 14 | // handling the text message event 15 | } 16 | } 17 | ``` 18 | 19 | You can get the text content using `context.event.text` to use it in the reply: 20 | 21 | ```js 22 | async function App(context) { 23 | if (context.event.isText) { 24 | await context.sendText(`received the text message: ${context.event.text}`); 25 | } 26 | } 27 | ``` 28 | 29 | ## Handling Events with Router 30 | 31 | Bottender offers a bunch of helpers to route within your Slack or multi-platform app. To learn more about how to use those Slack particular routes with router, check out [Slack Routing](channel-slack-routing.md). 32 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.4/channel-viber-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-viber-handling-events 3 | title: Handling Viber Events 4 | original_id: channel-viber-handling-events 5 | --- 6 | 7 | ## Text Events 8 | 9 | The most common events are text message events. To determine whether the event is a text message event, you may use the `context.event.isText` boolean value: 10 | 11 | ```js 12 | async function App(context) { 13 | if (context.event.isText) { 14 | // handling the text message event 15 | } 16 | } 17 | ``` 18 | 19 | You can get the text content using `context.event.text` to use it in the reply: 20 | 21 | ```js 22 | async function App(context) { 23 | if (context.event.isText) { 24 | await context.sendText(`received the text message: ${context.event.text}`); 25 | } 26 | } 27 | ``` 28 | 29 | ## Handling Events with Router 30 | 31 | Bottender offers a bunch of helpers to route within your Viber or multi-platform app. To learn more about how to use those Viber particular routes with router, check out [Viber Routing](channel-viber-routing.md). 32 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.5/channel-slack-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-slack-handling-events 3 | title: Handling Slack Events 4 | original_id: channel-slack-handling-events 5 | --- 6 | 7 | ## Text Events 8 | 9 | The most common events are text message events. To determine whether the event is a text message event, you may use the `context.event.isText` boolean value: 10 | 11 | ```js 12 | async function App(context) { 13 | if (context.event.isText) { 14 | // handling the text message event 15 | } 16 | } 17 | ``` 18 | 19 | You can get the text content using `context.event.text` to use it in the reply: 20 | 21 | ```js 22 | async function App(context) { 23 | if (context.event.isText) { 24 | await context.sendText(`received the text message: ${context.event.text}`); 25 | } 26 | } 27 | ``` 28 | 29 | ## Handling Events with Router 30 | 31 | Bottender offers a bunch of helpers to route within your Slack or multi-platform app. To learn more about how to use those Slack particular routes with router, check out [Slack Routing](channel-slack-routing.md). 32 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.5/channel-viber-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-viber-handling-events 3 | title: Handling Viber Events 4 | original_id: channel-viber-handling-events 5 | --- 6 | 7 | ## Text Events 8 | 9 | The most common events are text message events. To determine whether the event is a text message event, you may use the `context.event.isText` boolean value: 10 | 11 | ```js 12 | async function App(context) { 13 | if (context.event.isText) { 14 | // handling the text message event 15 | } 16 | } 17 | ``` 18 | 19 | You can get the text content using `context.event.text` to use it in the reply: 20 | 21 | ```js 22 | async function App(context) { 23 | if (context.event.isText) { 24 | await context.sendText(`received the text message: ${context.event.text}`); 25 | } 26 | } 27 | ``` 28 | 29 | ## Handling Events with Router 30 | 31 | Bottender offers a bunch of helpers to route within your Viber or multi-platform app. To learn more about how to use those Viber particular routes with router, check out [Viber Routing](channel-viber-routing.md). 32 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.3.1/channel-slack-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-slack-handling-events 3 | title: Handling Slack Events 4 | original_id: channel-slack-handling-events 5 | --- 6 | 7 | ## Text Events 8 | 9 | The most common events are text message events. To determine whether the event is a text message event, you may use the `context.event.isText` boolean value: 10 | 11 | ```js 12 | async function App(context) { 13 | if (context.event.isText) { 14 | // handling the text message event 15 | } 16 | } 17 | ``` 18 | 19 | You can get the text content using `context.event.text` to use it in the reply: 20 | 21 | ```js 22 | async function App(context) { 23 | if (context.event.isText) { 24 | await context.sendText(`received the text message: ${context.event.text}`); 25 | } 26 | } 27 | ``` 28 | 29 | ## Handling Events with Router 30 | 31 | Bottender offers a bunch of helpers to route within your Slack or multi-platform app. To learn more about how to use those Slack particular routes with router, check out [Slack Routing](channel-slack-routing.md). 32 | -------------------------------------------------------------------------------- /website/versioned_docs/version-1.3.1/channel-viber-handling-events.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-viber-handling-events 3 | title: Handling Viber Events 4 | original_id: channel-viber-handling-events 5 | --- 6 | 7 | ## Text Events 8 | 9 | The most common events are text message events. To determine whether the event is a text message event, you may use the `context.event.isText` boolean value: 10 | 11 | ```js 12 | async function App(context) { 13 | if (context.event.isText) { 14 | // handling the text message event 15 | } 16 | } 17 | ``` 18 | 19 | You can get the text content using `context.event.text` to use it in the reply: 20 | 21 | ```js 22 | async function App(context) { 23 | if (context.event.isText) { 24 | await context.sendText(`received the text message: ${context.event.text}`); 25 | } 26 | } 27 | ``` 28 | 29 | ## Handling Events with Router 30 | 31 | Bottender offers a bunch of helpers to route within your Viber or multi-platform app. To learn more about how to use those Viber particular routes with router, check out [Viber Routing](channel-viber-routing.md). 32 | -------------------------------------------------------------------------------- /examples/messenger-multi-pages/bottender.config.js: -------------------------------------------------------------------------------- 1 | const { 2 | PAGE_1_PAGE_ID, 3 | PAGE_1_ACCESS_TOKEN, 4 | PAGE_2_PAGE_ID, 5 | PAGE_2_ACCESS_TOKEN, 6 | } = process.env; 7 | 8 | /** 9 | * The example show how to map pages to tokens from (id, token) set, 10 | * but you can dynamically load tokens from SQL database, mongodb, redis, REST API ... 11 | * or whatever you want. 12 | */ 13 | const mapPageToAccessToken = (pageId) => { 14 | switch (pageId) { 15 | case PAGE_1_PAGE_ID: 16 | return PAGE_1_ACCESS_TOKEN; 17 | case PAGE_2_PAGE_ID: 18 | default: 19 | return PAGE_2_ACCESS_TOKEN; 20 | } 21 | }; 22 | 23 | module.exports = { 24 | channels: { 25 | messenger: { 26 | enabled: true, 27 | path: '/webhooks/messenger', 28 | pageId: process.env.MESSENGER_PAGE_ID, 29 | accessToken: process.env.MESSENGER_ACCESS_TOKEN, 30 | appId: process.env.MESSENGER_APP_ID, 31 | appSecret: process.env.MESSENGER_APP_SECRET, 32 | verifyToken: process.env.MESSENGER_VERIFY_TOKEN, 33 | mapPageToAccessToken, 34 | }, 35 | }, 36 | }; 37 | -------------------------------------------------------------------------------- /examples/session-redis/README.md: -------------------------------------------------------------------------------- 1 | # Session: Redis 2 | 3 | ## Install and Run 4 | 5 | Download this example or clone [bottender](https://github.com/Yoctol/bottender). 6 | 7 | ```sh 8 | curl https://codeload.github.com/Yoctol/bottender/tar.gz/master | tar -xz --strip=2 bottender-master/examples/session-redis 9 | cd session-redis 10 | npm install 11 | npm run dev -- --console 12 | ``` 13 | 14 | ## Redis Connection Settings 15 | 16 | ```js 17 | module.exports = { 18 | session: { 19 | driver: 'redis', 20 | stores: { 21 | redis: { 22 | port: 6379, 23 | host: '127.0.0.1', 24 | password: 'auth', 25 | db: 0, 26 | }, 27 | }, 28 | }, 29 | }; 30 | ``` 31 | 32 | ## Idea of This Example 33 | 34 | This example shows how to use session to store some information from users in your Redis server. For more information, check our [session guide](https://bottender.js.org/docs/the-basics-session). 35 | 36 | ## Related Examples 37 | 38 | - [session-memory](../session-memory) 39 | - [session-file](../session-file) 40 | - [session-mongo](../session-mongo) 41 | -------------------------------------------------------------------------------- /packages/bottender/src/cli/providers/sh/help.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk'; 2 | 3 | const help = () => { 4 | console.log(` 5 | bottender 6 | 7 | ${chalk.dim('Commands:')} 8 | 9 | ${chalk.dim('Platform')} 10 | 11 | messenger Manage your Messenger command 12 | telegram Manage your Telegram command 13 | line Manage your LINE command 14 | viber Manage your Viber command 15 | 16 | ${chalk.dim('Global')} 17 | 18 | init Init a bot skeleton 19 | help Show this help 20 | 21 | ${chalk.dim('Examples:')} 22 | 23 | ${chalk.dim('-')} Init a bot 24 | 25 | ${chalk.cyan('$ bottender init')} 26 | 27 | ${chalk.dim('-')} Get the help 28 | 29 | ${chalk.cyan('$ bottender help')} 30 | ${chalk.cyan('$ bottender messenger help')} 31 | 32 | ${chalk.dim('-')} Manage the Messenger command 33 | 34 | ${chalk.cyan('$ bottender messenger webhook set')} 35 | `); 36 | }; 37 | 38 | export default help; 39 | -------------------------------------------------------------------------------- /website/docs/channel-line-errors.md: -------------------------------------------------------------------------------- 1 | --- 2 | id: channel-line-errors 3 | title: Error Handling in LINE 4 | --- 5 | 6 | ## Customizing the Error Message in LINE 7 | 8 | Your bot can only reply to the user once after a user interacts with your LINE official account. However, the error may happen when calling Reply API. So, you can check the value of `context.isReplied` before replying to the user in your error handler: 9 | 10 | ```js 11 | // _error.js 12 | 13 | module.exports = async function HandleError(context, props) { 14 | console.error(props.error); 15 | if (!context.isReplied) { 16 | // or you can choose not to reply any error messages 17 | await context.replyText( 18 | 'There are some unexpected errors happened. Please try again later, sorry for the inconvenience.' 19 | ); 20 | } 21 | if (process.env.NODE_ENV === 'production') { 22 | // send your error to the error tracker, for example: Sentry 23 | } 24 | }; 25 | ``` 26 | 27 | For more information, you may refer to [Customizing the Error Message](the-basics-errors.md) for the basic knowledge of error handling. 28 | --------------------------------------------------------------------------------